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?
.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 theWarning: Requires selected vertices or active vertex group
problem again. And if I place.mode_set()
functions inside the loop (tomode='EDIT'
at the start and 'mode='OBJECT' at the end, I get some weird errors, usuallyIndexError
, but at other times an endless loop or segmentation fault, or very large (negative) index? $\endgroup$