refactor ensure glyphs + alternates
This commit is contained in:
parent
14d1b7a160
commit
7de8fcc5d1
3 changed files with 332 additions and 129 deletions
85
__init__.py
85
__init__.py
|
@ -143,13 +143,40 @@ class ABC3D_glyph_properties(bpy.types.PropertyGroup):
|
|||
t = butils.get_text_properties(self.text_id)
|
||||
if t is not None:
|
||||
butils.set_text_on_curve(t)
|
||||
return None
|
||||
|
||||
def alternate_get_callback(self):
|
||||
return self["alternate"] if "alternate" in self else 0
|
||||
|
||||
def alternate_set_callback(self, value):
|
||||
min_value = 0
|
||||
new_value = max(value, min_value)
|
||||
|
||||
if self.text_id >= 0:
|
||||
text_properties = butils.get_text_properties(self.text_id)
|
||||
max_value = (
|
||||
len(
|
||||
Font.get_glyphs(
|
||||
text_properties.font_name,
|
||||
text_properties.face_name,
|
||||
self.glyph_id,
|
||||
)
|
||||
)
|
||||
- 1
|
||||
)
|
||||
new_value = min(new_value, max_value)
|
||||
|
||||
self["alternate"] = new_value
|
||||
return None
|
||||
|
||||
glyph_id: bpy.props.StringProperty(maxlen=1)
|
||||
text_id: bpy.props.IntProperty(
|
||||
default=-1,
|
||||
)
|
||||
alternate: bpy.props.IntProperty(
|
||||
default=-1,
|
||||
default=0, # also change in alternate_get_callback
|
||||
get=alternate_get_callback,
|
||||
set=alternate_set_callback,
|
||||
update=update_callback,
|
||||
)
|
||||
glyph_object: bpy.props.PointerProperty(type=bpy.types.Object)
|
||||
|
@ -267,6 +294,7 @@ class ABC3D_data(bpy.types.PropertyGroup):
|
|||
available_texts: bpy.props.CollectionProperty(
|
||||
type=ABC3D_text_properties, name="Available texts"
|
||||
)
|
||||
texts: bpy.props.CollectionProperty(type=ABC3D_text_properties, name="texts")
|
||||
|
||||
def active_text_index_update(self, context):
|
||||
lock_depsgraph_updates()
|
||||
|
@ -713,6 +741,22 @@ class ABC3D_PT_TextPropertiesPanel(bpy.types.Panel):
|
|||
layout.label(text="props.text_object is none")
|
||||
return
|
||||
|
||||
# TODO: put this at a better place
|
||||
# here we set the font if it is not correct
|
||||
# this is a fix for a UI glitch, perhaps it could be fixed
|
||||
# rather where it is not set properly
|
||||
# if (
|
||||
# butils.get_key("font_name") in props.text_object
|
||||
# and butils.get_key("face_name") in props.text_object
|
||||
# ):
|
||||
# font = f"{props.text_object[butils.get_key('font_name')]} {props.text_object[butils.get_key('face_name')]}"
|
||||
# if font != props.font:
|
||||
#
|
||||
# def setfont():
|
||||
# props.font = font
|
||||
#
|
||||
# butils.run_in_main_thread(setfont)
|
||||
#
|
||||
layout.label(text=f"Mom: {props.text_object.name}")
|
||||
layout.row().prop(props, "font")
|
||||
layout.row().prop(props, "text")
|
||||
|
@ -729,8 +773,26 @@ class ABC3D_PT_TextPropertiesPanel(bpy.types.Panel):
|
|||
if glyph_props is None:
|
||||
return
|
||||
box = layout.box()
|
||||
box.label(text=f"{glyph_props.glyph_id}")
|
||||
box.label(text=f"selected character: {glyph_props.glyph_id}")
|
||||
box.row().prop(glyph_props, "letter_spacing")
|
||||
# if True:
|
||||
# font_name = props.font_name
|
||||
# face_name = props.face_name
|
||||
# glyph_id = glyph_props.glyph_id
|
||||
# glyphs_n = len(Font.get_glyphs(font_name, face_name, glyph_id))
|
||||
# glyph_props.alternate.hard_min = -1
|
||||
# glyph_props.alternate.hard_max = glyphs_n - 1
|
||||
n_alternates = len(
|
||||
Font.get_glyphs(
|
||||
props.font_name,
|
||||
props.face_name,
|
||||
glyph_props.glyph_id,
|
||||
)
|
||||
)
|
||||
if n_alternates > 1:
|
||||
box.row().prop(glyph_props, "alternate", text=f"alternate ({n_alternates})")
|
||||
# if glyph_props.glyph_object.preview is not None:
|
||||
# box.row().template_preview(glyph_props.glyph_object.preview.icon_id)
|
||||
|
||||
|
||||
class ABC3D_OT_RefreshAvailableFonts(bpy.types.Operator):
|
||||
|
@ -1586,9 +1648,10 @@ class ABC3D_OT_CreateFontFromObjects(bpy.types.Operator):
|
|||
def do_autodetect_names(self, name: str):
|
||||
ifxsplit = name.split("_")
|
||||
if len(ifxsplit) < 4:
|
||||
print(f"name could not be autodetected {name}")
|
||||
print("split:")
|
||||
print(ifxsplit)
|
||||
print(
|
||||
f"{utils.prefix()}::CreateFontFromObjects: name could not be autodetected {name}"
|
||||
)
|
||||
print(f"{utils.prefix()}::CreateFontFromObjects: split: {ifxsplit=}")
|
||||
return self.font_name, self.face_name
|
||||
detected_font_name = f"{ifxsplit[1]}_{ifxsplit[2]}"
|
||||
detected_face_name = ifxsplit[3]
|
||||
|
@ -1663,9 +1726,11 @@ class ABC3D_OT_CreateFontFromObjects(bpy.types.Operator):
|
|||
row.label(text=f"{k} → {Font.known_misspellings[k]}{character}")
|
||||
|
||||
def execute(self, context):
|
||||
print(f"executing {self.bl_idname}")
|
||||
print(f"{utils.prefix()}::CreateFontFromObjects: executing {self.bl_idname}")
|
||||
if len(context.selected_objects) == 0:
|
||||
print(f"cancelled {self.bl_idname} - no objects selected")
|
||||
print(
|
||||
f"{utils.prefix()}::CreateFontFromObjects: cancelled {self.bl_idname} - no objects selected"
|
||||
)
|
||||
return {"CANCELLED"}
|
||||
global shared
|
||||
scene = bpy.context.scene
|
||||
|
@ -1682,7 +1747,7 @@ class ABC3D_OT_CreateFontFromObjects(bpy.types.Operator):
|
|||
currentObjects = []
|
||||
for o in context.selected_objects:
|
||||
if o.name not in currentObjects:
|
||||
print(f"processing {o.name}")
|
||||
print(f"{utils.prefix()}::CreateFontFromObjects: processing {o.name}")
|
||||
process_object = True
|
||||
if self.autodetect_names:
|
||||
font_name, face_name = self.do_autodetect_names(o.name)
|
||||
|
@ -1719,7 +1784,9 @@ class ABC3D_OT_CreateFontFromObjects(bpy.types.Operator):
|
|||
f.face_name = face_name
|
||||
|
||||
else:
|
||||
print(f"import warning: did not understand glyph {name}")
|
||||
print(
|
||||
f"{utils.prefix()}::CreateFontFromObjects: import warning: did not understand glyph {name}"
|
||||
)
|
||||
self.report({"INFO"}, f"did not understand glyph {name}")
|
||||
|
||||
return {"FINISHED"}
|
||||
|
|
353
butils.py
353
butils.py
|
@ -663,7 +663,7 @@ def clean_fontcollection(fontcollection=None):
|
|||
fontcollection = bpy.data.collections.get("ABC3D")
|
||||
if fontcollection is None:
|
||||
print(
|
||||
f"{utils.prefix()}::clean_fontcollection: failed beacause fontcollection is none"
|
||||
f"{utils.prefix()}::clean_fontcollection: failed because fontcollection is none"
|
||||
)
|
||||
return False
|
||||
|
||||
|
@ -1011,9 +1011,11 @@ def predict_actual_text(text_properties):
|
|||
)
|
||||
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, "")
|
||||
C = c.swapcase()
|
||||
if C in AVAILABILITY.missing:
|
||||
t_text = t_text.replace(c, "")
|
||||
else:
|
||||
t_text = t_text.replace(c, C)
|
||||
return t_text
|
||||
|
||||
|
||||
|
@ -1184,6 +1186,7 @@ def get_text_properties(text_id, scene=None):
|
|||
return t
|
||||
return None
|
||||
|
||||
|
||||
def get_text_properties_by_index(text_index, scene=None):
|
||||
if scene is None:
|
||||
scene = bpy.context.scene
|
||||
|
@ -1299,10 +1302,7 @@ def transfer_text_object_to_text_properties(
|
|||
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
|
||||
glyph_properties["text_id"] = text_properties.text_id
|
||||
glyph_object["text_id"] = text_properties.text_id
|
||||
# glyph_properties["glyph_object"] = glyph_object
|
||||
inner_node = None
|
||||
for c in glyph_object.children:
|
||||
if c.name.startswith(f"{glyph_id}_mesh"):
|
||||
|
@ -1311,6 +1311,9 @@ def transfer_text_object_to_text_properties(
|
|||
fail_after_all = True
|
||||
pass
|
||||
glyph_properties["glyph_object"] = glyph_object
|
||||
glyph_properties["glyph_index"] = glyph_index
|
||||
glyph_properties["text_id"] = text_properties.text_id
|
||||
glyph_object["text_id"] = text_properties.text_id
|
||||
if not fail_after_all:
|
||||
found_reconstructable_glyphs = True
|
||||
|
||||
|
@ -1421,10 +1424,21 @@ def transfer_glyph_object_to_glyph_properties(glyph_object, glyph_properties):
|
|||
glyph_properties["text_id"] = glyph_object[get_key("text_id")]
|
||||
|
||||
|
||||
def get_text_difference_index(text_a, text_b):
|
||||
len_a = len(text_a)
|
||||
len_b = len(text_b)
|
||||
len_min = min(len_a, len_b)
|
||||
len_max = max(len_a, len_b)
|
||||
for i in range(0, len_max):
|
||||
if i >= len_min or text_a[i] != text_b[i]:
|
||||
return i
|
||||
return False
|
||||
|
||||
|
||||
def would_regenerate(text_properties):
|
||||
predicted_text = predict_actual_text(text_properties)
|
||||
if text_properties.actual_text != predicted_text:
|
||||
return True
|
||||
return get_text_difference_index(text_properties.actual_text, predicted_text)
|
||||
if len(text_properties.glyphs) == 0:
|
||||
return True
|
||||
|
||||
|
@ -1476,6 +1490,7 @@ def is_or_has_parent(o, parent, if_is_parent=True, max_depth=10):
|
|||
|
||||
|
||||
def parent_to_curve(o, c):
|
||||
# https://projects.blender.org/blender/blender/issues/100661
|
||||
o.parent_type = "OBJECT"
|
||||
o.parent = c
|
||||
# o.matrix_parent_inverse = c.matrix_world.inverted()
|
||||
|
@ -1491,6 +1506,184 @@ def parent_to_curve(o, c):
|
|||
o.matrix_parent_inverse.translation = p * -1.0
|
||||
|
||||
|
||||
def get_original_glyph(text_properties, glyph_properties):
|
||||
glyph_tmp = Font.get_glyph(
|
||||
text_properties.font_name,
|
||||
text_properties.face_name,
|
||||
glyph_properties.glyph_id,
|
||||
glyph_properties.alternate,
|
||||
)
|
||||
if glyph_tmp is None:
|
||||
return None
|
||||
return glyph_tmp.original
|
||||
|
||||
|
||||
def ensure_glyph_object(text_properties, glyph_properties):
|
||||
glyph_index = glyph_properties["glyph_index"]
|
||||
# First, let's see if there was ever a glyph object constructed
|
||||
if (
|
||||
glyph_properties.glyph_object is None
|
||||
or not isinstance(glyph_properties.glyph_object, bpy_types.Object)
|
||||
or not is_glyph_object(glyph_properties.glyph_object)
|
||||
):
|
||||
# we do need a text_object though
|
||||
# if there is not, let's give up for this iteration
|
||||
if not isinstance(text_properties.text_object, bpy_types.Object):
|
||||
print(
|
||||
f"{utils.prefix()}::ensure_glyph_object: failed! text object is not an object"
|
||||
)
|
||||
return False
|
||||
|
||||
outer_node = bpy.data.objects.new(f"{glyph_properties.glyph_id}", None)
|
||||
inner_node = bpy.data.objects.new(
|
||||
f"{glyph_properties.glyph_id}_mesh",
|
||||
get_original_glyph(text_properties, glyph_properties).data,
|
||||
)
|
||||
transfer_properties_to_glyph_object(
|
||||
text_properties, glyph_properties, outer_node
|
||||
)
|
||||
|
||||
# Add into the scene.
|
||||
text_properties.text_object.users_collection[0].objects.link(outer_node)
|
||||
text_properties.text_object.users_collection[0].objects.link(inner_node)
|
||||
|
||||
# Parenting is hard.
|
||||
inner_node.parent_type = "OBJECT"
|
||||
inner_node.parent = outer_node
|
||||
inner_node.matrix_parent_inverse = outer_node.matrix_world.inverted()
|
||||
parent_to_curve(outer_node, text_properties.text_object)
|
||||
|
||||
# outer_node["inner_node"] = bpy.types.PointerProperty(inner_node)
|
||||
# for some funny reason we cannot set 'glyph_object' by key, but need to set the attribute
|
||||
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
|
||||
|
||||
# we might just want to update the data
|
||||
# imagine a different font, letter or alternate
|
||||
# this way we keep all manual transforms
|
||||
if (
|
||||
glyph_properties.glyph_object[get_key("glyph_id")] != glyph_properties.glyph_id
|
||||
or glyph_properties.glyph_object[get_key("alternate")]
|
||||
!= glyph_properties.alternate
|
||||
or glyph_properties.glyph_object[get_key("font_name")]
|
||||
!= text_properties.font_name
|
||||
or glyph_properties.glyph_object[get_key("face_name")]
|
||||
!= text_properties.face_name
|
||||
):
|
||||
|
||||
inner_node = None
|
||||
old_font_name = glyph_properties.glyph_object[get_key("font_name")]
|
||||
old_face_name = glyph_properties.glyph_object[get_key("face_name")]
|
||||
old_face = Font.get_font_face(old_font_name, old_face_name)
|
||||
face = Font.get_font_face(text_properties.font_name, text_properties.face_name)
|
||||
ratio = old_face.unit_factor / face.unit_factor
|
||||
# try:
|
||||
# inner_node = glyph_properties["inner_node"].original
|
||||
# inner_node.location = inner_node.location * ratio
|
||||
# except KeyError:
|
||||
old_glyph_id = glyph_properties.glyph_object[get_key("glyph_id")]
|
||||
for c in glyph_properties.glyph_object.children:
|
||||
if c.name.startswith(f"{old_glyph_id}_mesh"):
|
||||
inner_node = c
|
||||
inner_node.location = inner_node.location * ratio
|
||||
inner_node.name = f"{glyph_properties.glyph_id}_mesh"
|
||||
# outer_node["inner_node"] = bpy.types.PointerProperty(inner_node)
|
||||
if inner_node is None:
|
||||
print(f"{utils.prefix()}::ensure_glyph_object: failed! no inner_node found")
|
||||
return False
|
||||
inner_node.data = get_original_glyph(text_properties, glyph_properties).data
|
||||
glyph_properties.glyph_object[get_key("glyph_id")] = glyph_properties.glyph_id
|
||||
glyph_properties.glyph_object[get_key("alternate")] = glyph_properties.alternate
|
||||
glyph_properties.glyph_object[get_key("font_name")] = text_properties.font_name
|
||||
glyph_properties.glyph_object[get_key("face_name")] = text_properties.face_name
|
||||
|
||||
glyph_properties.glyph_object.hide_set(True)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def ensure_glyphs(text_properties, predicted_text: str):
|
||||
|
||||
######### REQUIREMENTS
|
||||
|
||||
# turns out this is not a requirement
|
||||
# and can be a case we want to tackle
|
||||
#
|
||||
# if not text_properties.get("glyphs"):
|
||||
# ShowMessageBox(
|
||||
# title="text_properties has no glyphs", message="well, what I said"
|
||||
# )
|
||||
# return False
|
||||
|
||||
######### SETUP
|
||||
|
||||
n_glyphs = len(text_properties.glyphs)
|
||||
n_predicted = len(predicted_text)
|
||||
|
||||
########## ENSURE AMOUNT
|
||||
|
||||
if n_glyphs == n_predicted:
|
||||
# same amount of glyphs
|
||||
# this is the most common case
|
||||
# don't do anything
|
||||
pass
|
||||
|
||||
elif n_glyphs > n_predicted:
|
||||
# more glyphs than predicted
|
||||
# it's a shorter word, or letters were deleted
|
||||
count = n_glyphs - n_predicted
|
||||
for i in range(0, count):
|
||||
reverse_i = n_glyphs - (i + 1)
|
||||
# let's attempt to remove the glyph_object first
|
||||
# so we avoid dangling data
|
||||
if isinstance(
|
||||
text_properties.glyphs[reverse_i].glyph_object, bpy_types.Object
|
||||
):
|
||||
# bam!
|
||||
completely_delete_objects(
|
||||
[text_properties.glyphs[reverse_i].glyph_object]
|
||||
)
|
||||
# else:
|
||||
# # nothing to do, if there is no blender object
|
||||
# # possibly we could do a 'del', but we can also
|
||||
# # just comment out the whole conditional fork
|
||||
# pass
|
||||
|
||||
# now that blender data is gone, we can remove the glyph
|
||||
text_properties.glyphs.remove(reverse_i)
|
||||
|
||||
elif n_glyphs < n_predicted:
|
||||
# less glyphs than predicted
|
||||
# it's a longer word, or letters were added
|
||||
while n_glyphs < n_predicted:
|
||||
glyph_id = predicted_text[n_glyphs]
|
||||
glyph_properties = text_properties.glyphs.add()
|
||||
glyph_properties["glyph_id"] = predicted_text[n_glyphs]
|
||||
glyph_properties["glyph_index"] = n_glyphs
|
||||
glyph_properties["text_id"] = text_properties.text_id
|
||||
glyph_properties["letter_spacing"] = 0
|
||||
n_glyphs += 1
|
||||
|
||||
######### ENSURE VALUES
|
||||
|
||||
for i, glyph_properties in enumerate(text_properties.glyphs):
|
||||
glyph_properties["glyph_index"] = i
|
||||
glyph_properties["text_id"] = text_properties.text_id
|
||||
glyph_properties["glyph_id"] = predicted_text[i]
|
||||
if not ensure_glyph_object(text_properties, glyph_properties):
|
||||
print(f"{utils.prefix()}::ensure_glyphs: could not ensure glyph_object")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
# C.scene.abc3d_data.available_texts[0]
|
||||
# import abc3d
|
||||
# abc3d.butils.ensure_glyphs(C.scene.abc3d_data.available_texts[0], "whatever")
|
||||
|
||||
|
||||
def set_text_on_curve(
|
||||
text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4, can_regenerate=False
|
||||
):
|
||||
|
@ -1519,27 +1712,8 @@ def set_text_on_curve(
|
|||
|
||||
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
|
||||
#
|
||||
# use_path messes with parenting
|
||||
# however, we need it for follow_path
|
||||
# https://projects.blender.org/blender/blender/issues/100661
|
||||
# previous_use_path = mom.data.use_path
|
||||
# if distribution_type == "CALCULATE":
|
||||
# mom.data.use_path = False
|
||||
# elif distribution_type == "FOLLOW_PATH":
|
||||
# mom.data.use_path = True
|
||||
|
||||
regenerate = can_regenerate and would_regenerate(text_properties)
|
||||
|
||||
# if we regenerate.... delete objects
|
||||
if regenerate and text_properties.get("glyphs"):
|
||||
glyph_objects = [g["glyph_object"] for g in text_properties["glyphs"]]
|
||||
completely_delete_objects(glyph_objects, True)
|
||||
text_properties.glyphs.clear()
|
||||
|
||||
transfer_text_properties_to_text_object(text_properties, mom)
|
||||
predicted_text = predict_actual_text(text_properties)
|
||||
ensure_glyphs(text_properties, predicted_text)
|
||||
|
||||
curve_length = get_curve_length(mom)
|
||||
advance = text_properties.offset
|
||||
|
@ -1549,6 +1723,9 @@ def set_text_on_curve(
|
|||
previous_spline_index = -1
|
||||
|
||||
actual_text = ""
|
||||
# we need to iterate over the original text, as we want commands
|
||||
# however, ideally it could be an array of glyphs, commands and spaces
|
||||
# now we need to handle non existing characters etc everytime in the loop
|
||||
for i, c in enumerate(text_properties.text):
|
||||
face = Font.get_font_face(text_properties.font_name, text_properties.face_name)
|
||||
scalor = face.unit_factor * text_properties.font_size
|
||||
|
@ -1572,90 +1749,31 @@ def set_text_on_curve(
|
|||
|
||||
spline_index = 0
|
||||
|
||||
############### GET GLYPH
|
||||
############### HANDLE SPACES
|
||||
|
||||
glyph_tmp = Font.get_glyph(
|
||||
text_properties.font_name, text_properties.face_name, glyph_id, -1
|
||||
)
|
||||
if glyph_tmp is None:
|
||||
if glyph_id not in predicted_text:
|
||||
space_width = Font.is_space(glyph_id)
|
||||
if space_width:
|
||||
advance = advance + space_width * text_properties.font_size
|
||||
continue
|
||||
|
||||
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 can_regenerate:
|
||||
ShowMessageBox(
|
||||
title="Glyph replaced" if replaced else "Glyph missing",
|
||||
icon="INFO" if replaced else "ERROR",
|
||||
message=message,
|
||||
prevent_repeat=True,
|
||||
)
|
||||
if not replaced:
|
||||
continue
|
||||
|
||||
glyph = glyph_tmp.original
|
||||
continue
|
||||
|
||||
############### GLYPH PROPERTIES
|
||||
|
||||
glyph_properties = (
|
||||
text_properties.glyphs[glyph_index]
|
||||
if not regenerate
|
||||
else text_properties.glyphs.add()
|
||||
)
|
||||
glyph_properties = text_properties.glyphs[glyph_index]
|
||||
# ensure_glyph_object(text_properties, glyph_properties)
|
||||
|
||||
if regenerate:
|
||||
glyph_properties["glyph_id"] = glyph_id
|
||||
glyph_properties["text_id"] = text_properties.text_id
|
||||
glyph_properties["letter_spacing"] = 0
|
||||
actual_text += glyph_id
|
||||
############### ACTUAL TEXT
|
||||
|
||||
actual_text += glyph_id
|
||||
|
||||
############### NODE SCENE MANAGEMENT
|
||||
|
||||
inner_node = None
|
||||
outer_node = None
|
||||
if regenerate:
|
||||
outer_node = bpy.data.objects.new(f"{glyph_id}", None)
|
||||
inner_node = bpy.data.objects.new(f"{glyph_id}_mesh", glyph.data)
|
||||
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)
|
||||
|
||||
# Parenting is hard.
|
||||
inner_node.parent_type = "OBJECT"
|
||||
inner_node.parent = outer_node
|
||||
inner_node.matrix_parent_inverse = outer_node.matrix_world.inverted()
|
||||
parent_to_curve(outer_node, mom)
|
||||
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
|
||||
for c in outer_node.children:
|
||||
if c.name.startswith(f"{glyph_id}_mesh"):
|
||||
inner_node = c
|
||||
# outsourced to ensure_glyph_object
|
||||
|
||||
############### TRANSFORMS
|
||||
|
||||
glyph = get_original_glyph(text_properties, glyph_properties)
|
||||
|
||||
# origins could be shifted
|
||||
# so we need to apply a pre_advance
|
||||
glyph_pre_advance, glyph_post_advance = get_glyph_prepost_advances(glyph)
|
||||
|
@ -1683,14 +1801,12 @@ def set_text_on_curve(
|
|||
outer_node.constraints["Follow Path"].up_axis = "UP_Y"
|
||||
spline_index = 0
|
||||
elif distribution_type == "CALCULATE":
|
||||
previous_outer_node_rotation_mode = None
|
||||
previous_inner_node_rotation_mode = None
|
||||
if outer_node.rotation_mode != "QUATERNION":
|
||||
outer_node.rotation_mode = "QUATERNION"
|
||||
previous_outer_node_rotation_mode = outer_node.rotation_mode
|
||||
if inner_node.rotation_mode != "QUATERNION":
|
||||
inner_node.rotation_mode = "QUATERNION"
|
||||
previous_inner_node_rotation_mode = inner_node.rotation_mode
|
||||
previous_glyph_object_rotation_mode = None
|
||||
if glyph_properties.glyph_object.rotation_mode != "QUATERNION":
|
||||
previous_glyph_object_rotation_mode = (
|
||||
glyph_properties.glyph_object.rotation_mode
|
||||
)
|
||||
glyph_properties.glyph_object.rotation_mode = "QUATERNION"
|
||||
|
||||
# get info from bezier
|
||||
location, tangent, spline_index = calc_point_on_bezier_curve(
|
||||
|
@ -1702,7 +1818,9 @@ def set_text_on_curve(
|
|||
is_newline = True
|
||||
|
||||
# position
|
||||
outer_node.location = location + text_properties.translation
|
||||
glyph_properties.glyph_object.location = (
|
||||
location + text_properties.translation
|
||||
)
|
||||
|
||||
# orientation / rotation
|
||||
mask = [0]
|
||||
|
@ -1720,21 +1838,21 @@ def set_text_on_curve(
|
|||
|
||||
q = mathutils.Quaternion()
|
||||
q.rotate(text_properties.orientation)
|
||||
outer_node.rotation_quaternion = (
|
||||
glyph_properties.glyph_object.rotation_quaternion = (
|
||||
motor[0].to_3x3() @ q.to_matrix()
|
||||
).to_quaternion()
|
||||
|
||||
# # NOTE: supercool but out of scope, as we wouldhave to update it everytime the curve object rotates,
|
||||
# # but this would ignore the curve objects orientation:
|
||||
# outer_node.rotation_quaternion = (mom.matrix_world.inverted().to_3x3() @ motor[0].to_3x3() @ q.to_matrix()).to_quaternion()
|
||||
# glyph_properties.glyph_object.rotation_quaternion = (mom.matrix_world.inverted().to_3x3() @ motor[0].to_3x3() @ q.to_matrix()).to_quaternion()
|
||||
|
||||
# # scale
|
||||
outer_node.scale = (scalor, scalor, scalor)
|
||||
glyph_properties.glyph_object.scale = (scalor, scalor, scalor)
|
||||
|
||||
if previous_outer_node_rotation_mode:
|
||||
outer_node.rotation_mode = previous_outer_node_rotation_mode
|
||||
if previous_inner_node_rotation_mode:
|
||||
inner_node.rotation_mode = previous_inner_node_rotation_mode
|
||||
if previous_glyph_object_rotation_mode:
|
||||
glyph_properties.glyph_object.rotation_mode = (
|
||||
previous_glyph_object_rotation_mode
|
||||
)
|
||||
|
||||
# outer_node.hide_viewport = True
|
||||
|
||||
|
@ -1805,8 +1923,7 @@ def set_text_on_curve(
|
|||
glyph_index += 1
|
||||
previous_spline_index = spline_index
|
||||
|
||||
if regenerate:
|
||||
text_properties["actual_text"] = actual_text
|
||||
text_properties["actual_text"] = actual_text
|
||||
|
||||
return True
|
||||
|
||||
|
|
|
@ -266,7 +266,13 @@ def get_glyphs(font_name, face_name, glyph_id):
|
|||
return glyphs_for_id
|
||||
|
||||
|
||||
def get_glyph(font_name, face_name, glyph_id, alternate=0):
|
||||
def get_glyph(
|
||||
font_name: str,
|
||||
face_name: str,
|
||||
glyph_id: str,
|
||||
alternate: int = 0,
|
||||
alternate_tolerant: bool = True,
|
||||
):
|
||||
"""add_glyph adds a glyph to a FontFace
|
||||
it creates the :class:`Font` and :class:`FontFace` if it does not exist yet
|
||||
|
||||
|
@ -276,6 +282,10 @@ def get_glyph(font_name, face_name, glyph_id, alternate=0):
|
|||
:type face_name: str
|
||||
:param glyph_id: The ``glyph_id`` from the glyph you want
|
||||
:type glyph_id: str
|
||||
:param alternate: The ``alternate`` from the glyph you want
|
||||
:type alternate: int
|
||||
:param alternate_tolerant: Fetch an existing alternate if requested is out of bounds
|
||||
:type glyph_id: bool
|
||||
...
|
||||
:return: returns the glyph object, or ``None`` if it does not exist
|
||||
:rtype: `Object`
|
||||
|
@ -283,12 +293,21 @@ def get_glyph(font_name, face_name, glyph_id, alternate=0):
|
|||
|
||||
glyphs = get_glyphs(font_name, face_name, glyph_id)
|
||||
|
||||
if len(glyphs) <= alternate or len(glyphs) == 0:
|
||||
if len(glyphs) == 0:
|
||||
print(
|
||||
f"ABC3D::get_glyph: font({font_name}) face({face_name}) glyph({glyph_id})[{alternate}] not found"
|
||||
)
|
||||
return None
|
||||
|
||||
if len(glyphs) <= alternate:
|
||||
if alternate_tolerant:
|
||||
alternate = 0
|
||||
else:
|
||||
print(
|
||||
f"ABC3D::get_glyph: font({font_name}) face({face_name}) glyph({glyph_id})[{alternate}] not found"
|
||||
)
|
||||
return None
|
||||
|
||||
return glyphs[alternate]
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue