introduce detect_text() and friends

This commit is contained in:
jrkb 2025-05-25 14:16:15 +02:00
parent 7a034efd1c
commit c27cf41368
2 changed files with 423 additions and 147 deletions

View file

@ -451,7 +451,7 @@ class ABC3D_PT_TextPlacement(bpy.types.Panel):
@classmethod @classmethod
def poll(self, context): def poll(self, context):
if ( if (
type(context.active_object) != type(None) context.active_object is not None
and context.active_object.type == "CURVE" and context.active_object.type == "CURVE"
): ):
self.can_place = True self.can_place = True
@ -502,8 +502,8 @@ class ABC3D_PT_TextManagement(bpy.types.Panel):
for c in t.text_object.children: for c in t.text_object.children:
if ( if (
len(c.users_collection) > 0 len(c.users_collection) > 0
and not isinstance(c.get(f"{utils.prefix()}_linked_textobject"), None) and not isinstance(c.get(f"{utils.prefix()}_text_id"), None)
and c.get(f"{utils.prefix()}_linked_textobject") == t.text_id and c.get(f"{utils.prefix()}_text_id") == t.text_id
): ):
remove_me = False remove_me = False
# not sure how to solve this reliably atm, # not sure how to solve this reliably atm,
@ -542,14 +542,14 @@ class ABC3D_PT_TextManagement(bpy.types.Panel):
remove_list.append(i) remove_list.append(i)
for i in remove_list: 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 mom = abc3d_data.available_texts[i].text_object
def delif(o, p): def delif(o, p):
if p in o: if p in o:
del o[p] 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()}_font_name")
delif(mom, f"{utils.prefix()}_face_name") delif(mom, f"{utils.prefix()}_face_name")
delif(mom, f"{utils.prefix()}_font_size") delif(mom, f"{utils.prefix()}_font_size")
@ -736,11 +736,11 @@ class ABC3D_PT_TextPropertiesPanel(bpy.types.Panel):
# and bpy.context.object.select_get(): # and bpy.context.object.select_get():
a_o = bpy.context.active_object a_o = bpy.context.active_object
if a_o is not None: if a_o is not None:
if f"{utils.prefix()}_linked_textobject" in a_o: if f"{utils.prefix()}_text_id" in a_o:
text_index = a_o[f"{utils.prefix()}_linked_textobject"] text_index = a_o[f"{utils.prefix()}_text_id"]
return bpy.context.scene.abc3d_data.available_texts[text_index] 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: 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()}_linked_textobject"] text_index = a_o.parent[f"{utils.prefix()}_text_id"]
return bpy.context.scene.abc3d_data.available_texts[text_index] return bpy.context.scene.abc3d_data.available_texts[text_index]
else: else:
for t in bpy.context.scene.abc3d_data.available_texts: 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): def get_active_glyph_properties(self):
a_o = bpy.context.active_object a_o = bpy.context.active_object
if a_o is not None: 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): 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"] glyph_index = a_o[f"{utils.prefix()}_glyph_index"]
return bpy.context.scene.abc3d_data.available_texts[text_index].glyphs[glyph_index] return bpy.context.scene.abc3d_data.available_texts[text_index].glyphs[glyph_index]
else: else:
@ -795,13 +795,13 @@ class ABC3D_PT_TextPropertiesPanel(bpy.types.Panel):
@classmethod @classmethod
def poll(self, context): 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): def draw(self, context):
layout = self.layout layout = self.layout
wm = context.window_manager
scene = context.scene
abc3d_data = scene.abc3d_data
props = self.get_active_text_properties() props = self.get_active_text_properties()
glyph_props = self.get_active_glyph_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", description="Remove both ABC3D text functionality and the objects/meshes",
default=True, 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): def invoke(self, context, event):
wm = context.window_manager wm = context.window_manager
@ -1206,26 +1211,27 @@ class ABC3D_OT_RemoveText(bpy.types.Operator):
return {"CANCELLED"} return {"CANCELLED"}
i = abc3d_data.active_text_index 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 mom = abc3d_data.available_texts[i].text_object
def delif(o, p): if self.remove_custom_properties:
if p in o: def delif(o, p):
del o[p] if p in o:
del o[p]
delif(mom, f"{utils.prefix()}_type") delif(mom, f"{utils.prefix()}_type")
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()}_font_name")
delif(mom, f"{utils.prefix()}_face_name") delif(mom, f"{utils.prefix()}_face_name")
delif(mom, f"{utils.prefix()}_font_size") delif(mom, f"{utils.prefix()}_font_size")
delif(mom, f"{utils.prefix()}_letter_spacing") delif(mom, f"{utils.prefix()}_letter_spacing")
delif(mom, f"{utils.prefix()}_orientation") delif(mom, f"{utils.prefix()}_orientation")
delif(mom, f"{utils.prefix()}_translation") delif(mom, f"{utils.prefix()}_translation")
delif(mom, f"{utils.prefix()}_offset") delif(mom, f"{utils.prefix()}_offset")
if self.remove_objects: if self.remove_objects:
remove_list = [] remove_list = []
for g in abc3d_data.available_texts[i].glyphs: 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) remove_list.append(g.glyph_object)
butils.simply_delete_objects(remove_list) butils.simply_delete_objects(remove_list)
@ -1787,18 +1793,61 @@ def compare_text_object_with_object(t, o, strict=False):
def detect_text(): def detect_text():
lock_depsgraph_updates(auto_unlock_s=-1)
print("DETECT TEXT:: begin")
scene = bpy.context.scene scene = bpy.context.scene
abc3d_data = scene.abc3d_data abc3d_data = scene.abc3d_data
for o in scene.objects: required_keys = [
if o[f"{utils.prefix()}_type"] == "textobject": "type",
linked_textobject = int(o[f"{utils.prefix()}_linked_textobject"]) "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 ( if (
len(abc3d_data.available_texts) > linked_textobject len(abc3d_data.available_texts) > current_text_id
and abc3d_data.available_texts[linked_textobject].text_object == o and abc3d_data.available_texts[current_text_id].text_object == o
): ):
t = abc3d_data.available_texts[linked_textobject] print(" {o.name=} seems fine")
a = test_availability(o["font_name"], o["face_name"], o["text"]) pass
butils.transfer_blender_object_to_text_properties(o, t) # 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(): def load_used_glyphs():
@ -1850,21 +1899,25 @@ def on_frame_changed(self, dummy):
butils.set_text_on_curve(t) butils.set_text_on_curve(t)
depsgraph_updates_locked = False depsgraph_updates_locked = 0
def unlock_depsgraph_updates(): def unlock_depsgraph_updates():
global depsgraph_updates_locked 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 global depsgraph_updates_locked
depsgraph_updates_locked = True depsgraph_updates_locked += 1
if bpy.app.timers.is_registered(unlock_depsgraph_updates): if auto_unlock_s >= 0:
bpy.app.timers.unregister(unlock_depsgraph_updates) if bpy.app.timers.is_registered(unlock_depsgraph_updates):
bpy.app.timers.register(unlock_depsgraph_updates, first_interval=1) 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 import time
@ -1872,29 +1925,44 @@ import time
@persistent @persistent
def on_depsgraph_update(scene, depsgraph): def on_depsgraph_update(scene, depsgraph):
global depsgraph_updates_locked 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: for u in depsgraph.updates:
if ( if (
f"{utils.prefix()}_linked_textobject" in u.id.keys() butils.get_key("text_id") in u.id.keys()
and f"{utils.prefix()}_type" in u.id.keys() and butils.get_key("type") in u.id.keys()
and u.id[f"{utils.prefix()}_type"] == "textobject" and u.id[butils.get_key("type")] == "textobject"
): ):
linked_textobject = u.id[f"{utils.prefix()}_linked_textobject"] print("DEPSGRAPH:: we have a textobject")
if ( text_id = u.id[butils.get_key("text_id")]
u.is_updated_geometry if u.is_updated_geometry:
and len(scene.abc3d_data.available_texts) > linked_textobject print(" updated geometry is true")
): print(f" is {len(scene.abc3d_data.available_texts)} bigger than {text_id=} is true?")
lock_depsgraph_updates() # 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(): # def later():
if butils.lock_depsgraph_update_n_times <= 0: # if butils.lock_depsgraph_update_n_times <= 0:
butils.set_text_on_curve( # butils.set_text_on_curve(
scene.abc3d_data.available_texts[linked_textobject] # scene.abc3d_data.available_texts[text_id]
) # )
elif butils.lock_depsgraph_update_n_times <= 0: # elif butils.lock_depsgraph_update_n_times <= 0:
butils.lock_depsgraph_update_n_times -= 1 # 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(): def register():

378
butils.py
View file

@ -52,6 +52,9 @@ def get_parent_collection_names(collection, parent_names):
get_parent_collection_names(parent_collection, parent_names) get_parent_collection_names(parent_collection, parent_names)
return return
def get_key(key):
return f"{utils.prefix()}_{key}"
# Ensure it's a curve object # Ensure it's a curve object
# TODO: no raising, please # TODO: no raising, please
@ -556,8 +559,8 @@ def update_available_fonts():
# def update_available_texts(): # def update_available_texts():
# abc3d_data = bpy.context.scene.abc3d_data # abc3d_data = bpy.context.scene.abc3d_data
# for o in bpy.context.scene.objects: # for o in bpy.context.scene.objects:
# if "linked_textobject" in o.keys(): # if "text_id" in o.keys():
# i = o["linked_textobject"] # i = o["text_id"]
# found = False # found = False
# if len(abc3d_data.available_texts) > i: # if len(abc3d_data.available_texts) > i:
# if abc3d_data.available_texts[i].glyphs # if abc3d_data.available_texts[i].glyphs
@ -692,7 +695,7 @@ def is_text_object(o):
return False return False
def is_glyph(o): def is_glyph_object(o):
if f"{utils.prefix()}_type" in o: if f"{utils.prefix()}_type" in o:
return o[f"{utils.prefix()}_type"] == "glyph" return o[f"{utils.prefix()}_type"] == "glyph"
try: try:
@ -706,6 +709,10 @@ def is_glyph(o):
return False return False
def is_glyph(o):
return is_glyph_object(o)
def update_types(): def update_types():
scene = bpy.context.scene scene = bpy.context.scene
abc3d_data = scene.abc3d_data abc3d_data = scene.abc3d_data
@ -777,9 +784,280 @@ def is_bezier(curve):
return True return True
def would_regenerate(text_properties): text_object_keys = [
mom = text_properties.text_object "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): if len(text_properties.text) != len(text_properties.glyphs):
return True return True
@ -789,9 +1067,9 @@ def would_regenerate(text_properties):
elif g.glyph_object.type != "EMPTY": elif g.glyph_object.type != "EMPTY":
return True return True
# check if perhaps one glyph was deleted # check if perhaps one glyph was deleted
elif type(g.glyph_object) == type(None): elif g.glyph_object is None:
return True return True
elif type(g.glyph_object.parent) == type(None): elif g.glyph_object.parent is None:
return True return True
elif g.glyph_object.parent.users_collection != g.glyph_object.users_collection: elif g.glyph_object.parent.users_collection != g.glyph_object.users_collection:
return True return True
@ -811,7 +1089,7 @@ def update_matrices(obj):
if obj.parent is None: if obj.parent is None:
obj.matrix_world = obj.matrix_basis obj.matrix_world = obj.matrix_basis
else: # else:
obj.matrix_world = obj.parent.matrix_world * \ obj.matrix_world = obj.parent.matrix_world * \
obj.matrix_parent_inverse * \ obj.matrix_parent_inverse * \
obj.matrix_basis obj.matrix_basis
@ -843,6 +1121,7 @@ def parent_to_curve(o, c):
p = cm.vertices[0].co p = cm.vertices[0].co
o.matrix_parent_inverse.translation = p * -1.0 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): def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4, can_regenerate=False):
"""set_text_on_curve """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 return False
distribution_type = "CALCULATE" if is_bezier(mom) else "FOLLOW_PATH" distribution_type = "CALCULATE" if is_bezier(mom) else "FOLLOW_PATH"
# NOTE: following not necessary anymore # NOTE: following not necessary anymore
# as we fixed data_path with parent_to_curve trick # 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) completely_delete_objects(glyph_objects, True)
text_properties.glyphs.clear() text_properties.glyphs.clear()
mom[f"{utils.prefix()}_type"] = "textobject" transfer_text_properties_to_text_object(text_properties, mom)
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
curve_length = get_curve_length(mom) curve_length = get_curve_length(mom)
advance = text_properties.offset 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: if regenerate:
outer_node = bpy.data.objects.new(f"{glyph_id}", None) outer_node = bpy.data.objects.new(f"{glyph_id}", None)
inner_node = bpy.data.objects.new(f"{glyph_id}_mesh", glyph.data) inner_node = bpy.data.objects.new(f"{glyph_id}_mesh", glyph.data)
outer_node[f"{utils.prefix()}_type"] = "glyph" transfer_properties_to_glyph_object(text_properties, glyph_properties, outer_node)
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
# Add into the scene. # Add into the scene.
mom.users_collection[0].objects.link(outer_node) mom.users_collection[0].objects.link(outer_node)
mom.users_collection[0].objects.link(inner_node) mom.users_collection[0].objects.link(inner_node)
# bpy.context.scene.collection.objects.link(inner_node)
# Parenting is hard. # Parenting is hard.
inner_node.parent_type = 'OBJECT' 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) outer_node.hide_set(True)
glyph_properties["glyph_object"] = outer_node glyph_properties["glyph_object"] = outer_node
outer_node[f"{utils.prefix()}_glyph_index"] = glyph_index
else: else:
outer_node = glyph_properties.glyph_object outer_node = glyph_properties.glyph_object
outer_node[f"{utils.prefix()}_glyph_index"] = glyph_index 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 glyph_index += 1
previous_spline_index = spline_index 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 return True
verification_object = { verification_object = {
f"{utils.prefix()}_type": "textobject", 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()}_font_name": "font_name",
f"{utils.prefix()}_face_name": "face_name", f"{utils.prefix()}_face_name": "face_name",
f"{utils.prefix()}_font_size": 42, f"{utils.prefix()}_font_size": 42,
@ -1187,28 +1416,6 @@ def verify_text_object(o):
pass 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 # blender bound_box vertices
# #
# 3------7. # 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() # c.location -= mathutils.Vector((diff, 0.0, 0.0)) @ o.matrix_world.inverted()
# return "" # return ""