1
$\begingroup$

I've been trying to generalize the solution to this question: Using python to create a curve and attach its endpoints with hooks to two spheres, so that it can work on an arbitrarily long bezier curve, but I can't seem to get the hook_assign() function to work right. (See also How to use bpy ops that take a modifier Enum as an argument?)

When I print the state of .select_control_point for each point on the curve before (or after) I call the hook_assign function, the console output looks right except the function still returns Warning: Requires selected vertices or active vertex group.

Below the code is some sample console feedback.

noodcurv = bpy.data.curves.new('noodcurve', type='CURVE')       #create a new curve
noodcurv.dimensions = '3D'                                      #set some settings
noodcurv.resolution_u = 2
noodcurv.bevel_object = bevob
noodcurv.use_fill_caps = True

bez = noodcurv.splines.new('BEZIER')                            #create a new spline
bez.bezier_points.add(len(coords)-1)                            #add points
for k, coord in enumerate(coords):                              #set coordinates & handles
    p = bez.bezier_points[k]
    p.co = coord
    p.handle_right_type = 'VECTOR'                              #or 'AUTOMATIC', 'ALIGNED', 'FREE_ALIGN'
    p.handle_left_type = 'VECTOR'

noodcurvobj = bpy.data.objects.new('noodcurve', noodcurv)       #create object
modnames = ['hook.'+str(k).zfill(3) for k in range(len(coords))] #create modifier names

for k, coord in enumerate(coords):                              #create modifiers & set Parent Object
    noodcurvobj.modifiers.new(modnames[k], type='HOOK')         
    noodcurvobj.modifiers[modnames[k]].object = bpy.context.scene.objects['link'+modnames[k][4:]]

scn = bpy.context.scene                                         #scene variable
scn.objects.link(noodcurvobj)                                   #link to scene & activate
scn.objects.active = noodcurvobj

bez.bezier_points[0].select_control_point = True                #this fixes the warning!?

bpy.ops.object.mode_set(mode='EDIT')                            #change context
for k, coord in enumerate(coords):                              #assign points to modifiers
    p = bez.bezier_points[k]                                    #assign points to variables
    p.select_control_point = True
    for q,t in enumerate(bez.bezier_points):                    #debugging check
        print('control point: ', q, ' is', t.select_control_point)
    bpy.ops.object.hook_assign(modifier=modnames[k])            #assign vextex to modifier
    p.select_control_point = False

bpy.ops.object.mode_set(mode='OBJECT')

Some of the console output:

control point:  0  is True
control point:  1  is False
control point:  2  is False
control point:  3  is False
control point:  4  is False
Warning: Requires selected vertices or active vertex group

control point:  0  is False
control point:  1  is True
control point:  2  is False
control point:  3  is False
control point:  4  is False
Warning: Requires selected vertices or active vertex group

control point:  0  is False
control point:  1  is False
control point:  2  is True
control point:  3  is False
control point:  4  is False
Warning: Requires selected vertices or active vertex group
...

(I know it'd be better if I could use one loop instead of three, but as I ran into problems I kept rearranging stuff to more closely mimic the original answer, and at this point I'm out of ideas!)

I also noticed that if I set .select_control_point = True for one or more points before I enter the third loop, the warnings go away and the modifiers are all assigned to whatever control point(s) I selected before the loop (even though the console output will indicate I'm still stepping through the selected points correctly).

So what am I missing?

$\endgroup$
  • $\begingroup$ Now I'm noticing that if I move the .mode_set() function into the loop, I get an error, IndexError: bpy_prop_collection[index]: index 0 out of range, size 0, and if I move the .select_control_points = True to below the .mode_set() function I get the Warning: Requires selected vertices or active vertex group problem again. And if I place .mode_set() functions inside the loop (to mode='EDIT' at the start and 'mode='OBJECT' at the end, I get some weird errors, usually IndexError, but at other times an endless loop or segmentation fault, or very large (negative) index? $\endgroup$ – Cody Reisdorf Dec 24 '14 at 18:07
2
$\begingroup$

After a great deal of rearranging code and learning what doesn't work, I've finally found the solution! The key turned out to be these two lines from the answer to the related question, (Using python to create a curve and attach its endpoints with hooks to two spheres):

bpy.ops.object.mode_set(mode='EDIT')

# the mode_set() invalidated the pointers, so get fresh ones
p0 = curve.splines[0].bezier_points[0]
...

So all I needed was to add the line p = noodcurv.splines[0].bezier_points, before entering the loop. So this is the final code:

noodcurv = bpy.data.curves.new('noodcurve', type='CURVE')       #create a new curve
noodcurv.dimensions = '3D'                                      #set some settings
noodcurv.resolution_u = 2
noodcurv.bevel_object = bevob
noodcurv.use_fill_caps = True

bez = noodcurv.splines.new('BEZIER')                            #create a new spline
bez.bezier_points.add(len(coords)-1)                            #add points
p = bez.bezier_points                                           #for convenience
for k, coord in enumerate(coords):                              #set coordinates & handles
    p[k].co = coord
    p[k].handle_right_type = 'VECTOR'                           #or 'AUTOMATIC', 'ALIGNED', 'VECTOR'
    p[k].handle_left_type = 'VECTOR'

noodcurvobj = bpy.data.objects.new('noodcurve', noodcurv)       #create object
modnames = ['hook.'+str(k).zfill(3) for k in range(len(coords))] #create modifier names

for k, coord in enumerate(coords):                              #create modifiers & set Parent Object
    noodcurvobj.modifiers.new(modnames[k], type='HOOK')         
    noodcurvobj.modifiers[modnames[k]].object = bpy.context.scene.objects['link'+modnames[k][4:]]

scn = bpy.context.scene                                         #scene variable
scn.objects.link(noodcurvobj)                                   #link to scene & activate
scn.objects.active = noodcurvobj

bpy.ops.object.mode_set(mode='EDIT')                            #change context

p = noodcurv.splines[0].bezier_points
for k in range(len(coords)):                                    #assign points to modifiers
    p[k].select_control_point = True                            #select point
    p[k].select_left_handle = True
    p[k].select_right_handle = True
    bpy.ops.object.hook_assign(modifier=modnames[k])            #assign vextex to modifier
    p[k].select_control_point = False                           #deselect point
    p[k].select_left_handle = False
    p[k].select_right_handle = False
    p[k].handle_right_type = 'FREE'                             #or 'AUTOMATIC', 'ALIGNED', 'VECTOR'
    p[k].handle_left_type = 'FREE'

bpy.ops.object.mode_set(mode='OBJECT')
$\endgroup$
  • $\begingroup$ I returned to this project after a few years and it's broken, and haven't been able to figure out how to fix it yet. Seems to be a problem with the looping over the control points & hook assignment where it drops the list object and then can't loop properly, and I can't seem to get it to select it right. I'll update this if I can straighten it out. $\endgroup$ – Cody Reisdorf Apr 24 '17 at 3:57

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.