From 04229fbc311ff74001e38b86de72160a58c00582 Mon Sep 17 00:00:00 2001 From: themancalledjakob Date: Thu, 29 May 2025 19:32:56 +0200 Subject: [PATCH] [fix] fix bezier when individual handles sit on points bonus: prevent eternal while loop --- butils.py | 97 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 68 insertions(+), 29 deletions(-) diff --git a/butils.py b/butils.py index 11a30ca..557054a 100644 --- a/butils.py +++ b/butils.py @@ -215,6 +215,34 @@ def calc_bezier_length(bezier_point_1, bezier_point_2, resolution=20): return length +def get_real_beziers_and_lengths(bezier_spline_obj, resolution_factor): + beziers = [] + lengths = [] + total_length = 0 + n_bezier_points = len(bezier_spline_obj.bezier_points) + real_n_bezier_points = len(bezier_spline_obj.bezier_points) + if bezier_spline_obj.use_cyclic_u: + n_bezier_points += 1 + for i in range(0, n_bezier_points - 1): + i_a = i % (n_bezier_points - 1) + i_b = (i_a + 1) % real_n_bezier_points + bezier = [ + bezier_spline_obj.bezier_points[i_a], + bezier_spline_obj.bezier_points[i_b], + ] + length = calc_bezier_length( + bezier[0], + bezier[1], + int(bezier_spline_obj.resolution_u * resolution_factor), + ) + total_length += length + beziers.append(bezier) + lengths.append(length) + # if total_length > distance: + # break + return beziers, lengths, total_length + + def calc_point_on_bezier_spline( bezier_spline_obj, distance, output_tangent=False, resolution_factor=1.0 ): @@ -241,6 +269,17 @@ def calc_point_on_bezier_spline( if distance <= 0: p = bezier_spline_obj.bezier_points[0] travel = (p.co - p.handle_left).normalized() * distance + + # in case the handles sit on the points + # we interpolate the travel from points of the bezier + # 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) + travel_point = calc_point_on_bezier(beziers[0][1], beziers[0][0], 0.001) + travel = travel_point.normalized() * distance + location = p.co + travel if output_tangent: p2 = bezier_spline_obj.bezier_points[1] @@ -249,30 +288,8 @@ def calc_point_on_bezier_spline( else: return location - beziers = [] - lengths = [] - total_length = 0 - n_bezier_points = len(bezier_spline_obj.bezier_points) - real_n_bezier_points = len(bezier_spline_obj.bezier_points) - if bezier_spline_obj.use_cyclic_u: - n_bezier_points += 1 - for i in range(0, n_bezier_points - 1): - i_a = i % (n_bezier_points - 1) - i_b = (i_a + 1) % real_n_bezier_points - bezier = [ - bezier_spline_obj.bezier_points[i_a], - bezier_spline_obj.bezier_points[i_b], - ] - length = calc_bezier_length( - bezier[0], - bezier[1], - int(bezier_spline_obj.resolution_u * resolution_factor), - ) - total_length += length - beziers.append(bezier) - lengths.append(length) - # if total_length > distance: - # break + beziers, lengths, total_length = get_real_beziers_and_lengths(bezier_spline_obj, + resolution_factor) iterated_distance = 0 for i in range(0, len(beziers)): @@ -290,7 +307,16 @@ def calc_point_on_bezier_spline( # if we are here, the point is outside the spline last_i = len(beziers) - 1 p = beziers[last_i][1] + travel = (p.handle_right - p.co).normalized() * (distance - total_length) + + # in case the handles sit on the points + # we interpolate the travel from points of the bezier + # 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_right == p.co and len(beziers) > 0: + travel_point = calc_point_on_bezier(beziers[-1][1], beziers[-1][0], 0.001) + travel = travel_point.normalized() * (distance - total_length) location = p.co + travel if output_tangent: tangent = calc_tangent_on_bezier(beziers[last_i][0], p, 1) @@ -580,7 +606,7 @@ def update_available_fonts(): f = abc3d_data.available_fonts.add() f.font_name = font_name f.face_name = face_name - print(f"{__name__} added {font_name} {face_name}") + print(f"{utils.prefix()}::update_available_fonts: {__name__} added {font_name} {face_name}") # def update_available_texts(): @@ -1474,26 +1500,39 @@ def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4, mom, advance + glyph_advance, False, True ) if psi == si: + n_max = 100 + n = 0 while ( previous_location - new_location - ).length > glyph_advance and psi == si: + ).length > glyph_advance and psi == si and n < n_max: curve_compensation = curve_compensation - glyph_advance * 0.01 - new_location, si = calc_point_on_bezier_curve( + tmp_new_location, si = calc_point_on_bezier_curve( mom, advance + glyph_advance + curve_compensation, output_tangent=False, output_spline_index=True, ) + if tmp_new_location == new_location: + print(f"{utils.prefix()}::set_text_on_curve::compensate_curvature while loop overstaying welcome") + break + new_location = tmp_new_location + n += 1 + n = 0 while ( previous_location - new_location - ).length < glyph_advance and psi == si: + ).length < glyph_advance and psi == si and n < n_max: curve_compensation = curve_compensation + glyph_advance * 0.01 - new_location, si = calc_point_on_bezier_curve( + tmp_new_location, si = calc_point_on_bezier_curve( mom, advance + glyph_advance + curve_compensation, output_tangent=False, output_spline_index=True, ) + if tmp_new_location == new_location: + print(f"{utils.prefix()}::set_text_on_curve::compensate_curvature while loop overstaying welcome") + break + new_location = tmp_new_location + n += 1 advance = advance + glyph_advance + curve_compensation glyph_index += 1