From 3ea2f0e30445dbdb967bc916c20c4fd8adfd0989 Mon Sep 17 00:00:00 2001 From: themancalledjakob Date: Sun, 25 May 2025 14:15:21 +0200 Subject: [PATCH 1/8] ignore more venvs --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 220a137..19bc9a2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # python __pycache__ venv +venv* # vim *.swo From 7a034efd1c61cb75dc91f8b044421d4f43612ac9 Mon Sep 17 00:00:00 2001 From: themancalledjakob Date: Sun, 25 May 2025 14:15:42 +0200 Subject: [PATCH 2/8] all floats --- common/spacesUnicode.txt | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/common/spacesUnicode.txt b/common/spacesUnicode.txt index da6a7c9..b7270e0 100644 --- a/common/spacesUnicode.txt +++ b/common/spacesUnicode.txt @@ -4,20 +4,20 @@ space 0020 0.25 nbspace 00A0 0.25 # ethi:wordspace 1361 # NOTE: has shape enquad 2000 0.5 -emquad 2001 1 +emquad 2001 1.0 enspace 2002 0.5 -emspace 2003 1 -threeperemspace 2004 3 -fourperemspace 2005 4 -sixperemspace 2006 6 -figurespace 2007 1 -punctuationspace 2008 1 +emspace 2003 1.0 +threeperemspace 2004 3.0 +fourperemspace 2005 4.0 +sixperemspace 2006 6.0 +figurespace 2007 1.0 +punctuationspace 2008 1.0 thinspace 2009 0.1 hairspace 200A 0.05 -zerowidthspace 200B 0 +zerowidthspace 200B 0.0 narrownobreakspace 202F 0.1 -mediummathematicalspace 205F 1 +mediummathematicalspace 205F 1.0 cntr:space 2420 0.25 -ideographicspace 3000 1 +ideographicspace 3000 1.0 # ideographichalffillspace 303F # NOTE: has shape -zerowidthnobreakspace FEFF 0 +zerowidthnobreakspace FEFF 0.0 From c27cf41368adca737208a681106c9f5443073560 Mon Sep 17 00:00:00 2001 From: themancalledjakob Date: Sun, 25 May 2025 14:16:15 +0200 Subject: [PATCH 3/8] introduce detect_text() and friends --- __init__.py | 192 +++++++++++++++++--------- butils.py | 378 ++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 423 insertions(+), 147 deletions(-) diff --git a/__init__.py b/__init__.py index 9dc72d6..d1695de 100644 --- a/__init__.py +++ b/__init__.py @@ -451,7 +451,7 @@ class ABC3D_PT_TextPlacement(bpy.types.Panel): @classmethod def poll(self, context): if ( - type(context.active_object) != type(None) + context.active_object is not None and context.active_object.type == "CURVE" ): self.can_place = True @@ -502,8 +502,8 @@ class ABC3D_PT_TextManagement(bpy.types.Panel): for c in t.text_object.children: if ( len(c.users_collection) > 0 - and not isinstance(c.get(f"{utils.prefix()}_linked_textobject"), None) - and c.get(f"{utils.prefix()}_linked_textobject") == t.text_id + and not isinstance(c.get(f"{utils.prefix()}_text_id"), None) + and c.get(f"{utils.prefix()}_text_id") == t.text_id ): remove_me = False # not sure how to solve this reliably atm, @@ -542,14 +542,14 @@ class ABC3D_PT_TextManagement(bpy.types.Panel): remove_list.append(i) for i in remove_list: - if type(abc3d_data.available_texts[i].text_object) != type(None): + if abc3d_data.available_texts[i].text_object is not None: mom = abc3d_data.available_texts[i].text_object def delif(o, p): if p in o: del o[p] - delif(mom, f"{utils.prefix()}_linked_textobject") + delif(mom, f"{utils.prefix()}_text_id") delif(mom, f"{utils.prefix()}_font_name") delif(mom, f"{utils.prefix()}_face_name") delif(mom, f"{utils.prefix()}_font_size") @@ -736,11 +736,11 @@ class ABC3D_PT_TextPropertiesPanel(bpy.types.Panel): # and bpy.context.object.select_get(): a_o = bpy.context.active_object if a_o is not None: - if f"{utils.prefix()}_linked_textobject" in a_o: - text_index = a_o[f"{utils.prefix()}_linked_textobject"] + if f"{utils.prefix()}_text_id" in a_o: + text_index = a_o[f"{utils.prefix()}_text_id"] return bpy.context.scene.abc3d_data.available_texts[text_index] - elif a_o.parent is not None and f"{utils.prefix()}_linked_textobject" in a_o.parent: - text_index = a_o.parent[f"{utils.prefix()}_linked_textobject"] + elif a_o.parent is not None and f"{utils.prefix()}_text_id" in a_o.parent: + text_index = a_o.parent[f"{utils.prefix()}_text_id"] return bpy.context.scene.abc3d_data.available_texts[text_index] else: for t in bpy.context.scene.abc3d_data.available_texts: @@ -752,9 +752,9 @@ class ABC3D_PT_TextPropertiesPanel(bpy.types.Panel): def get_active_glyph_properties(self): a_o = bpy.context.active_object if a_o is not None: - if (f"{utils.prefix()}_linked_textobject" in a_o + if (f"{utils.prefix()}_text_id" in a_o and f"{utils.prefix()}_glyph_index" in a_o): - text_index = a_o[f"{utils.prefix()}_linked_textobject"] + text_index = a_o[f"{utils.prefix()}_text_id"] glyph_index = a_o[f"{utils.prefix()}_glyph_index"] return bpy.context.scene.abc3d_data.available_texts[text_index].glyphs[glyph_index] else: @@ -795,13 +795,13 @@ class ABC3D_PT_TextPropertiesPanel(bpy.types.Panel): @classmethod def poll(self, context): - return self.get_active_text_properties(self) is not None + try: + return self.get_active_text_properties(self) is not None + except IndexError: + return False def draw(self, context): layout = self.layout - wm = context.window_manager - scene = context.scene - abc3d_data = scene.abc3d_data props = self.get_active_text_properties() glyph_props = self.get_active_glyph_properties() @@ -1190,6 +1190,11 @@ class ABC3D_OT_RemoveText(bpy.types.Operator): description="Remove both ABC3D text functionality and the objects/meshes", default=True, ) + remove_custom_properties: bpy.props.BoolProperty( + name="Remove Custom Properties", + description="Remove ABC3D custom properties of objects", + default=True, + ) def invoke(self, context, event): wm = context.window_manager @@ -1206,26 +1211,27 @@ class ABC3D_OT_RemoveText(bpy.types.Operator): return {"CANCELLED"} i = abc3d_data.active_text_index - if type(abc3d_data.available_texts[i].text_object) != type(None): + if abc3d_data.available_texts[i].text_object is not None: mom = abc3d_data.available_texts[i].text_object - def delif(o, p): - if p in o: - del o[p] + if self.remove_custom_properties: + def delif(o, p): + if p in o: + del o[p] - delif(mom, f"{utils.prefix()}_type") - delif(mom, f"{utils.prefix()}_linked_textobject") - delif(mom, f"{utils.prefix()}_font_name") - delif(mom, f"{utils.prefix()}_face_name") - delif(mom, f"{utils.prefix()}_font_size") - delif(mom, f"{utils.prefix()}_letter_spacing") - delif(mom, f"{utils.prefix()}_orientation") - delif(mom, f"{utils.prefix()}_translation") - delif(mom, f"{utils.prefix()}_offset") + delif(mom, f"{utils.prefix()}_type") + delif(mom, f"{utils.prefix()}_text_id") + delif(mom, f"{utils.prefix()}_font_name") + delif(mom, f"{utils.prefix()}_face_name") + delif(mom, f"{utils.prefix()}_font_size") + delif(mom, f"{utils.prefix()}_letter_spacing") + delif(mom, f"{utils.prefix()}_orientation") + delif(mom, f"{utils.prefix()}_translation") + delif(mom, f"{utils.prefix()}_offset") if self.remove_objects: remove_list = [] for g in abc3d_data.available_texts[i].glyphs: - if type(g) != type(None): + if g is not None: remove_list.append(g.glyph_object) butils.simply_delete_objects(remove_list) @@ -1787,18 +1793,61 @@ def compare_text_object_with_object(t, o, strict=False): def detect_text(): + lock_depsgraph_updates(auto_unlock_s=-1) + print("DETECT TEXT:: begin") scene = bpy.context.scene abc3d_data = scene.abc3d_data - for o in scene.objects: - if o[f"{utils.prefix()}_type"] == "textobject": - linked_textobject = int(o[f"{utils.prefix()}_linked_textobject"]) + required_keys = [ + "type", + "text_id", + "font_name", + "face_name", + "text", + ] + objects = scene.objects + for o in objects: + print(f" {o.name=}") + valid = True + for key in required_keys: + if butils.get_key(key) not in o: + # print(f" key {butils.get_key(key)} not there") + valid = False + break + if not valid: + continue + print(" object may be valid textobject") + if o[butils.get_key("type")] == "textobject": + print(" {o.name=} a textobject") + print(f" {type(o)=} {o.name=}") + current_text_id = int(o[butils.get_key("text_id")]) if ( - len(abc3d_data.available_texts) > linked_textobject - and abc3d_data.available_texts[linked_textobject].text_object == o + len(abc3d_data.available_texts) > current_text_id + and abc3d_data.available_texts[current_text_id].text_object == o ): - t = abc3d_data.available_texts[linked_textobject] - a = test_availability(o["font_name"], o["face_name"], o["text"]) - butils.transfer_blender_object_to_text_properties(o, t) + print(" {o.name=} seems fine") + pass + # t = abc3d_data.available_texts[text_id] + # a = test_availability(o[butils.get_key("font_name")], + # o[butils.get_key("face_name")], + # o[butils.get_key("text")]) + # butils.transfer_text_object_to_text_properties(o, t) + else: + butils.link_text_object_with_new_text_properties(o, scene) + # print(" {o.name=} is a duplicate") + # text_id = butils.find_free_text_id() + # t = abc3d_data.available_texts.add() + # t["text_id"] = text_id + # print(f" found free {text_id=}") + # print(" preparing text") + # butils.prepare_text(o[butils.get_key("font_name")], + # o[butils.get_key("face_name")], + # o[butils.get_key("text")]) + # print(" prepared text, transferring text object") + # t.text_object = o + # butils.transfer_text_object_to_text_properties(o, t) + # print(" {o.name=} transerred text object") + print("DETECT TEXT:: end") + unlock_depsgraph_updates() def load_used_glyphs(): @@ -1850,21 +1899,25 @@ def on_frame_changed(self, dummy): butils.set_text_on_curve(t) -depsgraph_updates_locked = False +depsgraph_updates_locked = 0 def unlock_depsgraph_updates(): global depsgraph_updates_locked - depsgraph_updates_locked = False + depsgraph_updates_locked -= 1 -def lock_depsgraph_updates(): +def lock_depsgraph_updates(auto_unlock_s=1): global depsgraph_updates_locked - depsgraph_updates_locked = True - if bpy.app.timers.is_registered(unlock_depsgraph_updates): - bpy.app.timers.unregister(unlock_depsgraph_updates) - bpy.app.timers.register(unlock_depsgraph_updates, first_interval=1) + depsgraph_updates_locked += 1 + if auto_unlock_s >= 0: + if bpy.app.timers.is_registered(unlock_depsgraph_updates): + bpy.app.timers.unregister(unlock_depsgraph_updates) + bpy.app.timers.register(unlock_depsgraph_updates, first_interval=auto_unlock_s) +def are_depsgraph_updates_locked(): + global depsgraph_updates_locked + return depsgraph_updates_locked > 0 import time @@ -1872,29 +1925,44 @@ import time @persistent def on_depsgraph_update(scene, depsgraph): global depsgraph_updates_locked - if not bpy.context.mode.startswith("EDIT") and not depsgraph_updates_locked: + print("DEPSGRAPH:: BEGIN") + if not bpy.context.mode.startswith("EDIT") and not are_depsgraph_updates_locked(): for u in depsgraph.updates: if ( - f"{utils.prefix()}_linked_textobject" in u.id.keys() - and f"{utils.prefix()}_type" in u.id.keys() - and u.id[f"{utils.prefix()}_type"] == "textobject" + butils.get_key("text_id") in u.id.keys() + and butils.get_key("type") in u.id.keys() + and u.id[butils.get_key("type")] == "textobject" ): - linked_textobject = u.id[f"{utils.prefix()}_linked_textobject"] - if ( - u.is_updated_geometry - and len(scene.abc3d_data.available_texts) > linked_textobject - ): - lock_depsgraph_updates() + print("DEPSGRAPH:: we have a textobject") + text_id = u.id[butils.get_key("text_id")] + if u.is_updated_geometry: + 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: + print(" YES") + print(" is ? text object is not u.id") + if scene.abc3d_data.available_texts[text_id].text_object != u.id: + print(" yes") + else: + print(" no") + else: + print(" NO") + print("DEPSGRAPH:: done textobject") + # lock_depsgraph_updates() - def later(): - if butils.lock_depsgraph_update_n_times <= 0: - butils.set_text_on_curve( - scene.abc3d_data.available_texts[linked_textobject] - ) - elif butils.lock_depsgraph_update_n_times <= 0: - butils.lock_depsgraph_update_n_times -= 1 + # def later(): + # if butils.lock_depsgraph_update_n_times <= 0: + # butils.set_text_on_curve( + # scene.abc3d_data.available_texts[text_id] + # ) + # elif butils.lock_depsgraph_update_n_times <= 0: + # butils.lock_depsgraph_update_n_times -= 1 - butils.run_in_main_thread(later) + # butils.run_in_main_thread(later) + if are_depsgraph_updates_locked(): + print(" L O C K E D") + print("DEPSGRAPH:: done") def register(): diff --git a/butils.py b/butils.py index 6663a1a..7c38fe5 100644 --- a/butils.py +++ b/butils.py @@ -52,6 +52,9 @@ def get_parent_collection_names(collection, parent_names): get_parent_collection_names(parent_collection, parent_names) return +def get_key(key): + return f"{utils.prefix()}_{key}" + # Ensure it's a curve object # TODO: no raising, please @@ -556,8 +559,8 @@ def update_available_fonts(): # def update_available_texts(): # abc3d_data = bpy.context.scene.abc3d_data # for o in bpy.context.scene.objects: -# if "linked_textobject" in o.keys(): -# i = o["linked_textobject"] +# if "text_id" in o.keys(): +# i = o["text_id"] # found = False # if len(abc3d_data.available_texts) > i: # if abc3d_data.available_texts[i].glyphs @@ -692,7 +695,7 @@ def is_text_object(o): return False -def is_glyph(o): +def is_glyph_object(o): if f"{utils.prefix()}_type" in o: return o[f"{utils.prefix()}_type"] == "glyph" try: @@ -706,6 +709,10 @@ def is_glyph(o): return False +def is_glyph(o): + return is_glyph_object(o) + + def update_types(): scene = bpy.context.scene abc3d_data = scene.abc3d_data @@ -777,9 +784,280 @@ def is_bezier(curve): return True -def would_regenerate(text_properties): - mom = text_properties.text_object +text_object_keys = [ + "font_name", + "face_name", + "type", + "text_id", + "font_size", + "letter_spacing", + "distribution_type", + "orientation", + "translation", + "offset", + "text", +] +glyph_object_keys = [ + "type", + "glyph_index", + "glyph_id", + "text_id", + "font_name", + "face_name", + "font_size", + "letter_spacing", + "alternate", +] + +ignore_keys_in_text_object_comparison = [ + "type", +] + +ignore_keys_in_glyph_object_comparison = [ + "type", + "glyph_index", + "font_name", + "face_name", + "text_id", +] + +ignore_keys_in_glyph_object_transfer = [ + "type", + "text_id", + "glyph_index", +] + +keys_trigger_regeneration = [ + "font_name", + "face_name", +] + +COMPARE_TEXT_OBJECT_SAME = 0 +COMPARE_TEXT_OBJECT_DIFFER = 1 +COMPARE_TEXT_OBJECT_REGENERATE = 2 + +def find_free_text_id(): + scene = bpy.context.scene + abc3d_data = scene.abc3d_data + text_id = 0 + found_free = False + while not found_free: + occupied = False + for t in abc3d_data.available_texts: + if text_id == t.text_id: + occupied = True + if occupied: + text_id += 1 + else: + found_free = True + return text_id + +def compare_text_properties_to_text_object(text_properties, o): + for key in text_object_keys: + if key in ignore_keys_in_text_object_comparison: + continue + object_key = get_key(key) + text_property = text_properties[key] if key in text_properties else getattr(text_properties, key) + text_object_property = o[object_key] if object_key in o else False + if text_property != text_object_property: + if key in keys_trigger_regeneration: + print(f"{key}: REGENERATE {text_property=} {text_object_property}") + return COMPARE_TEXT_OBJECT_REGENERATE + elif key in ["translation", "orientation"]: + if ( + text_property[0] != text_object_property[0] or + text_property[1] != text_object_property[1] or + text_property[2] != text_object_property[2]): + print(f"{key}: DIFFER {text_property=} {text_object_property}") + return COMPARE_TEXT_OBJECT_DIFFER + else: + print(f"{key}: SAME {text_property.xyz=} {text_object_property.to_list()}") + else: + print(f"{key}: DIFFER {text_property=} {text_object_property}") + return COMPARE_TEXT_OBJECT_DIFFER + else: + print(f"{key}: SAME {text_property=} {text_object_property}") + return COMPARE_TEXT_OBJECT_SAME + + +def transfer_text_properties_to_text_object(text_properties, o): + for key in text_object_keys: + if key in ignore_keys_in_text_object_comparison: + continue + object_key = get_key(key) + text_property = text_properties[key] if key in text_properties else getattr(text_properties, key) + o[object_key] = text_property + o[get_key("type")] = "textobject" + + +def get_glyph(glyph_id, font_name, face_name, notify_on_replacement=False): + glyph_tmp = Font.get_glyph(font_name, + face_name, + glyph_id, + -1) + if glyph_tmp is None: + space_width = Font.is_space(glyph_id) + if space_width: + return space_width + + message = f"Glyph not found for font_name='{text_properties.font_name}' face_name='{text_properties.face_name}' glyph_id='{glyph_id}'" + replaced = False + if glyph_id.isalpha(): + possible_replacement = glyph_id.swapcase() + glyph_tmp = Font.get_glyph( + text_properties.font_name, + text_properties.face_name, + possible_replacement, + -1 + ) + if glyph_tmp is not None: + message = message + f" (replaced with '{possible_replacement}')" + replaced = True + + if notify_on_replacement: + ShowMessageBox( + title="Glyph replaced" if replaced else "Glyph missing", + icon="INFO" if replaced else "ERROR", + message=message, + prevent_repeat=True, + ) + if not replaced: + return None + + return glyph_tmp.original + +def transfer_text_object_to_text_properties(o, text_properties, id_from_text_properties=True): + print("TRANSFER:: BEGIN") + print(f" {text_properties['text_id']=}") + print(f" {type(o)=}") + 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"] + print(f" {object_key} <= {key}") + else: + text_object_property = o[object_key] if object_key in o 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) + # else: + # completely_delete_objects([glyph_object]) + + # 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: + # gp["alternate"] = g["alternate"] + # for key in glyph_object_keys: + # if key in ignore_keys_in_glyph_object_comparison: + # continue + # object_key = get_key(key) + # if object_key in g: + # gp[key] = g[object_key] + # else: + # text_properties.glyphs.clear() + # # for g in found_glyphs_with_indices: + # # i = g["glyph_index"] + # # gp = text_properties.glyphs.add() + # # if gp["glyph_id"] == g["glyph_id"] or gp["glyph_id"] == g["glyph_id"].swapcase(): + # # if "alternate" in g: + # # gp["alternate"] = g["alternate"] + # # for key in glyph_object_keys: + # # if key in ignore_keys_in_glyph_object_comparison: + # # continue + # # object_key = get_key(key) + # # if object_key in g: + # # gp[key] = g[object_key] + + if "font_name" in text_properties and "face_name" in text_properties: + font_name = text_properties["font_name"] + face_name = text_properties["face_name"] + text_properties.font = f"{font_name} {face_name}" + + print("TRANSFER:: END") + + +def link_text_object_with_new_text_properties(text_object, scene=None): + if scene is None: + scene = bpy.context.scene + 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 + print(f" found free {text_id=}") + print(" preparing text") + prepare_text(text_object[get_key("font_name")], + text_object[get_key("face_name")], + text_object[get_key("text")]) + print(" prepared text, transferring text object") + text_properties.text_object = text_object + transfer_text_object_to_text_properties(text_object, text_properties) + + +def test_finding(): + scene = bpy.context.scene + abc3d_data = scene.abc3d_data + text_id = find_free_text_id() + t = abc3d_data.available_texts.add() + print(type(t)) + t["text_id"] = text_id + print(t["text_id"]) + o = bpy.context.active_object + transfer_text_object_to_text_properties(o, t) + +# def detect_texts(): + # scene = bpy.context.scene + # abc3d_data = scene.abc3d_data + # for o in bpy.data.objects: + # if get_key("type") in o \ + # and o[get_key("type") == "textobject" \ + # and o[get_key("t + + + +def link_text_object_and_text_properties(o, text_properties): + text_id = text_properties.text_id + o["text_id"] = text_id + text_properties.textobject = o + +def get_glyph_object_property(text_properties, glyph_properties, key): + if key in glyph_properties: + return glyph_properties[key] + if hasattr(glyph_properties, key): + return getattr(glyph_properties, key) + return text_properties[key] if key in text_properties else getattr(text_properties, key) + +def transfer_properties_to_glyph_object(text_properties, glyph_properties, glyph_object): + for key in glyph_object_keys: + if key in ignore_keys_in_glyph_object_transfer: + continue + object_key = get_key(key) + glyph_object[object_key] = get_glyph_object_property(text_properties, glyph_properties, key) + glyph_object[get_key("type")] = "glyph" + glyph_object[get_key("text_id")] = text_properties["text_id"] + +def would_regenerate(text_properties): if len(text_properties.text) != len(text_properties.glyphs): return True @@ -789,9 +1067,9 @@ def would_regenerate(text_properties): elif g.glyph_object.type != "EMPTY": return True # check if perhaps one glyph was deleted - elif type(g.glyph_object) == type(None): + elif g.glyph_object is None: return True - elif type(g.glyph_object.parent) == type(None): + elif g.glyph_object.parent is None: return True elif g.glyph_object.parent.users_collection != g.glyph_object.users_collection: return True @@ -811,7 +1089,7 @@ def update_matrices(obj): if obj.parent is None: obj.matrix_world = obj.matrix_basis - else: + # else: obj.matrix_world = obj.parent.matrix_world * \ obj.matrix_parent_inverse * \ obj.matrix_basis @@ -843,6 +1121,7 @@ def parent_to_curve(o, c): p = cm.vertices[0].co o.matrix_parent_inverse.translation = p * -1.0 + def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4, can_regenerate=False): """set_text_on_curve @@ -866,6 +1145,7 @@ def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4, return False distribution_type = "CALCULATE" if is_bezier(mom) else "FOLLOW_PATH" + # NOTE: following not necessary anymore # as we fixed data_path with parent_to_curve trick @@ -889,14 +1169,7 @@ def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4, completely_delete_objects(glyph_objects, True) text_properties.glyphs.clear() - mom[f"{utils.prefix()}_type"] = "textobject" - mom[f"{utils.prefix()}_linked_textobject"] = text_properties.text_id - mom[f"{utils.prefix()}_font_name"] = text_properties.font_name - mom[f"{utils.prefix()}_face_name"] = text_properties.face_name - mom[f"{utils.prefix()}_font_size"] = text_properties.font_size - mom[f"{utils.prefix()}_letter_spacing"] = text_properties.letter_spacing - mom[f"{utils.prefix()}_orientation"] = text_properties.orientation - mom[f"{utils.prefix()}_translation"] = text_properties.translation + transfer_text_properties_to_text_object(text_properties, mom) curve_length = get_curve_length(mom) advance = text_properties.offset @@ -982,16 +1255,11 @@ def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4, if regenerate: outer_node = bpy.data.objects.new(f"{glyph_id}", None) inner_node = bpy.data.objects.new(f"{glyph_id}_mesh", glyph.data) - outer_node[f"{utils.prefix()}_type"] = "glyph" - outer_node[f"{utils.prefix()}_linked_textobject"] = text_properties.text_id - outer_node[f"{utils.prefix()}_glyph_index"] = glyph_index - outer_node[f"{utils.prefix()}_font_name"] = text_properties.font_name - outer_node[f"{utils.prefix()}_face_name"] = text_properties.face_name + transfer_properties_to_glyph_object(text_properties, glyph_properties, outer_node) # Add into the scene. mom.users_collection[0].objects.link(outer_node) mom.users_collection[0].objects.link(inner_node) - # bpy.context.scene.collection.objects.link(inner_node) # Parenting is hard. inner_node.parent_type = 'OBJECT' @@ -1001,6 +1269,7 @@ def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4, outer_node.hide_set(True) glyph_properties["glyph_object"] = outer_node + outer_node[f"{utils.prefix()}_glyph_index"] = glyph_index else: outer_node = glyph_properties.glyph_object outer_node[f"{utils.prefix()}_glyph_index"] = glyph_index @@ -1128,52 +1397,12 @@ def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4, glyph_index += 1 previous_spline_index = spline_index - # NOTE: depsgraph update not locked - # as we fixed data_path with parent_to_curve trick - # if lock_depsgraph_update_n_times < 0: - # lock_depsgraph_update_n_times = len( - # bpy.context.selected_objects - # ) - # else: - # lock_depsgraph_update_n_times += len( - # bpy.context.selected_objects - # ) - # # NOTE: we reset with a timeout, as setting and resetting certain things - # # in fast succession will cause visual glitches (e.g. {}.data.use_path). - # def reset(): - # mom.data.use_path = previous_use_path - # if counted_reset in bpy.app.handlers.depsgraph_update_post: - # bpy.app.handlers.depsgraph_update_post.remove(counted_reset) - # if bpy.app.timers.is_registered(reset): - # bpy.app.timers.unregister(reset) - # molotov = reset_depsgraph_n + 0 - # def counted_reset(scene, depsgraph): - # nonlocal molotov - # if molotov == 0: - # reset() - # else: - # molotov -= 1 - # # unregister previous resets to avoid multiple execution - # if bpy.app.timers.is_registered(reset): - # bpy.app.timers.unregister(reset) - # if counted_reset in bpy.app.handlers.depsgraph_update_post: - # bpy.app.handlers.depsgraph_update_post.remove(counted_reset) - # if not isinstance(reset_timeout_s, bool): - # if reset_timeout_s > 0: - # bpy.app.timers.register(reset, first_interval=reset_timeout_s) - # elif reset_timeout <= 0: - # reset() - # bpy.app.handlers.depsgraph_update_post.append(counted_reset) - - # endtime = time.perf_counter_ns() - # elapsedtime = endtime - starttime - return True verification_object = { f"{utils.prefix()}_type": "textobject", - f"{utils.prefix()}_linked_textobject": 0, + f"{utils.prefix()}_text_id": 0, f"{utils.prefix()}_font_name": "font_name", f"{utils.prefix()}_face_name": "face_name", f"{utils.prefix()}_font_size": 42, @@ -1187,28 +1416,6 @@ def verify_text_object(o): pass -def transfer_text_properties_to_text_object(text_properties, o): - o[f"{utils.prefix()}_linked_textobject"] = text_properties.text_id - o[f"{utils.prefix()}_font_name"] = text_properties.font_name - o[f"{utils.prefix()}_face_name"] = text_properties.face_name - o[f"{utils.prefix()}_font_size"] = text_properties.font_size - o[f"{utils.prefix()}_letter_spacing"] = text_properties.letter_spacing - o[f"{utils.prefix()}_orientation"] = text_properties.orientation - o[f"{utils.prefix()}_translation"] = text_properties.translation - o[f"{utils.prefix()}_text"] = text_properties["text"] - - -def transfer_text_object_to_text_properties(o, text_properties): - text_properties["text_id"] = o[f"{utils.prefix()}_linked_textobject"] - text_properties["font_name"] = o[f"{utils.prefix()}_font_name"] - text_properties["face_name"] = o[f"{utils.prefix()}_face_name"] - text_properties["font_size"] = o[f"{utils.prefix()}_font_size"] - text_properties["letter_spacing"] = o[f"{utils.prefix()}_letter_spacing"] - text_properties["orientation"] = o[f"{utils.prefix()}_orientation"] - text_properties["translation"] = o[f"{utils.prefix()}_translation"] - text_properties["text"] = o[f"{utils.prefix()}_text"] - - # blender bound_box vertices # # 3------7. @@ -1686,3 +1893,4 @@ def align_origins_to_active_object(objects=None, axis=2): # c.location -= mathutils.Vector((diff, 0.0, 0.0)) @ o.matrix_world.inverted() # return "" + From 10e57dd46a93a70990c2600e2c6572152084ba8e Mon Sep 17 00:00:00 2001 From: themancalledjakob Date: Sun, 25 May 2025 15:35:52 +0200 Subject: [PATCH 4/8] depsgraph detect texts implementing in depsgraph allows for duplication --- __init__.py | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/__init__.py b/__init__.py index d1695de..34bb3fb 100644 --- a/__init__.py +++ b/__init__.py @@ -1791,6 +1791,11 @@ def compare_text_object_with_object(t, o, strict=False): # if return True +def link_text_object_with_new_text_properties(text_object, scene = None): + lock_depsgraph_updates(auto_unlock_s=-1) + butils.link_text_object_with_new_text_properties(text_object, scene) + unlock_depsgraph_updates() + def detect_text(): lock_depsgraph_updates(auto_unlock_s=-1) @@ -1921,10 +1926,8 @@ def are_depsgraph_updates_locked(): import time - @persistent def on_depsgraph_update(scene, depsgraph): - global depsgraph_updates_locked print("DEPSGRAPH:: BEGIN") if not bpy.context.mode.startswith("EDIT") and not are_depsgraph_updates_locked(): for u in depsgraph.updates: @@ -1940,26 +1943,21 @@ def on_depsgraph_update(scene, depsgraph): 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] print(" YES") - print(" is ? text object is not u.id") - if scene.abc3d_data.available_texts[text_id].text_object != u.id: - print(" yes") + print(f" is ? {text_properties.text_object.name=} is {u.id.name=}") + if text_properties.text_object == u.id.original: + print(" yes by id") else: - print(" no") + # must be duplicate + link_text_object_with_new_text_properties(u.id.original, scene) + print(" no by id") else: + # must be new thing + print(" NO, LINK TO NEW TEXTOBJECT") + link_text_object_with_new_text_properties(u.id.original, scene) print(" NO") print("DEPSGRAPH:: done textobject") - # lock_depsgraph_updates() - - # def later(): - # if butils.lock_depsgraph_update_n_times <= 0: - # butils.set_text_on_curve( - # scene.abc3d_data.available_texts[text_id] - # ) - # elif butils.lock_depsgraph_update_n_times <= 0: - # butils.lock_depsgraph_update_n_times -= 1 - - # butils.run_in_main_thread(later) if are_depsgraph_updates_locked(): print(" L O C K E D") print("DEPSGRAPH:: done") @@ -1993,6 +1991,7 @@ def register(): butils.run_in_main_thread(butils.update_available_fonts) butils.run_in_main_thread(load_used_glyphs) butils.run_in_main_thread(butils.update_types) + butils.run_in_main_thread(detect_text) Font.init() From 88cfaf3be7a09030f3e016e887ee2a2977a13efb Mon Sep 17 00:00:00 2001 From: themancalledjakob Date: Sun, 25 May 2025 20:36:46 +0200 Subject: [PATCH 5/8] 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 From 2ace31a246719438515b42f252833826fbed86ac Mon Sep 17 00:00:00 2001 From: themancalledjakob Date: Sun, 25 May 2025 20:41:00 +0200 Subject: [PATCH 6/8] use get_text_properties instead of id as index --- __init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/__init__.py b/__init__.py index b2bd911..2d8ac95 100644 --- a/__init__.py +++ b/__init__.py @@ -1829,9 +1829,10 @@ def detect_text(): print(" {o.name=} a textobject") print(f" {type(o)=} {o.name=}") current_text_id = int(o[butils.get_key("text_id")]) + text_properties = butils.get_text_properties(current_text_id) if ( - len(abc3d_data.available_texts) > current_text_id - and abc3d_data.available_texts[current_text_id].text_object == o + text_properties is not None + and text_properties.text_object == o ): print(" {o.name=} seems fine") pass From 8f3d58aad0c068ca0f546927c8d9694f1601a4aa Mon Sep 17 00:00:00 2001 From: themancalledjakob Date: Sun, 25 May 2025 22:00:54 +0200 Subject: [PATCH 7/8] transfer glyph transforms on duplication --- __init__.py | 24 +++++++++++++----------- butils.py | 44 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 54 insertions(+), 14 deletions(-) diff --git a/__init__.py b/__init__.py index 2d8ac95..358b99b 100644 --- a/__init__.py +++ b/__init__.py @@ -284,17 +284,19 @@ class ABC3D_data(bpy.types.PropertyGroup): def active_text_index_update(self, context): if self.active_text_index != -1: - o = self.available_texts[self.active_text_index].text_object - # active_text_index changed. so let's update the selection - # check if it is already selected - # or perhaps one of the glyphs - if ( - not o.select_get() - and not len([c for c in o.children if c.select_get()]) > 0 - ): - bpy.ops.object.select_all(action="DESELECT") - o.select_set(True) - bpy.context.view_layer.objects.active = o + text_properties = butils.get_text_properties(self.active_text_index, context.scene) + if text_properties is not None: + o = text_properties.text_object + # active_text_index changed. so let's update the selection + # check if it is already selected + # or perhaps one of the glyphs + if (o is not None + and not o.select_get() + and not len([c for c in o.children if c.select_get()]) > 0 + ): + bpy.ops.object.select_all(action="DESELECT") + o.select_set(True) + context.view_layer.objects.active = o # else: # print("already selected") diff --git a/butils.py b/butils.py index 57dea5b..b74a618 100644 --- a/butils.py +++ b/butils.py @@ -772,6 +772,15 @@ def prepare_text(font_name, face_name, text, allow_replacement=True): load_font_from_filepath(filepath, loadable, font_name, face_name) return True +def predict_actual_text(text_properties): + 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, "") + return t_text def is_bezier(curve): if curve.type != "CURVE": @@ -936,6 +945,25 @@ def get_text_properties(text_id, scene = None): return t return None +def duplicate(obj, data=True, actions=True, add_to_collection=True, collection=None, recursive=True): + obj_copy = obj.copy() + if add_to_collection: + if collection: + collection.objects.link(obj_copy) + elif len(obj.users_collection) > 0: + obj.users_collection[0].objects.link(obj_copy) + if data and obj.data: + obj_copy.data = obj.data.copy() + if actions and obj.animation_data: + obj_copy.animation_data.action = obj.animation_data.action.copy() + if recursive and hasattr(obj, "children"): + for child in obj.children: + child_copy = duplicate(child) + child_copy.parent_type = child.parent_type + child_copy.parent = obj_copy + # child_copy.matrix_parent_inverse = obj_copy.matrix_world.inverted() + return obj_copy + 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']=}") @@ -957,9 +985,18 @@ def transfer_text_object_to_text_properties(text_object, text_properties, id_fro print(f" {text_properties['offset']=}") if len(text_object.children) == 0: - print("could be duplicate?") + print("ccccccccccccccccccccccccccccccccc ccccc ccccc c c c c ccould be duplicate?") if possible_brother_text_id != text_properties["text_id"] and possible_brother_text_id != "": - pass + possible_brother_properties = get_text_properties(possible_brother_text_id) + possible_brother_object = possible_brother_properties.text_object + if possible_brother_object is not None: + for child in possible_brother_object.children: + if is_glyph_object(child): + child_copy = duplicate(child) + child_copy.parent_type = child.parent_type + child_copy.parent = text_object + parent_to_curve(child_copy, text_object) + # child_copy.matrix_parent_inverse = text_object.matrix_world.inverted() found_reconstructable_glyphs = False glyph_objects_with_indices = [] @@ -1155,7 +1192,8 @@ def transfer_glyph_object_to_glyph_properties(glyph_object, glyph_properties): import inspect def would_regenerate(text_properties): print("REGENERATE?") - if len(text_properties.actual_text) != len(text_properties.glyphs): + predicted_text = predict_actual_text(text_properties) + if text_properties.actual_text != predicted_text: print(inspect.currentframe().f_lineno) return True if len(text_properties.glyphs) == 0: From e14251523bfe4f88ae5e9b76a03871de7e388022 Mon Sep 17 00:00:00 2001 From: themancalledjakob Date: Mon, 26 May 2025 06:55:29 +0200 Subject: [PATCH 8/8] cleanup prints --- __init__.py | 46 +++-------------------------- butils.py | 84 ++--------------------------------------------------- 2 files changed, 6 insertions(+), 124 deletions(-) diff --git a/__init__.py b/__init__.py index 358b99b..be82bb0 100644 --- a/__init__.py +++ b/__init__.py @@ -1805,7 +1805,6 @@ def link_text_object_with_new_text_properties(text_object, scene = None): def detect_text(): lock_depsgraph_updates(auto_unlock_s=-1) - print("DETECT TEXT:: begin") scene = bpy.context.scene abc3d_data = scene.abc3d_data required_keys = [ @@ -1817,48 +1816,24 @@ def detect_text(): ] objects = scene.objects for o in objects: - print(f" {o.name=}") valid = True for key in required_keys: if butils.get_key(key) not in o: - # print(f" key {butils.get_key(key)} not there") valid = False break if not valid: continue - print(" object may be valid textobject") if o[butils.get_key("type")] == "textobject": - print(" {o.name=} a textobject") - print(f" {type(o)=} {o.name=}") current_text_id = int(o[butils.get_key("text_id")]) text_properties = butils.get_text_properties(current_text_id) if ( text_properties is not None and text_properties.text_object == o ): - print(" {o.name=} seems fine") + # all good pass - # t = abc3d_data.available_texts[text_id] - # a = test_availability(o[butils.get_key("font_name")], - # o[butils.get_key("face_name")], - # o[butils.get_key("text")]) - # butils.transfer_text_object_to_text_properties(o, t) else: butils.link_text_object_with_new_text_properties(o, scene) - # print(" {o.name=} is a duplicate") - # text_id = butils.find_free_text_id() - # t = abc3d_data.available_texts.add() - # t["text_id"] = text_id - # print(f" found free {text_id=}") - # print(" preparing text") - # butils.prepare_text(o[butils.get_key("font_name")], - # o[butils.get_key("face_name")], - # o[butils.get_key("text")]) - # print(" prepared text, transferring text object") - # t.text_object = o - # butils.transfer_text_object_to_text_properties(o, t) - # print(" {o.name=} transerred text object") - print("DETECT TEXT:: end") unlock_depsgraph_updates() @@ -1935,7 +1910,6 @@ import time @persistent def on_depsgraph_update(scene, depsgraph): - print("DEPSGRAPH:: BEGIN") if not bpy.context.mode.startswith("EDIT") and not are_depsgraph_updates_locked(): for u in depsgraph.updates: if ( @@ -1943,31 +1917,19 @@ def on_depsgraph_update(scene, depsgraph): and butils.get_key("type") in u.id.keys() and u.id[butils.get_key("type")] == "textobject" ): - print("DEPSGRAPH:: we have a textobject") text_id = u.id[butils.get_key("text_id")] - if u.is_updated_geometry: - print(" updated geometry is true") - print(f" is {len(scene.abc3d_data.available_texts)} bigger than {text_id=} is true?") - # butils.detect_texts() + # if u.is_updated_geometry: 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: - print(" yes by id") + # nothing to do + pass else: # must be duplicate link_text_object_with_new_text_properties(u.id.original, scene) - print(" no by id") else: # must be new thing - print(" NO, LINK TO NEW TEXTOBJECT") link_text_object_with_new_text_properties(u.id.original, scene) - print(" NO") - print("DEPSGRAPH:: done textobject") - if are_depsgraph_updates_locked(): - print(" L O C K E D") - print("DEPSGRAPH:: done") def register(): diff --git a/butils.py b/butils.py index b74a618..138ed9f 100644 --- a/butils.py +++ b/butils.py @@ -871,22 +871,17 @@ def compare_text_properties_to_text_object(text_properties, o): text_object_property = o[object_key] if object_key in o else False if text_property != text_object_property: if key in keys_trigger_regeneration: - print(f"{key}: REGENERATE {text_property=} {text_object_property}") return COMPARE_TEXT_OBJECT_REGENERATE elif key in ["translation", "orientation"]: if ( text_property[0] != text_object_property[0] or text_property[1] != text_object_property[1] or text_property[2] != text_object_property[2]): - print(f"{key}: DIFFER {text_property=} {text_object_property}") return COMPARE_TEXT_OBJECT_DIFFER - else: - print(f"{key}: SAME {text_property.xyz=} {text_object_property.to_list()}") + # else same else: - print(f"{key}: DIFFER {text_property=} {text_object_property}") return COMPARE_TEXT_OBJECT_DIFFER - else: - print(f"{key}: SAME {text_property=} {text_object_property}") + # else same return COMPARE_TEXT_OBJECT_SAME @@ -965,9 +960,6 @@ def duplicate(obj, data=True, actions=True, add_to_collection=True, collection=N return obj_copy 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(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: @@ -975,17 +967,12 @@ def transfer_text_object_to_text_properties(text_object, text_properties, id_fro object_key = get_key(key) if id_from_text_properties and key == "text_id": text_object[object_key] = text_properties["text_id"] - print(f" {object_key} <= {key}") else: 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']=}") if len(text_object.children) == 0: - print("ccccccccccccccccccccccccccccccccc ccccc ccccc c c c c ccould be duplicate?") if possible_brother_text_id != text_properties["text_id"] and possible_brother_text_id != "": possible_brother_properties = get_text_properties(possible_brother_text_id) possible_brother_object = possible_brother_properties.text_object @@ -1021,7 +1008,6 @@ def transfer_text_object_to_text_properties(text_object, text_properties, id_fro 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")] @@ -1029,7 +1015,6 @@ def transfer_text_object_to_text_properties(text_object, text_properties, id_fro 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()) @@ -1039,17 +1024,8 @@ def transfer_text_object_to_text_properties(text_object, text_properties, id_fro 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: - # 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) @@ -1069,7 +1045,6 @@ def transfer_text_object_to_text_properties(text_object, text_properties, id_fro 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 @@ -1077,41 +1052,12 @@ def transfer_text_object_to_text_properties(text_object, text_properties, id_fro if not fail_after_all: found_reconstructable_glyphs = True - - # gp = text_properties.glyphs[i] - # if gp["glyph_id"] == g["glyph_id"] or gp["glyph_id"] == g["glyph_id"].swapcase(): - # if "alternate" in g: - # gp["alternate"] = g["alternate"] - # for key in glyph_object_keys: - # if key in ignore_keys_in_glyph_object_comparison: - # continue - # object_key = get_key(key) - # if object_key in g: - # gp[key] = g[object_key] - # else: - # text_properties.glyphs.clear() - # # for g in found_glyphs_with_indices: - # # i = g["glyph_index"] - # # gp = text_properties.glyphs.add() - # # if gp["glyph_id"] == g["glyph_id"] or gp["glyph_id"] == g["glyph_id"].swapcase(): - # # if "alternate" in g: - # # gp["alternate"] = g["alternate"] - # # for key in glyph_object_keys: - # # if key in ignore_keys_in_glyph_object_comparison: - # # continue - # # object_key = get_key(key) - # # 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) @@ -1120,8 +1066,6 @@ def transfer_text_object_to_text_properties(text_object, text_properties, id_fro face_name = text_properties["face_name"] text_properties.font = f"{font_name} {face_name}" - print("TRANSFER:: END") - def link_text_object_with_new_text_properties(text_object, scene=None): if scene is None: @@ -1130,12 +1074,9 @@ def link_text_object_with_new_text_properties(text_object, scene=None): text_properties = scene.abc3d_data.available_texts.add() text_properties["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")], text_object[get_key("face_name")], text_object[get_key("text")]) - print(" prepared text, transferring text object") text_properties.text_object = text_object transfer_text_object_to_text_properties(text_object, text_properties) @@ -1145,9 +1086,7 @@ def test_finding(): abc3d_data = scene.abc3d_data text_id = find_free_text_id() t = abc3d_data.available_texts.add() - print(type(t)) t["text_id"] = text_id - print(t["text_id"]) o = bpy.context.active_object transfer_text_object_to_text_properties(o, t) @@ -1189,46 +1128,34 @@ def transfer_glyph_object_to_glyph_properties(glyph_object, glyph_properties): 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): - print("REGENERATE?") predicted_text = predict_actual_text(text_properties) if text_properties.actual_text != predicted_text: - 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 @@ -1270,9 +1197,6 @@ 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. @@ -1310,13 +1234,9 @@ 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"): - for g in text_properties.glyphs: - print(dict(g)) glyph_objects = [g["glyph_object"] for g in text_properties["glyphs"]] completely_delete_objects(glyph_objects, True) text_properties.glyphs.clear()