From 58e0df34275ce4373fc96b675b0984e50d8ed848 Mon Sep 17 00:00:00 2001 From: themancalledjakob Date: Wed, 4 Jun 2025 21:28:08 +0200 Subject: [PATCH] first step spline animation --- __init__.py | 14 ++++++ butils.py | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 152 insertions(+), 4 deletions(-) diff --git a/__init__.py b/__init__.py index 8d9a59d..a4021ab 100644 --- a/__init__.py +++ b/__init__.py @@ -2040,13 +2040,27 @@ def on_depsgraph_update(scene, depsgraph): elif butils.is_text_object_legit(u.id.original): # must be duplicate link_text_object_with_new_text_properties(u.id.original, scene) + # butils.prepare_text( + # text_properties.font_name, + # text_properties.face_name, + # text_properties.text, + # ) elif ( butils.is_text_object_legit(u.id.original) and len(u.id.original.users_collection) > 0 ): # must be a new thing, maybe manually created or so link_text_object_with_new_text_properties(u.id.original, scene) + # butils.prepare_text( + # text_properties.font_name, + # text_properties.face_name, + # text_properties.text, + # ) butils.clean_text_properties() + try: + butils.set_text_on_curve(text_properties) + except: + pass update_active_text_index() unlock_depsgraph_updates() diff --git a/butils.py b/butils.py index e7aa72d..67367df 100644 --- a/butils.py +++ b/butils.py @@ -217,6 +217,118 @@ def calc_bezier_length(bezier_point_1, bezier_point_2, resolution=20): return length +def get_hook_modifiers(blender_object: bpy.types.Object): + return [m for m in blender_object.modifiers if m.type == "HOOK"] + + +class BezierSplinePoint: + def __init__( + self, + co: mathutils.Vector, + handle_left: mathutils.Vector, + handle_right: mathutils.Vector, + ): + self.co: mathutils.Vector = co + self.handle_left: mathutils.Vector = handle_left + self.handle_right: mathutils.Vector = handle_right + + +class BezierSpline: + def __init__( + self, + n: int, + use_cyclic_u: bool, + resolution_u: int, + ): + self.bezier_points = [BezierSplinePoint] * n + self.use_cyclic_u: int = use_cyclic_u + self.resolution_u: int = resolution_u + self.beziers: [] + self.lengths: [float] + self.total_length: float + + def calc_length(self, resolution) -> float: + # ignore resolution when accessing length to imitate blender function + print(f"{self.total_length=}") + return self.total_length + + +class BezierData: + def __init__(self, n): + self.splines = [BezierSpline] * n + + +class BezierCurve: + def __init__(self, blender_curve: bpy.types.Object, resolution_factor=1.0): + self.data = BezierData(len(blender_curve.data.splines)) + i = 0 + hooks = get_hook_modifiers(blender_curve) + print(f"{blender_curve.name=} =============================================") + for si, blender_spline in enumerate(blender_curve.data.splines): + self.data.splines[si] = BezierSpline( + len(blender_spline.bezier_points), + blender_spline.use_cyclic_u, + blender_spline.resolution_u, + ) + for pi, blender_bezier_point in enumerate(blender_spline.bezier_points): + self.data.splines[si].bezier_points[pi] = BezierSplinePoint( + blender_bezier_point.co, + blender_bezier_point.handle_left, + blender_bezier_point.handle_right, + ) + print(pi) + for hook in hooks: + hook_co = False + hook_handle_left = False + hook_handle_right = False + for vi in hook.vertex_indices: + if vi == i * 3: + hook_handle_left = True + elif vi == i * 3 + 1: + hook_co = True + elif vi == i * 3 + 2: + hook_handle_right = True + if hook_co: + location = ( + blender_curve.matrix_world.inverted() + @ 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.lerp(location, hook.strength) + ) + # if hook_handle_left: + # location = ( + # hook.object.matrix_world.translation + # - blender_curve.matrix_world.translation + # ) + 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.lerp(location, hook.strength) + # ) + # if hook_handle_right: + # location = ( + # hook.object.matrix_world.translation + # - blender_curve.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 + ( + self.data.splines[si].beziers, + self.data.splines[si].lengths, + self.data.splines[si].total_length, + ) = 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): beziers = [] lengths = [] @@ -277,8 +389,14 @@ def calc_point_on_bezier_spline( # if the bezier points sit on each other we have same issue # but that is then to be fixed in the bezier if p.handle_left == p.co and len(bezier_spline_obj.bezier_points) > 1: - beziers, lengths, total_length = get_real_beziers_and_lengths( - bezier_spline_obj, resolution_factor + beziers, lengths, total_length = ( + get_real_beziers_and_lengths(bezier_spline_obj, resolution_factor) + if not isinstance(bezier_spline_obj, BezierSpline) + else ( + bezier_spline_obj.beziers, + bezier_spline_obj.lengths, + bezier_spline_obj.total_length, + ) ) travel_point = calc_point_on_bezier(beziers[0][1], beziers[0][0], 0.001) travel = travel_point.normalized() * distance @@ -291,8 +409,14 @@ def calc_point_on_bezier_spline( else: return location - beziers, lengths, total_length = get_real_beziers_and_lengths( - bezier_spline_obj, resolution_factor + beziers, lengths, total_length = ( + get_real_beziers_and_lengths(bezier_spline_obj, resolution_factor) + if not isinstance(bezier_spline_obj, BezierSpline) + else ( + bezier_spline_obj.beziers, + bezier_spline_obj.lengths, + bezier_spline_obj.total_length, + ) ) iterated_distance = 0 @@ -336,6 +460,8 @@ def calc_point_on_bezier_curve( output_spline_index=False, resolution_factor=1.0, ): + # bezier_curve = BezierCurve(bezier_curve_obj) + # curve = bezier_curve.data curve = bezier_curve_obj.data # Loop through all splines in the curve @@ -343,6 +469,7 @@ def calc_point_on_bezier_curve( for i, spline in enumerate(curve.splines): resolution = int(spline.resolution_u * resolution_factor) length = spline.calc_length(resolution=resolution) + print(f"{utils.LINE()} {length=}") if total_length + length > distance or i == len(curve.splines) - 1: if output_spline_index and output_tangent: # return value from c_p_o_b_s is a tuple @@ -1704,7 +1831,11 @@ def set_text_on_curve( # global lock_depsgraph_update_n_times # starttime = time.perf_counter_ns() + if text_properties is None: + return False mom = text_properties.text_object + if mom is None: + return False if mom.type != "CURVE": return False if len(mom.users_collection) < 1: @@ -1812,6 +1943,9 @@ def set_text_on_curve( location, tangent, spline_index = calc_point_on_bezier_curve( mom, applied_advance, True, True ) + # location, tangent, spline_index = calc_point_on_bezier_curve( + # mom_hooked, applied_advance, True, True + # ) # check if we are on a new line if spline_index != previous_spline_index: