3
$\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$
5
$\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$
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 = obj.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$
  • $\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 Jul 17 '15 at 13:56
  • $\begingroup$ yeah this may be faster than explicitly looping over stuff with python. $\endgroup$ – zeffii Jul 17 '15 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 Jul 17 '15 at 14:02
  • $\begingroup$ I edited my question to be more specific. $\endgroup$ – jasperge Jul 17 '15 at 14:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.