3

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.

R-800
  • 2,881
  • 1
  • 9
  • 23
Oye LKY
  • 51
  • 4

1 Answers1

3

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(&quot;empty&quot;, 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...

Brezdo
  • 51
  • 3
  • 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. – Graham Thomas Mar 20 '23 at 22:52