1
$\begingroup$

Sorry if the title is confusing, I wasn't sure how to best describe what I am trying to do.

Basically, I'm trying to create Bezier Curves that "grow" off of each other. For example, I would make one curve, deform it in whatever way, and then create a second curve that starts at a random point along the first curve, like this:

enter image description here

Obviously, I'm trying to do all of this programmatically in python.

I was able to pull the points of the first curve using the code suggested in this comment: Getting the list of points that describe a Curve without converting to mesh along with some of my own code.

However, the output of this seems to always be between -1 and 1 (though it goes a little over on both ends) no matter where in the scene I place it. This suggests to me that the exported points are in some relative space and not in the coordinate space used by the scene.

Does anyone know how to output the points of a curve in X, Y, Z coordinates of the scene? Or at least how to transform those points into the coordinates of the scene?

Thank you!

$\endgroup$
1
  • $\begingroup$ The curve data, like mesh data, is in local coordinates, whereas you want global coordinates, which can be obtained by multiplying by the curve objects matrix_world. blender.stackexchange.com/a/6156/15543 $\endgroup$
    – batFINGER
    Jun 2, 2016 at 17:19

2 Answers 2

2
$\begingroup$

Perhaps the following code gives you an explicit example of how to interpolate points on a Bezier curve. (I almost forgot the matrix_world that batFINGER has already explained is very important)

import bpy
from math import *

def interpBez3(bp0, t, bp3):
    # bp1, HR, HL, bp2

    return interpBez3_(bp0.co, bp0.handle_right, bp3.handle_left, bp3.co, t)
#    return interpBez3_(bp0.co, bp0.handle_left, bp3.handle_right, bp3.co, t)

def interpBez3_(p0, p1, p2, p3, t):
    r = 1-t
    return (r*r*r*p0 +
            3*r*r*t*p1 +
            3*r*t*t*p2 +
            t*t*t*p3)

def interpBlenderSpline(spline, i1, t2):

    bp1 = spline.bezier_points[i1]
    bp2 = spline.bezier_points[i1+1]
    return interpBez3(bp1, t2, bp2)

def mission1(obj, t):
    """ mathematically correct spline interpolation """

    i1 = floor(t)

    curve = obj.data

    bpy.context.scene.cursor_location = obj.matrix_world * interpBlenderSpline(curve.splines[0], i1, t-i1)


def mission2(obj, t):
    """ spline interpolation that matches the resolution_u setting inside blender """
    i1 = floor(t)
    t2 = t - i1
    spline = obj.data.splines[0]

    res = obj.data.render_resolution_u or obj.data.resolution_u
    t3 = t2*res
    t3i = floor(t3) / res
    t3f = t3 - floor(t3)

    p8 = interpBlenderSpline(spline, i1, t3i)
    p9 = interpBlenderSpline(spline, i1, t3i + 1/res)

    p = (1-t3f)*p8 + t3f*p9

    bpy.context.scene.cursor_location = obj.matrix_world * p


mission1(bpy.context.active_object, 0.7)

The mission2 function is if you want the point to match how blender chops up the spline into line segments instead of being on the mathematically precise bezier segment.

$\endgroup$
3
  • $\begingroup$ Thanks Mutant. How to change it to respect curve resolution ? $\endgroup$
    – JuhaW
    Mar 26, 2018 at 16:37
  • $\begingroup$ I'm going to need you to define what you mean by "respect curve resolution" . $\endgroup$
    – Mutant Bob
    Apr 12, 2018 at 14:36
  • $\begingroup$ For default curve object, default resolution is 12 and when you decrease/increase resolution, curve object changes. So interpolated points have to change also. $\endgroup$
    – JuhaW
    Apr 12, 2018 at 15:05
0
$\begingroup$

For Blender 2.8 and later the following update to the code is helpful. I also added the ability to give a fraction from 0 to just less than 1 to allow interpolation for the entire curve.

from math import *
def interpBez3(bp0,t,bp3):
    return interpBez3_(bp0.co,bp0.handle_right,bp3.handle_left,bp3.co,t)

def interpBez3_(p0,p1,p2,p3,t):
    r=1-t
    return( r*r*r*p0+
            3*r*r*t*p1+
            3*r*t*t*p2+
            t*t*t*p3 )

def interpBlenderSpline(spline,i1,t2):
    bp1=spline.bezier_points[i1]
    bp2=spline.bezier_points[i1+1]
    return interpBez3(bp1,t2,bp2)

def mission1(obj,frac):
    curve=obj.data
    numpoints=len(curve.splines[0].bezier_points)
    t=frac*(numpoints-1)
    i1=floot(t)
    bpy.context.scene.cursor.location=obj.matrix_world @ interpBlenderSpline(curve.splines[0],i1,t-i1)

mission1(bpy.context.active_object,0.7) # works from 0 to 0.9999999 or so

The most important changes were the '@' for matrix multiplication with a vector and the cursor location is ....cursor.location and not .....cursor_location.

$\endgroup$

You must log in to answer this question.

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