3
$\begingroup$

I made a script to add a hook modifier on every point of a curve using a for loop but I get an error in 2nd iteration.

Below is the code:

import bpy

curve = bpy.data.objects["Ellipse"]

bpy.ops.object.mode_set(mode = "EDIT")
bpy.ops.curve.select_all(action = "DESELECT")
        
for spline in curve.data.splines:
    for point in spline.bezier_points:
        point.select_control_point = True
        bpy.ops.object.hook_add_newob()
        bpy.ops.curve.select_all(action = "DESELECT")

Below is the error details I get in 2nd iteration

Error: Requires selected vertices or active vertex group
Traceback (most recent call last):
  File "\Text", line 11, in <module>
  File "C:\Program Files\Blender Foundation\Blender 2.83\2.83\scripts\modules\bpy\ops.py", line 201, in __call__
    ret = op_call(self.idname_py(), None, kw)
RuntimeError: Error: Requires selected vertices or active vertex group

From what I gather, it seem that the hook modifier is applied before even the point is selected. But I don't know how to resolve this. Any sort of help is much appreciated. Thanks in advance.

$\endgroup$

1 Answer 1

3
$\begingroup$

EDIT: in this answer hook is added to each vertex to animate a mesh using slightly different code: How can I animate a vertex translation of a triangular mesh?

In short, this will work (tested on Blender 3.0):

import bpy

curve = bpy.context.active_object

# this can fail, check bpy.ops.object.mode_set.poll()
bpy.ops.object.mode_set(mode = "EDIT")
bpy.ops.curve.select_all(action = "DESELECT")

for spline in curve.data.splines:
    for point in spline.bezier_points:
        point.select_control_point = True
        point.select_left_handle = True
        point.select_right_handle = True

        bpy.ops.object.hook_add_newob()

        point.select_control_point = False
        point.select_left_handle = False
        point.select_right_handle = False

What you experienced is the essence why you in general should not use bpy.ops in scripts: the side effect of bpy.ops.object.hook_add_newob happens to be that new object is selected and the curve is deselected. I suppose it messes with execution of the operator bpy.ops.curve.select_all(action = "DESELECT") but I did not do extensive research.

To ilustrate the problem: add new curve, select one of points and execute in sripting:

>>> C.selected_objects
[bpy.data.objects['BezierCurve']]

>>> bpy.ops.object.hook_add_newob()
{'FINISHED'}

>>> C.active_object
bpy.data.objects['BezierCurve']

>>> C.selected_objects
[bpy.data.objects['Empty']]

In my opinion this is better but more complicated solution:

import bpy

curve = bpy.context.active_object

for spline in curve.data.splines:
    for i, point in enumerate(spline.bezier_points):
        m = curve.modifiers.new(name='scripted hook', type='HOOK')

        o = bpy.data.objects.new("empty", None )
        
        bpy.context.scene.collection.objects.link( o )
        
        m.center = point.co
        o.location = point.co + curve.location

        m.vertex_indices_set([i*3, i*3+1, i*3+2])  # left handle, point, right handle?
        bpy.context.evaluated_depsgraph_get() # magic spell
        m.object = o

If you omit the line with depsgraph evaluation, the hook modifier will activate too quickly and the vertex will be moved by object. I do not know why...

$\endgroup$
1
  • $\begingroup$ I have been fighting with bpy.ops forever now. half the time it works and half the time it doesn't. This saved me a ton of time from having to change my whole approach thankyou. $\endgroup$ Mar 20, 2023 at 22:52

You must log in to answer this question.

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