[feature] spline animation

This commit is contained in:
jrkb 2025-06-05 10:59:14 +02:00
parent f02f8fc2f0
commit cd99362bb1

105
butils.py
View file

@ -221,26 +221,26 @@ def get_hook_modifiers(blender_object: bpy.types.Object):
return [m for m in blender_object.modifiers if m.type == "HOOK"] return [m for m in blender_object.modifiers if m.type == "HOOK"]
class BezierSplinePoint: class HookBezierSplinePoint:
def __init__( def __init__(
self, self,
co: mathutils.Vector,
handle_left: mathutils.Vector, handle_left: mathutils.Vector,
co: mathutils.Vector,
handle_right: mathutils.Vector, handle_right: mathutils.Vector,
): ):
self.co: mathutils.Vector = co self.handle_left: mathutils.Vector = mathutils.Vector(handle_left)
self.handle_left: mathutils.Vector = handle_left self.co: mathutils.Vector = mathutils.Vector(co)
self.handle_right: mathutils.Vector = handle_right self.handle_right: mathutils.Vector = mathutils.Vector(handle_right)
class BezierSpline: class HookBezierSpline:
def __init__( def __init__(
self, self,
n: int, n: int,
use_cyclic_u: bool, use_cyclic_u: bool,
resolution_u: int, resolution_u: int,
): ):
self.bezier_points = [BezierSplinePoint] * n self.bezier_points = [HookBezierSplinePoint] * n
self.use_cyclic_u: int = use_cyclic_u self.use_cyclic_u: int = use_cyclic_u
self.resolution_u: int = resolution_u self.resolution_u: int = resolution_u
self.beziers: [] self.beziers: []
@ -249,34 +249,31 @@ class BezierSpline:
def calc_length(self, resolution) -> float: def calc_length(self, resolution) -> float:
# ignore resolution when accessing length to imitate blender function # ignore resolution when accessing length to imitate blender function
print(f"{self.total_length=}")
return self.total_length return self.total_length
class BezierData: class HookBezierData:
def __init__(self, n): def __init__(self, n):
self.splines = [BezierSpline] * n self.splines = [HookBezierSpline] * n
class BezierCurve: class HookBezierCurve:
def __init__(self, blender_curve: bpy.types.Object, resolution_factor=1.0): def __init__(self, blender_curve: bpy.types.Object, resolution_factor=1.0):
self.data = BezierData(len(blender_curve.data.splines)) self.data = HookBezierData(len(blender_curve.data.splines))
i = 0 i = 0
hooks = get_hook_modifiers(blender_curve) hooks = get_hook_modifiers(blender_curve)
print(f"{blender_curve.name=} =============================================")
for si, blender_spline in enumerate(blender_curve.data.splines): for si, blender_spline in enumerate(blender_curve.data.splines):
self.data.splines[si] = BezierSpline( self.data.splines[si] = HookBezierSpline(
len(blender_spline.bezier_points), len(blender_spline.bezier_points),
blender_spline.use_cyclic_u, blender_spline.use_cyclic_u,
blender_spline.resolution_u, blender_spline.resolution_u,
) )
for pi, blender_bezier_point in enumerate(blender_spline.bezier_points): for pi, blender_bezier_point in enumerate(blender_spline.bezier_points):
self.data.splines[si].bezier_points[pi] = BezierSplinePoint( self.data.splines[si].bezier_points[pi] = HookBezierSplinePoint(
blender_bezier_point.co,
blender_bezier_point.handle_left, blender_bezier_point.handle_left,
blender_bezier_point.co,
blender_bezier_point.handle_right, blender_bezier_point.handle_right,
) )
print(pi)
for hook in hooks: for hook in hooks:
hook_co = False hook_co = False
hook_handle_left = False hook_handle_left = False
@ -293,40 +290,57 @@ class BezierCurve:
blender_curve.matrix_world.inverted() blender_curve.matrix_world.inverted()
@ hook.object.matrix_world.translation @ hook.object.matrix_world.translation
) )
print(f"co {location=}")
self.data.splines[si].bezier_points[pi].co = ( self.data.splines[si].bezier_points[pi].co = (
self.data.splines[si] self.data.splines[si]
.bezier_points[pi] .bezier_points[pi]
.co.lerp(location, hook.strength) .co.lerp(location, hook.strength)
) )
# if hook_handle_left: if hook_handle_left:
# location = ( location_left = location + (
# hook.object.matrix_world.translation self.data.splines[si].bezier_points[pi].co
# - blender_curve.matrix_world.translation - self.data.splines[si].bezier_points[pi].handle_left
# ) + mathutils.Vector((-1, 0, 0)) )
# print(f"handle_left {location=}") self.data.splines[si].bezier_points[pi].handle_left = (
# self.data.splines[si].bezier_points[pi].handle_left = ( self.data.splines[si]
# self.data.splines[si] .bezier_points[pi]
# .bezier_points[pi] .co.lerp(location_left, hook.strength)
# .handle_left.lerp(location, hook.strength) )
# ) if hook_handle_right:
# if hook_handle_right: location_right = location + (
# location = ( self.data.splines[si].bezier_points[pi].co
# hook.object.matrix_world.translation - self.data.splines[si].bezier_points[pi].handle_right
# - blender_curve.matrix_world.translation )
# ) self.data.splines[si].bezier_points[pi].handle_right = (
# self.data.splines[si].bezier_points[pi].handle_right = ( self.data.splines[si]
# self.data.splines[si] .bezier_points[pi]
# .bezier_points[pi] .co.lerp(location, hook.strength)
# .handle_right.lerp(location, hook.strength) )
# ) elif hook_handle_left:
location = (
blender_curve.matrix_world.inverted()
@ hook.object.matrix_world.translation
)
self.data.splines[si].bezier_points[pi].handle_left = (
self.data.splines[si]
.bezier_points[pi]
.handle_left.lerp(location, hook.strength)
)
elif hook_handle_right:
location = (
blender_curve.matrix_world.inverted()
@ hook.object.matrix_world.translation
)
self.data.splines[si].bezier_points[pi].handle_right = (
self.data.splines[si]
.bezier_points[pi]
.handle_right.lerp(location, hook.strength)
)
i += 1 i += 1
( (
self.data.splines[si].beziers, self.data.splines[si].beziers,
self.data.splines[si].lengths, self.data.splines[si].lengths,
self.data.splines[si].total_length, self.data.splines[si].total_length,
) = get_real_beziers_and_lengths(self.data.splines[si], resolution_factor) ) = get_real_beziers_and_lengths(self.data.splines[si], resolution_factor)
print(f"total length {self.data.splines[si].total_length}")
def get_real_beziers_and_lengths(bezier_spline_obj, resolution_factor): def get_real_beziers_and_lengths(bezier_spline_obj, resolution_factor):
@ -391,7 +405,7 @@ def calc_point_on_bezier_spline(
if p.handle_left == p.co and len(bezier_spline_obj.bezier_points) > 1: if p.handle_left == p.co and len(bezier_spline_obj.bezier_points) > 1:
beziers, lengths, total_length = ( beziers, lengths, total_length = (
get_real_beziers_and_lengths(bezier_spline_obj, resolution_factor) get_real_beziers_and_lengths(bezier_spline_obj, resolution_factor)
if not isinstance(bezier_spline_obj, BezierSpline) if not isinstance(bezier_spline_obj, HookBezierSpline)
else ( else (
bezier_spline_obj.beziers, bezier_spline_obj.beziers,
bezier_spline_obj.lengths, bezier_spline_obj.lengths,
@ -411,7 +425,7 @@ def calc_point_on_bezier_spline(
beziers, lengths, total_length = ( beziers, lengths, total_length = (
get_real_beziers_and_lengths(bezier_spline_obj, resolution_factor) get_real_beziers_and_lengths(bezier_spline_obj, resolution_factor)
if not isinstance(bezier_spline_obj, BezierSpline) if not isinstance(bezier_spline_obj, HookBezierSpline)
else ( else (
bezier_spline_obj.beziers, bezier_spline_obj.beziers,
bezier_spline_obj.lengths, bezier_spline_obj.lengths,
@ -460,16 +474,15 @@ def calc_point_on_bezier_curve(
output_spline_index=False, output_spline_index=False,
resolution_factor=1.0, resolution_factor=1.0,
): ):
# bezier_curve = BezierCurve(bezier_curve_obj) bezier_curve = HookBezierCurve(bezier_curve_obj)
# curve = bezier_curve.data curve = bezier_curve.data
curve = bezier_curve_obj.data # curve = bezier_curve_obj.data
# Loop through all splines in the curve # Loop through all splines in the curve
total_length = 0 total_length = 0
for i, spline in enumerate(curve.splines): for i, spline in enumerate(curve.splines):
resolution = int(spline.resolution_u * resolution_factor) resolution = int(spline.resolution_u * resolution_factor)
length = spline.calc_length(resolution=resolution) length = spline.calc_length(resolution=resolution)
print(f"{utils.LINE()} {length=}")
if total_length + length > distance or i == len(curve.splines) - 1: if total_length + length > distance or i == len(curve.splines) - 1:
if output_spline_index and output_tangent: if output_spline_index and output_tangent:
# return value from c_p_o_b_s is a tuple # return value from c_p_o_b_s is a tuple