From 88cfaf3be7a09030f3e016e887ee2a2977a13efb Mon Sep 17 00:00:00 2001 From: themancalledjakob Date: Sun, 25 May 2025 20:36:46 +0200 Subject: [PATCH] detect textobject and allow primitive duplication --- __init__.py | 14 +++-- butils.py | 165 ++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 151 insertions(+), 28 deletions(-) diff --git a/__init__.py b/__init__.py index 34bb3fb..b2bd911 100644 --- a/__init__.py +++ b/__init__.py @@ -137,9 +137,12 @@ class ABC3D_glyph_properties(bpy.types.PropertyGroup): def update_callback(self, context): if self.text_id >= 0: - butils.set_text_on_curve( - context.scene.abc3d_data.available_texts[self.text_id] - ) + # butils.set_text_on_curve( + # context.scene.abc3d_data.available_texts[self.text_id] + # ) + t = butils.get_text_properties(self.text_id) + if t is not None: + butils.set_text_on_curve(t) glyph_id: bpy.props.StringProperty(maxlen=1) text_id: bpy.props.IntProperty( @@ -259,6 +262,7 @@ class ABC3D_text_properties(bpy.types.PropertyGroup): ) distribution_type: bpy.props.StringProperty() glyphs: bpy.props.CollectionProperty(type=ABC3D_glyph_properties) + actual_text: bpy.props.StringProperty() class ABC3D_data(bpy.types.PropertyGroup): @@ -1942,8 +1946,8 @@ def on_depsgraph_update(scene, depsgraph): print(" updated geometry is true") print(f" is {len(scene.abc3d_data.available_texts)} bigger than {text_id=} is true?") # butils.detect_texts() - if len(scene.abc3d_data.available_texts) > text_id: - text_properties = scene.abc3d_data.available_texts[text_id] + text_properties = butils.get_text_properties(text_id) + if text_properties is not None: print(" YES") print(f" is ? {text_properties.text_object.name=} is {u.id.name=}") if text_properties.text_object == u.id.original: diff --git a/butils.py b/butils.py index 7c38fe5..57dea5b 100644 --- a/butils.py +++ b/butils.py @@ -927,44 +927,120 @@ def get_glyph(glyph_id, font_name, face_name, notify_on_replacement=False): return glyph_tmp.original -def transfer_text_object_to_text_properties(o, text_properties, id_from_text_properties=True): +def get_text_properties(text_id, scene = None): + if scene is None: + scene = bpy.context.scene + abc3d_data = scene.abc3d_data + for t in abc3d_data.available_texts: + if text_id == t.text_id: + return t + return None + +def transfer_text_object_to_text_properties(text_object, text_properties, id_from_text_properties=True): print("TRANSFER:: BEGIN") print(f" {text_properties['text_id']=}") - print(f" {type(o)=}") + print(f" {type(text_object)=}") + possible_brother_text_id = text_object[get_key("text_id")] if get_key("text_id") in text_object else "" for key in text_object_keys: if key in ignore_keys_in_text_object_comparison: continue object_key = get_key(key) if id_from_text_properties and key == "text_id": - o[object_key] = text_properties["text_id"] + text_object[object_key] = text_properties["text_id"] print(f" {object_key} <= {key}") else: - text_object_property = o[object_key] if object_key in o else False + text_object_property = text_object[object_key] if object_key in text_object else False if text_object_property is not False: print(f" {object_key} => {key}") text_properties[key] = text_object_property print(f" {dict(text_properties)=}") print(f" {text_properties['offset']=}") - unfortunate_children = o.children - def kill_children(): - completely_delete_objects(unfortunate_children) - run_in_main_thread(kill_children) - # found_glyphs_with_indices = [] - # for glyph_object in o.children: - # if is_glyph_object(glyph_object): - # if "glyph_index" in glyph_object: - # found_glyphs_with_indices.append(glyph_object) + if len(text_object.children) == 0: + print("could be duplicate?") + if possible_brother_text_id != text_properties["text_id"] and possible_brother_text_id != "": + pass + + found_reconstructable_glyphs = False + glyph_objects_with_indices = [] + required_keys = [ + "glyph_index", + "glyph_id", + "type" + ] + for glyph_object in text_object.children: + if is_glyph_object(glyph_object): + has_required_keys = True + for key in required_keys: + if get_key(key) not in glyph_object: + has_required_keys = False + if has_required_keys: + inner_node = None + glyph_id = glyph_object[get_key("glyph_id")] + for c in glyph_object.children: + if c.name.startswith(f"{glyph_id}_mesh"): + inner_node = c + if inner_node is not None: + glyph_objects_with_indices.append(glyph_object) + + glyph_objects_with_indices.sort(key=lambda g: g[get_key("glyph_index")]) + print(f" {glyph_objects_with_indices=}") + text = "" + for g in glyph_objects_with_indices: + text += g[get_key("glyph_id")] + is_good_text = False + if len(text) > 0: + if text == text_properties.text: + is_good_text = True + print(f"{text=} is a good text because it is the same") + else: + availability = Font.test_availability(text_properties.font_name, text_properties.face_name, text_properties.text) + AVAILABILITY = Font.test_availability(text_properties.font_name, text_properties.face_name, text_properties.text.swapcase()) + t_text = text_properties.text + for c in availability["missing"]: + t_text = t_text.replace(c, "") + for c in AVAILABILITY["missing"]: + t_text = t_text.replace(c, "") + if len(t_text) == len(text): + print(f"{text=} is a good text because it is the same considering what is possible") + is_good_text = True + if is_good_text: + print(" GOOD TEXT") + # for glyph_index, glyph_object in enumerate(glyph_objects_with_indices): + # print(f"{glyph_index}: {glyph_object}") + # if glyph_index == glyph_object[get_key("glyph_index")]: + # print("yeey glyph_index matches") # else: - # completely_delete_objects([glyph_object]) + # print("nooo glyph_idex macthes not") + # found_reconstructable_glyphs = True + text_properties.actual_text = text + text_properties.glyphs.clear() + prepare_text(text_properties.font_name, text_properties.face_name, text) + fail_after_all = False + for glyph_index, glyph_object in enumerate(glyph_objects_with_indices): + glyph_id = glyph_object[get_key("glyph_id")] + # glyph_tmp = Font.get_glyph(text_properties.font_name, + # text_properties.face_name, + # glyph_id) + # glyph = glyph_tmp.original + glyph_properties = text_properties.glyphs.add() + + transfer_glyph_object_to_glyph_properties(glyph_object, glyph_properties) + glyph_properties["glyph_object"] = glyph_object + glyph_properties["glyph_index"] = glyph_index + inner_node = None + for c in glyph_object.children: + if c.name.startswith(f"{glyph_id}_mesh"): + inner_node = c + print(f"found inner node {inner_node.name=} for {glyph_id=}") + if inner_node is None: + fail_after_all = True + pass + glyph_properties["glyph_object"] = glyph_object + if not fail_after_all: + found_reconstructable_glyphs = True + - # found_glyphs_with_indices.sort(key=lambda g: g["glyph_index"]) - # text = "" - # for g in found_glyphs_with_indices: - # text += g["glyph_id"] - # if len(text_properties.glyphs) == len(text): - # for g in found_glyphs_with_indices: - # i = g["glyph_index"] # gp = text_properties.glyphs[i] # if gp["glyph_id"] == g["glyph_id"] or gp["glyph_id"] == g["glyph_id"].swapcase(): # if "alternate" in g: @@ -990,6 +1066,18 @@ def transfer_text_object_to_text_properties(o, text_properties, id_from_text_pro # # if object_key in g: # # gp[key] = g[object_key] + if not found_reconstructable_glyphs: + print("KILL THE GLYPHS") + text_properties.actual_text = "" + text_properties.glyphs.clear() + unfortunate_children = text_object.children + print("KILL THE CHILDREN") + completely_delete_objects(unfortunate_children) + def kill_children(): + print("KILL THE CHILDREN") + completely_delete_objects(unfortunate_children) + run_in_main_thread(kill_children) + if "font_name" in text_properties and "face_name" in text_properties: font_name = text_properties["font_name"] face_name = text_properties["face_name"] @@ -1004,7 +1092,7 @@ def link_text_object_with_new_text_properties(text_object, scene=None): text_id = find_free_text_id() text_properties = scene.abc3d_data.available_texts.add() text_properties["text_id"] = text_id - text_object[get_key("text_id")] = text_id + # text_object[get_key("text_id")] = text_id print(f" found free {text_id=}") print(" preparing text") prepare_text(text_object[get_key("font_name")], @@ -1057,31 +1145,52 @@ def transfer_properties_to_glyph_object(text_properties, glyph_properties, glyph glyph_object[get_key("type")] = "glyph" glyph_object[get_key("text_id")] = text_properties["text_id"] +def transfer_glyph_object_to_glyph_properties(glyph_object, glyph_properties): + for key in glyph_object_keys: + if key in ignore_keys_in_glyph_object_transfer: + continue + glyph_properties[key] = glyph_object[get_key(key)] + glyph_properties["text_id"] = glyph_object[get_key("text_id")] + +import inspect def would_regenerate(text_properties): - if len(text_properties.text) != len(text_properties.glyphs): + print("REGENERATE?") + if len(text_properties.actual_text) != len(text_properties.glyphs): + print(inspect.currentframe().f_lineno) + return True + if len(text_properties.glyphs) == 0: + print(inspect.currentframe().f_lineno) return True for i, g in enumerate(text_properties.glyphs): if not hasattr(g.glyph_object, "type"): + print(inspect.currentframe().f_lineno) return True elif g.glyph_object.type != "EMPTY": + print(inspect.currentframe().f_lineno) return True # check if perhaps one glyph was deleted elif g.glyph_object is None: + print(inspect.currentframe().f_lineno) return True elif g.glyph_object.parent is None: + print(inspect.currentframe().f_lineno) return True elif g.glyph_object.parent.users_collection != g.glyph_object.users_collection: + print(inspect.currentframe().f_lineno) return True elif len(text_properties.text) > i and g.glyph_id != text_properties.text[i]: + print(inspect.currentframe().f_lineno) return True elif len(text_properties.text) > i and ( g.glyph_object[f"{utils.prefix()}_font_name"] != text_properties.font_name or g.glyph_object[f"{utils.prefix()}_face_name"] != text_properties.face_name ): + print(inspect.currentframe().f_lineno) return True + print("NOT REGENERATE") return False @@ -1123,6 +1232,9 @@ def parent_to_curve(o, c): def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4, can_regenerate=False): + # for i in range (0, 42): + # print("WATCH OUT, WE DO NOT SET THE TEXT ATM") + # return False """set_text_on_curve An earlier reset cancels the other. @@ -1160,6 +1272,8 @@ def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4, # mom.data.use_path = True regenerate = can_regenerate and would_regenerate(text_properties) + if regenerate: + print("RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRREGENERATE") # if we regenerate.... delete objects if regenerate and text_properties.get("glyphs"): @@ -1178,6 +1292,7 @@ def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4, is_command = False previous_spline_index = -1 + actual_text = "" for i, c in enumerate(text_properties.text): face = Font.fonts[text_properties.font_name].faces[text_properties.face_name] scalor = face.unit_factor * text_properties.font_size @@ -1247,6 +1362,7 @@ def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4, glyph_properties["glyph_id"] = glyph_id glyph_properties["text_id"] = text_properties.text_id glyph_properties["letter_spacing"] = 0 + actual_text += glyph_id ############### NODE SCENE MANAGEMENT @@ -1397,6 +1513,9 @@ def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4, glyph_index += 1 previous_spline_index = spline_index + if regenerate: + text_properties["actual_text"] = actual_text + return True