5
$\begingroup$

I found this question: Getting the list of points that describe a Curve without converting to mesh

But I would like to do the same for a Nurbs curve. This Nurbs curve consists of a lot of splines and I would like to be able to pick a spline to get the points from. I could create a Bezier curve, but I would prefer to use a Nurbs curve in my script. How would I do this as there is no mathutils.geometry.interpolate_nurbs()?

$\endgroup$
0

2 Answers 2

4
$\begingroup$

extracted the source code for nurb curves and ported it to python - seems to work

import bpy
import bmesh
import math
import mathutils

def macro_knotsu(nu):
    return nu.order_u + nu.point_count_u + (nu.order_u - 1 if nu.use_cyclic_u else 0)

def macro_segmentsu(nu):
    return nu.point_count_u if nu.use_cyclic_u else nu.point_count_u - 1

def makeknots(nu):
    knots = [0.0] * (4 + macro_knotsu(nu))
    flag = nu.use_endpoint_u + (nu.use_bezier_u << 1)
    if nu.use_cyclic_u:
        calcknots(knots, nu.point_count_u, nu.order_u, 0)
        makecyclicknots(knots, nu.point_count_u, nu.order_u)
    else:
        calcknots(knots, nu.point_count_u, nu.order_u, flag)
    return knots

def calcknots(knots, pnts, order, flag):
    pnts_order = pnts + order
    if flag == 1:
        k = 0.0
        for a in range(1, pnts_order + 1):
            knots[a - 1] = k
            if a >= order and a <= pnts:
                k += 1.0
    elif flag == 2:
        if order == 4:
            k = 0.34
            for a in range(pnts_order):
                knots[a] = math.floor(k)
                k += (1.0 / 3.0)
        elif order == 3:
            k = 0.6
            for a in range(pnts_order):
                if a >= order and a <= pnts:
                    k += 0.5
                    knots[a] = math.floor(k)
    else:
        for a in range(pnts_order):
            knots[a] = a

def makecyclicknots(knots, pnts, order):
    order2 = order - 1

    if order > 2:
        b = pnts + order2
        for a in range(1, order2):
            if knots[b] != knots[b - a]:
                break

            if a == order2:
                knots[pnts + order - 2] += 1.0

    b = order
    c = pnts + order + order2
    for a in range(pnts + order2, c):
        knots[a] = knots[a - 1] + (knots[b] - knots[b - 1])
        b -= 1

def basisNurb(t, order, pnts, knots, basis, start, end):
    i1 = i2 = 0
    orderpluspnts = order + pnts
    opp2 = orderpluspnts - 1

    # this is for float inaccuracy
    if t < knots[0]:
        t = knots[0]
    elif t > knots[opp2]:
        t = knots[opp2]

    # this part is order '1'
    o2 = order + 1
    for i in range(opp2):
        if knots[i] != knots[i + 1] and t >= knots[i] and t <= knots[i + 1]:
            basis[i] = 1.0
            i1 = i - o2
            if i1 < 0:
                i1 = 0
            i2 = i
            i += 1
            while i < opp2:
                basis[i] = 0.0
                i += 1
            break

        else:
            basis[i] = 0.0

    basis[i] = 0.0

    # this is order 2, 3, ...
    for j in range(2, order + 1):

        if i2 + j >= orderpluspnts:
            i2 = opp2 - j

        for i in range(i1, i2 + 1):
            if basis[i] != 0.0:
                d = ((t - knots[i]) * basis[i]) / (knots[i + j - 1] - knots[i])
            else:
                d = 0.0

            if basis[i + 1] != 0.0:
                e = ((knots[i + j] - t) * basis[i + 1]) / (knots[i + j] - knots[i + 1])
            else:
                e = 0.0

            basis[i] = d + e

    start = 1000
    end = 0

    for i in range(i1, i2 + 1):
        if basis[i] > 0.0:
            end = i
            if start == 1000:
                start = i

    return start, end

def nurb_make_curve(nu, resolu, stride):
    EPS = 1e-6
    coord_index = istart = iend = 0

    coord_array = [0.0] * (3 * nu.resolution_u * macro_segmentsu(nu))
    sum_array = [0] * nu.point_count_u
    basisu = [0.0] * macro_knotsu(nu)
    knots = makeknots(nu)

    resolu = resolu * macro_segmentsu(nu)
    ustart = knots[nu.order_u - 1]
    uend   = knots[nu.point_count_u + nu.order_u - 1] if nu.use_cyclic_u else \
             knots[nu.point_count_u]
    ustep  = (uend - ustart) / (resolu - (0 if nu.use_cyclic_u else 1))
    cycl = nu.order_u - 1 if nu.use_cyclic_u else 0

    u = ustart
    while resolu:
        resolu -= 1
        istart, iend = basisNurb(u, nu.order_u, nu.point_count_u + cycl, knots, basisu, istart, iend)

        #/* calc sum */
        sumdiv = 0.0
        sum_index = 0
        pt_index = istart - 1
        for i in range(istart, iend + 1):
            if i >= nu.point_count_u:
                pt_index = i - nu.point_count_u
            else:
                pt_index += 1

            sum_array[sum_index] = basisu[i] * nu.points[pt_index].co[3]
            sumdiv += sum_array[sum_index]
            sum_index += 1

        if (sumdiv != 0.0) and (sumdiv < 1.0 - EPS or sumdiv > 1.0 + EPS):
            sum_index = 0
            for i in range(istart, iend + 1):
                sum_array[sum_index] /= sumdiv
                sum_index += 1

        coord_array[coord_index: coord_index + 3] = (0.0, 0.0, 0.0)

        sum_index = 0
        pt_index = istart - 1
        for i in range(istart, iend + 1):
            if i >= nu.point_count_u:
                pt_index = i - nu.point_count_u
            else:
                pt_index += 1

            if sum_array[sum_index] != 0.0:
                for j in range(3):
                    coord_array[coord_index + j] += sum_array[sum_index] * nu.points[pt_index].co[j]
            sum_index += 1

        coord_index += stride
        u += ustep

    return coord_array

if __name__ == "__main__":
    curve = bpy.data.curves['NurbsCurve']
    nu = curve.splines[0]
    resolution = (curve.render_resolution_u if curve.render_resolution_u else
                  curve.resolution_u)
    coord_array = nurb_make_curve(nu, resolution, 3)

    verts = (mathutils.Vector(coord_array[i: i + 3])
                for i in range(0, len(coord_array), 3))

    mesh = bpy.data.objects['Cube'].data
    bm = bmesh.new()

    prev = bm.verts.new(next(verts))
    for v in verts:
        current = bm.verts.new(v)
        bm.edges.new((prev, current))
        prev = current

    bm.to_mesh(mesh)    
    bm.free()

    for area in bpy.context.screen.areas:
        if area.type == "VIEW_3D":
            area.tag_redraw()
$\endgroup$
1
  • $\begingroup$ this seems like a strange solution. why is there a "Cube" and mesh creation involved. is it not possible without creating a new mesh? $\endgroup$
    – Harry McKenzie
    Commented Jul 19, 2022 at 7:02
2
$\begingroup$

In the absence of such a function, it's relatively light weight to

  • make a mesh from the object
  • get verts + edges from the mesh
  • discard the mesh
  • return verts, edge keys

code

import bpy

def get_verts_edges(nurbs_object, use_modifiers=True, settings='PREVIEW'):
    scene = bpy.context.scene
    # create a temporary mesh
    obj_data = nurbs_object.to_mesh(scene, use_modifiers, settings)

    verts = [v.co for v in obj_data.vertices]
    edges = obj_data.edge_keys
    
    # discard temporary mesh
    bpy.data.meshes.remove(obj_data)
    return verts, edges
    
obj = bpy.data.objects['NameOfMyCurveObject']
verts, edges = get_verts_edges(obj)

print(verts, edges)
$\endgroup$
4
  • $\begingroup$ Thanks, but if possible I would like to skip the conversion to mesh. I have a curve with lots of splines (easily around 100). I already have a function to only convert 1 spline to a mesh (duplicate curve, delete all splines except the one I need, then convert to mesh). Probably I should test if the performance really suffers from this conversion process. $\endgroup$
    – jasperge
    Commented Jul 17, 2015 at 13:56
  • $\begingroup$ yeah this may be faster than explicitly looping over stuff with python. $\endgroup$
    – zeffii
    Commented Jul 17, 2015 at 13:57
  • $\begingroup$ but to get disjoint NURBS curves from one object, the way I suggest does add extra overhead. So it may not be exactly what you're looking for, because you are being more specific -- but i'll leave it as an answer in-case someone else doesn't have that specificiy and stumbles on the question $\endgroup$
    – zeffii
    Commented Jul 17, 2015 at 14:02
  • $\begingroup$ I edited my question to be more specific. $\endgroup$
    – jasperge
    Commented Jul 17, 2015 at 14:32

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .