introduce detect_text() and friends
This commit is contained in:
parent
7a034efd1c
commit
c27cf41368
2 changed files with 423 additions and 147 deletions
378
butils.py
378
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 ""
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue