Get Points on a Curve

I’m trying to get 5 points equally spaced on a curve. I already searched through the API, but as i don’t have a clue where to look, i was not successfull. Can anybody help me?

The only idea i have, is to set the curve as a path with the length 5 and get the positions of an object moving along it. But i think there should be a faster / cleaner solution. Maybe converting the Curve to a Polyline and get the Controlpoints?

I also think i can remember a Script Curve -> Edges, but i can’t find it.

Ok, what i decided to do is to create a dummy object, use the curve as a path and move the dummy along this path. Then i can grab each location and rotation of the dummy object and convert it to a transormation matrix that represents a plane orthogonal to the curve and with the x axis pointing in the direction of the curves twist.

as i’m not working with blender python usually, i encounter some trouble in writing this function. So i’m posting a kind of pseudocode here and hope somebody will help me with hints on translating that to Blender Python.



# ----- get the frame positions of a curve
def framePositions(curve,len):

    # setting the Curves pathLength and Flag
    cu.setPathLen(len)
    tmpCu = Blender.Object.New("Curve","tmp-crv")
    tmpCu.link(curve)
    curve.setFlag(30) #Flag 30 = Path + CurveFollow
    
    # create the dummy object
    tmpOb = Blender.Object.New("Empty","tmpOb")
    
    # make the curve the dummys parent
    tmpCu.makeParent([tmpOb])
    
    for frame in range(len):
        # increase frame
        # read the dummys location and rotation
        # store location and rotation in the framelist

    # return framelist
    

Ok, found another possibility. This one is quite simple, only thing is it doesn’t work till now. it’s based upon dupliframes:


import Blender


# ----- get the frame positions of a curve
def framePositions(curve,len):

    tmpCu = Blender.Object.New("Curve","tmp-crv")
    tmpCu.link(curve)
    curve.setFlag(30)
    
    tmpOb = Blender.Object.New("Empty","tmpOb")
    tmpCu.makeParent([tmpOb])
    
    tmpOb.enableDupFrames = 1
    tmpOb.DupEnd = len
    
    frames = []
    for dpl in tmpOb.DupObjects:
        frames.append(dpl[1])

    return frames



#  M A I N

ob =  Blender.Object.GetSelected()[0]
cu = ob.getData()

frames = framePositions(cu,5)

scene = Blender.Scene.getCurrent()

for frame in frames:
    empty = scene.objects.new('Empty')
    empty.setMatrix(frame)
    
print ('--------------------')

The main part is just for testing. And testing shows, that this isn’t working the way i want it to work till now. What i want is the empties ON the curve and equally spaced along the curve. I don’t know why this is not the case, does anybody know?

I think it is because of the dupli-matrix, but the API is quite cryptic on that topic:

The first tuple item is the original object that is duplicated, the second is the 4x4 worldspace dupli-matrix.

What is the 4x4 worldspace dupli-matrix? Is it the same as an Object Orientation Matrix? If it is, why is my script not working?

You may have NOT selected anything - script stops at line

ob =  Blender.Object.GetSelected()[0]

You may have selected anything different than a curve… script stops due to error at line

    tmpCu.link(curve)

Which error do you get there???

:smiley:
I know how to read error messages, but thanks for the care. No, the script runs perfectly smooth when i select a curve - as the main part is only for testing purposes i didn’t code a check for a valid input. The defined method is going to be part of a bigger script in which it will be impossible to get a false object type for the curve input.

The thing is: it runs smooth but delivers wrong results. The Empties that are generated by the script are not on the curve. Or they are distributed from the middle of the curve to the end of the curve instead of from start to end.

I don’t think it’s a scripting issue, but one of understanding the general blender tool Dupliframes.

ok, script is progressing. it now looks like this:


#!BPY
"""
Name: 'curveFrames'
Blender: 248a
Group: 'Curve'
Tooltip: 'distributes Empties on a Curve'
"""

import Blender

    
# ----- get (almost?) orthogonal frame positions on a curve
def framePositions(object,numOfFrames):

    # --- get the frames of a path    
    def getFrames(curve):
        # make a dummy object that moves along the curve
        tmpOb = Blender.Object.New("Empty","tmpOb")
        ob.makeParent([tmpOb],1)
        # set up the dupliframes for the dummy object
        tmpOb.enableDupFrames = 1
        tmpOb.DupEnd = numOfFrames
        # get the 4x4 worldspace duplimatrix for each frame
        frames = []
        for dpl in tmpOb.DupObjects:
            frames.append(dpl[1])
        return frames    
    
    ob = object.copy()
    curve = ob.getData()
    curve.setFlag(31)
    curve.setPathLen(numOfFrames)
    
    frames1 = getFrames(curve[0])
    curve[0].switchDirection()
    Blender.Window.EditMode(1)
    Blender.Window.EditMode(0)
    frames2 = getFrames(curve[0])
    
    frames = [frames2[numOfFrames-1]]
    for frame in frames1:
        frames.append(frame)
        
    return frames


def main():
    
    segments = 5
    
    scene = Blender.Scene.GetCurrent()
    object = scene.getActiveObject()
    if object != None:        

        if object.getType() == 'Curve':
            frames = framePositions(object,segments)
            for frame in frames:
                empty = scene.objects.new('Empty')
                empty.setMatrix(frame)
    
            Blender.Redraw()
        else:
            print('active Object not a curve - skipped getCurveDivisionPoints')

    print("-------------------")
    
    
main()

it’s quite a hack until now, and i can’t get an empty at the start of the curve right now. But i think it should be possible.

Can anybody tell why the switchDirection() command does not affect the curve direction until i enter edit mode manually?

Just noticed your post in the thread about “Percent along a path”.

It seems that you’ve moved to the dupliframes approach, but you were initially trying to move an object down the path and record it’s position to place an empty. This is a similar technique used in the script to bake a bezier curve to Ipos for use in the game engine. You can check out that script here.

An empty is created and the frame changed incrementally so that an Ipo key can be inserted at that location. I seem to remember there were issues with inserting the Ipo keys that required a second empty or something, but the info is all in the thread and it probably won’t effect what you’re trying to do.

Good luck whichever method you finally choose. :slight_smile:

Too bad I’m not so good at scripting yet, I do have an idea to solve your problem (to get 5 points equally spaced on a curve).

First convert the curve to a mesh (currently this is done manual, can’t find the script for that) but when it’s done with a script the curve could be copied and converted to a mesh so the original curve is not touched). Than get the total edge length/distance. Like so perhaps:

lengthE = 0

for e in me.edges:
    lengthE =+ e.length 
        
print lengthE

That is the 100(%) and now divide that number with 4. Not by 5, because this is what you do when you have one point at start and one at end of the curve (= four edges/steps). The result we will call “average vertex place”.

Next we need to know the end’s of the curve, this can be done by checking which vertex is not part of two edges and than pick one vertex to start with (doesn’t mater which one). After that we start counting the length of the the vertex’s together from the start point (one by one) and when they match around the “average vertex place” we can number that vertex or add something (like a object there, because we have the coordinate).

Problem with this approach is that you don’t have the exact location of the 5 divided points, but the very close by vertex.

:slight_smile:

@funky: thanks for the reply, i think i could go back to the initial approach… have to think about it, the threadlink you posted gives a few very good hints. i have one idea left to get the first frame (the frame at the curves startpoint), if it works i’ll stay with dupliframes.

@Dune: this was one of the first things i tried, but as you can’t control the conversion from a curve to a mesh i didn’t continue working on it. I think the dupliframes approach should work, i just have to stay tuned…

ok, i’m still stuck with this. By now i think it’s not a scripting problem but a general one of dupliframes. I can’t get it right when trying to model this by hand. The startframe - the frame at the curve start point - is always missing, even if you set dupliframes by hand. Does anybody hava a solution to this?

Even more, it seems to be a general blender issue. It’s even the same in the animation. If you parent an object to a curve/path, the position that the object is starting at is not the curve start point, but the first frame along the curve.
If you have an animation with around 500 frames nobody is ever going to mention that, but if you want to have 5 frames along a path this is a serious issue. And it’s the same with the Curve to IPO script. If you set the animation length to 4 or 5 frames or something as short, you can see it.
Is it a bug? Or am i completely mistaken?

ok, it’s a very bad bad hack, but it works. The thing with the curve start point isn’t solved, i just set the pathlength to 1.000.000 to get a frame very close to the curves startpoint. This should be sufficient for the most tasks, allthough i’m not satisfied with it.

For anyone who wants to use it, heres the final script:

#!BPY
"""
Name: 'curveFrames'
Blender: 248a
Group: 'Curve'
Tooltip: 'distributes Empties on a Curve'
"""

import Blender

    
# ----- get (almost?) orthogonal frame positions on a curve
def framePositions(object,numOfFrames):

    # --- get the frames of a path    
	def getFrames(curve,frameCount):
	
		# make a dummy object that moves along the curve
		tmpOb = Blender.Object.New("Empty","tmpOb")
		ob.makeParent([tmpOb],1)
		# set up the dupliframes for the dummy object
		frames = []
		tmpOb.enableDupFrames = 1
		tmpOb.DupEnd = frameCount
		# get the 4x4 worldspace duplimatrix for each frame
		for dpl in tmpOb.DupObjects:
		    frames.append(dpl[1])
		return frames


	ob = object.copy()
	curve = ob.getData()
	curve.setFlag(31)
	# so this is the hack for the start point
	curve.setPathLen(1000000)
	frames = getFrames(curve[0],1)
	# get the rest of the positions along the curve
	curve.setPathLen(numOfFrames)
	frames2 = getFrames(curve[0],numOfFrames)
	for f in frames2:
		frames.append(f)
	return frames


# MAIN this should be quite clear
def main():
	
	# set the number of desired frames here:
	frames = 5
	
	segments = frames - 1
	scene = Blender.Scene.GetCurrent()
	object = scene.getActiveObject()
	
	if object != None:        
		if object.getType() == 'Curve':
			frames = framePositions(object,segments)
			for frame in frames:
				empty = scene.objects.new('Empty')
				empty.setMatrix(frame)
				Blender.Redraw()
		else:
			print('active Object not a curve - skipped getCurveDivisionPoints')
	else:
		print('no object active')
		
	print("-------------------")
	
	  
main()

for some reason this script is not working on path curves yet. As i do not need them right now, i won’t fix that.

is it possible to addempties to each control point on a Bezier or a path curve too!

i’d like thsi for the Spring rig at underround blender

it’s a nice helix in 3D but you got to add an emtpy at each control point for the stretch boen

and that’s a pain in the neck when you 75 or 200 points ?

keep up the good work

Thanks

i only tested this script for nurbs curves. But it should work for Bezier Curves too, and then you can have as many empties as you want.

The Script just returns a BlenderMatrix for each point on the curve. Just set that Matrices as the OrientationMatrix of the Empties, that should do the job.
pseudocodish:


- get as many frames on the curve as you want to
- for each frame:
    create an empty
    set the empties orientationMatrix to the actual frame

hope that helps

i’ll test some more this week

does it also create some new frames or not ?

cause i don’t need frame here only empties at the right location
but also to make these parented to the point on the curve as vertex parent

can it be modified to do that too

sorry not execellent yet at python still learning too

Thanks

i added a simple bezier curve and run the script

and i got an empty at each control point on bezier curve

begining to llokk good

now the only rhing is that the empty added is not as per the global system ZYZ

it is at a certain angle

now is there a way to change the angle of the enpty ?

not certain if this can be usefullt as it is but may be can be modified easily !

tell me what you think about it ?

i;m trying to follow the script for the Spring at underground blender
don’t know if you have seen it ?

the curve is an helix and one of the model requires to add empty at each control point

but the other model requires to add a hook

are empty = to hook ?

Thanks

The angle of the empty is being inherited from the curve because the CurveFollow button is pressed. This is set with the code


curve.setFlag(31)

If we visit the doc page:
http://www.zoo-logique.org/3D.Blender/scripts_python/API/Curve.Curve-class.html

Click on getFlag to see the breakdown of how to compute bits. We can, once again, see that the documentation is wrong. The documentation specifies that there is a “Bit 0”. This is a pink elephant, no bit 0 exists in any computer anywhere. It is always bit 1. The labels for the bit count are off by one in the documentation. So bit 1 controls the 3D flag of the curve, not “Bit:0”. In theory, If I set my bit flag to 1, the 3D button turns on, if I set it to 0, the 3D button turns off. But this only works if the curve is a path, so you have to add in the CurvePath bit which is in the 8’s position as well. Giving you a grand total of 9 for your set bits. Try replacing 31 with 9 and running the script.

ok i 'll do a test with this new 9 value !

but i tested with a bezier cuve not a path

is it necesary to use a path ?
or is it possible to do it with a bezier like it’s done in blender

cause if you make a bezier curve in 3D it think it is a path ?

alos what is this thing between path and frames?

now is it possible to modify this script to do only the enpties addition

i mean i can seea lot of frames addtition and wokring on frame

whish may be nice if you need it

but i my case i simply need to add enpties at the right place as per the global system

i think it should be possible to change it and make it more simpler than what i ti is now ?

Apprecciate any help

@RickyBlender:


import Blender
from Blender import Scene
scn = Scene.getCurrent()
obj = scn.objects.active
if obj.type == "Curve":
 for curve in obj.data:
  for point in curve:
   empty = scn.objects.new('Empty')
   empty.setLocation(point.vec[1])

:wink:

Edit: This is for Bezier curves. Some modification may be necessary to make it work with other curve types.

Edit 2: @LaPostal: Just realised that you could maybe replace your hack with another that would be exact.

You could add in the first empty using a modification of my little script above and instead of iterating through the points, place an empty on point 0 of curve 0.

“point” is a BezTriple object and the location of the handles are obtained by using the point.vec[] list of vectors. The central point or the knot at the start of the curve is second in the list at point.vec[1]

There can be numerous unconnected curves in a bezier curve (add more but in edit mode rather than object mode) and these are iterated through during the “for curve in obj.data” loop. If you aren’t bothered about any secondary curves, you could just place an empty at (if your curve object is called obj) obj.data[0][0].vec[1]