[feature] unload glyphs
This commit is contained in:
parent
9423659153
commit
2dcd4e7a2c
3 changed files with 552 additions and 280 deletions
124
__init__.py
124
__init__.py
|
@ -35,6 +35,7 @@ if "Font" in locals():
|
||||||
importlib.reload(bimport)
|
importlib.reload(bimport)
|
||||||
importlib.reload(addon_updater_ops)
|
importlib.reload(addon_updater_ops)
|
||||||
|
|
||||||
|
|
||||||
def getPreferences(context):
|
def getPreferences(context):
|
||||||
preferences = context.preferences
|
preferences = context.preferences
|
||||||
return preferences.addons[__name__].preferences
|
return preferences.addons[__name__].preferences
|
||||||
|
@ -158,6 +159,7 @@ class ABC3D_glyph_properties(bpy.types.PropertyGroup):
|
||||||
update=update_callback,
|
update=update_callback,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ABC3D_text_properties(bpy.types.PropertyGroup):
|
class ABC3D_text_properties(bpy.types.PropertyGroup):
|
||||||
def font_items_callback(self, context):
|
def font_items_callback(self, context):
|
||||||
items = []
|
items = []
|
||||||
|
@ -165,21 +167,6 @@ class ABC3D_text_properties(bpy.types.PropertyGroup):
|
||||||
items.append((f"{f[0]} {f[1]}", f"{f[0]} {f[1]}", ""))
|
items.append((f"{f[0]} {f[1]}", f"{f[0]} {f[1]}", ""))
|
||||||
return items
|
return items
|
||||||
|
|
||||||
def font_default_callback(self, context):
|
|
||||||
d = context.scene.abc3d_data
|
|
||||||
if len(d.available_fonts) > 0:
|
|
||||||
if len(d.available_fonts) > d.active_text_index:
|
|
||||||
f = d.available_fonts[d.active_text_index]
|
|
||||||
return 0 # f"{f.font_name} {f.face_name}"
|
|
||||||
else:
|
|
||||||
f = d.available_fonts[0]
|
|
||||||
return 0 # f"{f.font_name} {f.face_name}"
|
|
||||||
|
|
||||||
if not isinstance(self.font_name, None) and not isinstance(self.face_name, None):
|
|
||||||
return 0 # f"{self.font_name} {self.face_name}"
|
|
||||||
else:
|
|
||||||
return 0 # ""
|
|
||||||
|
|
||||||
def glyphs_update_callback(self, context):
|
def glyphs_update_callback(self, context):
|
||||||
butils.prepare_text(self.font_name, self.face_name, self.text)
|
butils.prepare_text(self.font_name, self.face_name, self.text)
|
||||||
butils.set_text_on_curve(self, can_regenerate=True)
|
butils.set_text_on_curve(self, can_regenerate=True)
|
||||||
|
@ -283,13 +270,16 @@ class ABC3D_data(bpy.types.PropertyGroup):
|
||||||
|
|
||||||
def active_text_index_update(self, context):
|
def active_text_index_update(self, context):
|
||||||
if self.active_text_index != -1:
|
if self.active_text_index != -1:
|
||||||
text_properties = butils.get_text_properties(self.active_text_index, context.scene)
|
text_properties = butils.get_text_properties(
|
||||||
|
self.active_text_index, context.scene
|
||||||
|
)
|
||||||
if text_properties is not None:
|
if text_properties is not None:
|
||||||
o = text_properties.text_object
|
o = text_properties.text_object
|
||||||
# active_text_index changed. so let's update the selection
|
# active_text_index changed. so let's update the selection
|
||||||
# check if it is already selected
|
# check if it is already selected
|
||||||
# or perhaps one of the glyphs
|
# or perhaps one of the glyphs
|
||||||
if (o is not None
|
if (
|
||||||
|
o is not None
|
||||||
and not o.select_get()
|
and not o.select_get()
|
||||||
and not len([c for c in o.children if c.select_get()]) > 0
|
and not len([c for c in o.children if c.select_get()]) > 0
|
||||||
):
|
):
|
||||||
|
@ -455,10 +445,7 @@ class ABC3D_PT_TextPlacement(bpy.types.Panel):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(self, context):
|
def poll(self, context):
|
||||||
if (
|
if context.active_object is not None and context.active_object.type == "CURVE":
|
||||||
context.active_object is not None
|
|
||||||
and context.active_object.type == "CURVE"
|
|
||||||
):
|
|
||||||
self.can_place = True
|
self.can_place = True
|
||||||
else:
|
else:
|
||||||
self.can_place = False
|
self.can_place = False
|
||||||
|
@ -649,6 +636,7 @@ class ABC3D_PG_FontCreation(bpy.types.PropertyGroup):
|
||||||
update=naming_glyph_id_update_callback,
|
update=naming_glyph_id_update_callback,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ABC3D_OT_NamingHelper(bpy.types.Operator):
|
class ABC3D_OT_NamingHelper(bpy.types.Operator):
|
||||||
bl_label = "Font Creation Naming Helper Apply To Active Object"
|
bl_label = "Font Creation Naming Helper Apply To Active Object"
|
||||||
bl_idname = f"{__name__}.apply_naming_helper"
|
bl_idname = f"{__name__}.apply_naming_helper"
|
||||||
|
@ -686,7 +674,10 @@ class ABC3D_PT_NamingHelper(bpy.types.Panel):
|
||||||
box.row().prop(abc3d_font_creation, "face_name")
|
box.row().prop(abc3d_font_creation, "face_name")
|
||||||
box.label(text="Glyph Output Name")
|
box.label(text="Glyph Output Name")
|
||||||
box.row().prop(abc3d_font_creation, "naming_glyph_name")
|
box.row().prop(abc3d_font_creation, "naming_glyph_name")
|
||||||
box.row().operator(f"{__name__}.apply_naming_helper", text="Apply name to active object")
|
box.row().operator(
|
||||||
|
f"{__name__}.apply_naming_helper", text="Apply name to active object"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ABC3D_PT_FontCreation(bpy.types.Panel):
|
class ABC3D_PT_FontCreation(bpy.types.Panel):
|
||||||
bl_label = "Font Creation"
|
bl_label = "Font Creation"
|
||||||
|
@ -725,7 +716,10 @@ class ABC3D_PT_FontCreation(bpy.types.Panel):
|
||||||
f"{__name__}.temporaryhelper", text="Debug Function Do Not Use"
|
f"{__name__}.temporaryhelper", text="Debug Function Do Not Use"
|
||||||
)
|
)
|
||||||
box.label(text="origin points")
|
box.label(text="origin points")
|
||||||
box.row().operator(f"{__name__}.align_origins_to_active_object", text="Align origins to Active Object")
|
box.row().operator(
|
||||||
|
f"{__name__}.align_origins_to_active_object",
|
||||||
|
text="Align origins to Active Object",
|
||||||
|
)
|
||||||
# box.row().operator(f"{__name__}.align_origins_to_metrics", text="Align origins to Metrics (left)")
|
# box.row().operator(f"{__name__}.align_origins_to_metrics", text="Align origins to Metrics (left)")
|
||||||
# box.row().operator(f"{__name__}.fix_objects_metrics_origins", text="Fix objects metrics origins")
|
# box.row().operator(f"{__name__}.fix_objects_metrics_origins", text="Fix objects metrics origins")
|
||||||
|
|
||||||
|
@ -749,27 +743,36 @@ class ABC3D_PT_TextPropertiesPanel(bpy.types.Panel):
|
||||||
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:
|
||||||
if butils.is_or_has_parent(bpy.context.active_object, t.text_object, max_depth=4):
|
if butils.is_or_has_parent(
|
||||||
|
bpy.context.active_object, t.text_object, max_depth=4
|
||||||
|
):
|
||||||
return t
|
return t
|
||||||
return None
|
return None
|
||||||
|
|
||||||
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()}_text_id" in a_o
|
if (
|
||||||
and f"{utils.prefix()}_glyph_index" in a_o):
|
f"{utils.prefix()}_text_id" in a_o
|
||||||
|
and f"{utils.prefix()}_glyph_index" in a_o
|
||||||
|
):
|
||||||
text_index = a_o[f"{utils.prefix()}_text_id"]
|
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:
|
||||||
for t in bpy.context.scene.abc3d_data.available_texts:
|
for t in bpy.context.scene.abc3d_data.available_texts:
|
||||||
if butils.is_or_has_parent(a_o, t.text_object, if_is_parent=False, max_depth=4):
|
if butils.is_or_has_parent(
|
||||||
|
a_o, t.text_object, if_is_parent=False, max_depth=4
|
||||||
|
):
|
||||||
for g in t.glyphs:
|
for g in t.glyphs:
|
||||||
if butils.is_or_has_parent(a_o, g.glyph_object, max_depth=4):
|
if butils.is_or_has_parent(
|
||||||
|
a_o, g.glyph_object, max_depth=4
|
||||||
|
):
|
||||||
return g
|
return g
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
# def font_items_callback(self, context):
|
# def font_items_callback(self, context):
|
||||||
# items = []
|
# items = []
|
||||||
# fonts = Font.get_loaded_fonts_and_faces()
|
# fonts = Font.get_loaded_fonts_and_faces()
|
||||||
|
@ -840,7 +843,6 @@ class ABC3D_PT_TextPropertiesPanel(bpy.types.Panel):
|
||||||
box.row().prop(glyph_props, "letter_spacing")
|
box.row().prop(glyph_props, "letter_spacing")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ABC3D_OT_InstallFont(bpy.types.Operator):
|
class ABC3D_OT_InstallFont(bpy.types.Operator):
|
||||||
"""Install or load Fontfile from path above.
|
"""Install or load Fontfile from path above.
|
||||||
(Format must be *.glb or *.gltf)"""
|
(Format must be *.glb or *.gltf)"""
|
||||||
|
@ -1026,7 +1028,14 @@ class ABC3D_OT_LoadFont(bpy.types.Operator):
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
face: Font.FontFace = Font.get_font_face(self.font_name, self.face_name)
|
face: Font.FontFace = Font.get_font_face(self.font_name, self.face_name)
|
||||||
if face is None:
|
if face is None:
|
||||||
butils.ShowMessageBox(f"{utils.prefix()} Load Font", icon="ERROR", message=["Could not load font, sorry!", f"{self.font_name=} {self.face_name=}"])
|
butils.ShowMessageBox(
|
||||||
|
f"{utils.prefix()} Load Font",
|
||||||
|
icon="ERROR",
|
||||||
|
message=[
|
||||||
|
"Could not load font, sorry!",
|
||||||
|
f"{self.font_name=} {self.face_name=}",
|
||||||
|
],
|
||||||
|
)
|
||||||
return {"CANCELLED"}
|
return {"CANCELLED"}
|
||||||
filepaths = face.filepaths
|
filepaths = face.filepaths
|
||||||
for f in filepaths:
|
for f in filepaths:
|
||||||
|
@ -1090,6 +1099,7 @@ class ABC3D_OT_AlignMetrics(bpy.types.Operator):
|
||||||
butils.align_metrics_of_objects(objects)
|
butils.align_metrics_of_objects(objects)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class ABC3D_OT_AlignOriginsToActiveObject(bpy.types.Operator):
|
class ABC3D_OT_AlignOriginsToActiveObject(bpy.types.Operator):
|
||||||
"""Align origins of selected objects to origin of active object on one axis."""
|
"""Align origins of selected objects to origin of active object on one axis."""
|
||||||
|
|
||||||
|
@ -1097,14 +1107,15 @@ class ABC3D_OT_AlignOriginsToActiveObject(bpy.types.Operator):
|
||||||
bl_label = "Align origins to Active Object"
|
bl_label = "Align origins to Active Object"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
enum_axis = (('0','X',''),('1','Y',''),('2','Z',''))
|
enum_axis = (("0", "X", ""), ("1", "Y", ""), ("2", "Z", ""))
|
||||||
axis: bpy.props.EnumProperty(items = enum_axis, default='2')
|
axis: bpy.props.EnumProperty(items=enum_axis, default="2")
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
objects = bpy.context.selected_objects
|
objects = bpy.context.selected_objects
|
||||||
butils.align_origins_to_active_object(objects, int(self.axis))
|
butils.align_origins_to_active_object(objects, int(self.axis))
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
# class ABC3D_OT_AlignOriginsToMetrics(bpy.types.Operator):
|
# class ABC3D_OT_AlignOriginsToMetrics(bpy.types.Operator):
|
||||||
# """Align origins of selected objects to their metrics left border.
|
# """Align origins of selected objects to their metrics left border.
|
||||||
|
|
||||||
|
@ -1157,6 +1168,7 @@ class ABC3D_OT_AlignOriginsToActiveObject(bpy.types.Operator):
|
||||||
# butils.fix_objects_metrics_origins(objects)
|
# butils.fix_objects_metrics_origins(objects)
|
||||||
# return {"FINISHED"}
|
# return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class ABC3D_OT_TemporaryHelper(bpy.types.Operator):
|
class ABC3D_OT_TemporaryHelper(bpy.types.Operator):
|
||||||
"""Temporary Helper ABC3D\nThis could do anything.\nIt's just there to make random functions available for testing."""
|
"""Temporary Helper ABC3D\nThis could do anything.\nIt's just there to make random functions available for testing."""
|
||||||
|
|
||||||
|
@ -1223,6 +1235,7 @@ class ABC3D_OT_RemoveText(bpy.types.Operator):
|
||||||
mom = abc3d_data.available_texts[i].text_object
|
mom = abc3d_data.available_texts[i].text_object
|
||||||
|
|
||||||
if self.remove_custom_properties:
|
if self.remove_custom_properties:
|
||||||
|
|
||||||
def delif(o, p):
|
def delif(o, p):
|
||||||
if p in o:
|
if p in o:
|
||||||
del o[p]
|
del o[p]
|
||||||
|
@ -1372,7 +1385,8 @@ class ABC3D_OT_PlaceText(bpy.types.Operator):
|
||||||
class ABC3D_OT_ToggleABC3DCollection(bpy.types.Operator):
|
class ABC3D_OT_ToggleABC3DCollection(bpy.types.Operator):
|
||||||
"""Toggle ABC3D Collection.
|
"""Toggle ABC3D Collection.
|
||||||
|
|
||||||
This will show the Fonts and Glyphs currently loaded by ABC3D. Useful for font creation, debugging and inspection."""
|
This will show the Fonts and Glyphs currently loaded by ABC3D. Useful for font creation, debugging and inspection.
|
||||||
|
"""
|
||||||
|
|
||||||
bl_idname = f"{__name__}.toggle_abc3d_collection"
|
bl_idname = f"{__name__}.toggle_abc3d_collection"
|
||||||
bl_label = "Toggle Collection visibility"
|
bl_label = "Toggle Collection visibility"
|
||||||
|
@ -1463,7 +1477,9 @@ class ABC3D_OT_SaveFontToFile(bpy.types.Operator):
|
||||||
row.label(text="Please select another directory")
|
row.label(text="Please select another directory")
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.alert = True
|
row.alert = True
|
||||||
elif not utils.can_create_path(export_dir): # does not exist and cannot be created
|
elif not utils.can_create_path(
|
||||||
|
export_dir
|
||||||
|
): # does not exist and cannot be created
|
||||||
self.can_execute = False
|
self.can_execute = False
|
||||||
row.alert = True
|
row.alert = True
|
||||||
row.label(text="Directory does not exist and cannot be created")
|
row.label(text="Directory does not exist and cannot be created")
|
||||||
|
@ -1487,7 +1503,9 @@ class ABC3D_OT_SaveFontToFile(bpy.types.Operator):
|
||||||
|
|
||||||
row.prop(abc3d_data, "export_dir")
|
row.prop(abc3d_data, "export_dir")
|
||||||
else:
|
else:
|
||||||
print(f"{utils.prefix()}::save_font_to_file ERROR {face=} {font_name=} {face_name=}")
|
print(
|
||||||
|
f"{utils.prefix()}::save_font_to_file ERROR {face=} {font_name=} {face_name=}"
|
||||||
|
)
|
||||||
print(f"{utils.prefix()} {Font.fonts=}")
|
print(f"{utils.prefix()} {Font.fonts=}")
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
|
@ -1500,10 +1518,10 @@ class ABC3D_OT_SaveFontToFile(bpy.types.Operator):
|
||||||
"ERROR",
|
"ERROR",
|
||||||
[
|
[
|
||||||
f"export directory '{abc3d_data.export_dir}' does not exist or is not writable",
|
f"export directory '{abc3d_data.export_dir}' does not exist or is not writable",
|
||||||
"try setting another path"
|
"try setting another path",
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
return {'CANCELLED'}
|
return {"CANCELLED"}
|
||||||
|
|
||||||
if not os.path.exists(butils.bpy_to_abspath(abc3d_data.export_dir)):
|
if not os.path.exists(butils.bpy_to_abspath(abc3d_data.export_dir)):
|
||||||
path = butils.bpy_to_abspath(abc3d_data.export_dir)
|
path = butils.bpy_to_abspath(abc3d_data.export_dir)
|
||||||
|
@ -1515,10 +1533,10 @@ class ABC3D_OT_SaveFontToFile(bpy.types.Operator):
|
||||||
"ERROR",
|
"ERROR",
|
||||||
[
|
[
|
||||||
f"export directory '{abc3d_data.export_dir}' does not exist and cannot be created",
|
f"export directory '{abc3d_data.export_dir}' does not exist and cannot be created",
|
||||||
"try setting another path"
|
"try setting another path",
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
return {'CANCELLED'}
|
return {"CANCELLED"}
|
||||||
|
|
||||||
fontcollection = bpy.data.collections.get("ABC3D")
|
fontcollection = bpy.data.collections.get("ABC3D")
|
||||||
|
|
||||||
|
@ -1598,9 +1616,11 @@ class ABC3D_OT_SaveFontToFile(bpy.types.Operator):
|
||||||
use_selection=True,
|
use_selection=True,
|
||||||
use_active_scene=True,
|
use_active_scene=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
def delete_scene():
|
def delete_scene():
|
||||||
bpy.ops.scene.delete()
|
bpy.ops.scene.delete()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
bpy.app.timers.register(lambda: delete_scene(), first_interval=1)
|
bpy.app.timers.register(lambda: delete_scene(), first_interval=1)
|
||||||
|
|
||||||
# bpy.ops.scene.delete()
|
# bpy.ops.scene.delete()
|
||||||
|
@ -1669,7 +1689,9 @@ class ABC3D_OT_CreateFontFromObjects(bpy.types.Operator):
|
||||||
row.prop(self, "autodetect_names")
|
row.prop(self, "autodetect_names")
|
||||||
first_object_name = context.selected_objects[-1].name
|
first_object_name = context.selected_objects[-1].name
|
||||||
if self.autodetect_names:
|
if self.autodetect_names:
|
||||||
self.font_name, self.face_name = self.do_autodetect_names(first_object_name)
|
self.font_name, self.face_name = self.do_autodetect_names(
|
||||||
|
first_object_name
|
||||||
|
)
|
||||||
if self.autodetect_names:
|
if self.autodetect_names:
|
||||||
scale_y = 0.5
|
scale_y = 0.5
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
|
@ -1867,6 +1889,7 @@ def compare_text_object_with_object(t, o, strict=False):
|
||||||
# if
|
# if
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def link_text_object_with_new_text_properties(text_object, scene=None):
|
def link_text_object_with_new_text_properties(text_object, scene=None):
|
||||||
lock_depsgraph_updates(auto_unlock_s=-1)
|
lock_depsgraph_updates(auto_unlock_s=-1)
|
||||||
butils.link_text_object_with_new_text_properties(text_object, scene)
|
butils.link_text_object_with_new_text_properties(text_object, scene)
|
||||||
|
@ -1896,10 +1919,7 @@ def detect_text():
|
||||||
if o[butils.get_key("type")] == "textobject":
|
if o[butils.get_key("type")] == "textobject":
|
||||||
current_text_id = int(o[butils.get_key("text_id")])
|
current_text_id = int(o[butils.get_key("text_id")])
|
||||||
text_properties = butils.get_text_properties(current_text_id)
|
text_properties = butils.get_text_properties(current_text_id)
|
||||||
if (
|
if text_properties is not None and text_properties.text_object == o:
|
||||||
text_properties is not None
|
|
||||||
and text_properties.text_object == o
|
|
||||||
):
|
|
||||||
# all good
|
# all good
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
|
@ -1972,12 +1992,15 @@ def lock_depsgraph_updates(auto_unlock_s=1):
|
||||||
bpy.app.timers.unregister(unlock_depsgraph_updates)
|
bpy.app.timers.unregister(unlock_depsgraph_updates)
|
||||||
bpy.app.timers.register(unlock_depsgraph_updates, first_interval=auto_unlock_s)
|
bpy.app.timers.register(unlock_depsgraph_updates, first_interval=auto_unlock_s)
|
||||||
|
|
||||||
|
|
||||||
def are_depsgraph_updates_locked():
|
def are_depsgraph_updates_locked():
|
||||||
global depsgraph_updates_locked
|
global depsgraph_updates_locked
|
||||||
return depsgraph_updates_locked > 0
|
return depsgraph_updates_locked > 0
|
||||||
|
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
|
||||||
@persistent
|
@persistent
|
||||||
def on_depsgraph_update(scene, depsgraph):
|
def on_depsgraph_update(scene, depsgraph):
|
||||||
if not bpy.context.mode.startswith("EDIT") and not are_depsgraph_updates_locked():
|
if not bpy.context.mode.startswith("EDIT") and not are_depsgraph_updates_locked():
|
||||||
|
@ -2010,7 +2033,9 @@ def register():
|
||||||
addon_updater_ops.make_annotations(cls) # Avoid blender 2.8 warnings.
|
addon_updater_ops.make_annotations(cls) # Avoid blender 2.8 warnings.
|
||||||
bpy.utils.register_class(cls)
|
bpy.utils.register_class(cls)
|
||||||
bpy.types.Scene.abc3d_data = bpy.props.PointerProperty(type=ABC3D_data)
|
bpy.types.Scene.abc3d_data = bpy.props.PointerProperty(type=ABC3D_data)
|
||||||
bpy.types.Scene.abc3d_font_creation = bpy.props.PointerProperty(type=ABC3D_PG_FontCreation)
|
bpy.types.Scene.abc3d_font_creation = bpy.props.PointerProperty(
|
||||||
|
type=ABC3D_PG_FontCreation
|
||||||
|
)
|
||||||
# bpy.types.Object.__del__ = lambda self: print(f"Bye {self.name}")
|
# bpy.types.Object.__del__ = lambda self: print(f"Bye {self.name}")
|
||||||
|
|
||||||
# autostart if we load a blend file
|
# autostart if we load a blend file
|
||||||
|
@ -2025,6 +2050,7 @@ def register():
|
||||||
if on_depsgraph_update not in bpy.app.handlers.depsgraph_update_post:
|
if on_depsgraph_update not in bpy.app.handlers.depsgraph_update_post:
|
||||||
bpy.app.handlers.depsgraph_update_post.append(on_depsgraph_update)
|
bpy.app.handlers.depsgraph_update_post.append(on_depsgraph_update)
|
||||||
|
|
||||||
|
butils.run_in_main_thread(Font.fonts.clear)
|
||||||
butils.run_in_main_thread(butils.clear_available_fonts)
|
butils.run_in_main_thread(butils.clear_available_fonts)
|
||||||
butils.run_in_main_thread(butils.register_installed_fonts)
|
butils.run_in_main_thread(butils.register_installed_fonts)
|
||||||
butils.run_in_main_thread(butils.update_available_fonts)
|
butils.run_in_main_thread(butils.update_available_fonts)
|
||||||
|
|
408
butils.py
408
butils.py
|
@ -4,6 +4,7 @@ import queue
|
||||||
import re
|
import re
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
|
import bpy_types
|
||||||
import mathutils
|
import mathutils
|
||||||
|
|
||||||
# import time # for debugging performance
|
# import time # for debugging performance
|
||||||
|
@ -45,12 +46,14 @@ def apply_all_transforms(obj):
|
||||||
obj.matrix_basis.identity()
|
obj.matrix_basis.identity()
|
||||||
|
|
||||||
|
|
||||||
def get_parent_collection_names(collection, parent_names):
|
# broken
|
||||||
for parent_collection in bpy.data.collections:
|
# def get_parent_collection_names(collection, parent_names):
|
||||||
if collection.name in parent_collection.children.keys():
|
# for parent_collection in bpy.data.collections:
|
||||||
parent_names.append(parent_collection.name)
|
# if collection.name in parent_collection.children.keys():
|
||||||
get_parent_collection_names(parent_collection, parent_names)
|
# parent_names.append(parent_collection.name)
|
||||||
return
|
# get_parent_collection_names(parent_collection, parent_names)
|
||||||
|
# return
|
||||||
|
|
||||||
|
|
||||||
def get_key(key):
|
def get_key(key):
|
||||||
return f"{utils.prefix()}_{key}"
|
return f"{utils.prefix()}_{key}"
|
||||||
|
@ -157,7 +160,6 @@ def calc_tangent_on_bezier(bezier_point_1, bezier_point_2, t):
|
||||||
# calc_point_on_bezier(a,b,0.5)
|
# calc_point_on_bezier(a,b,0.5)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def align_rotations_auto_pivot(
|
def align_rotations_auto_pivot(
|
||||||
mask, input_rotations, vectors, factors, local_main_axis
|
mask, input_rotations, vectors, factors, local_main_axis
|
||||||
):
|
):
|
||||||
|
@ -251,7 +253,7 @@ def calc_point_on_bezier_spline(
|
||||||
# however, maybe let's have it not crash and do this
|
# however, maybe let's have it not crash and do this
|
||||||
if len(bezier_spline_obj.bezier_points) < 1:
|
if len(bezier_spline_obj.bezier_points) < 1:
|
||||||
print(
|
print(
|
||||||
"butils::calc_point_on_bezier_spline: whoops, no points. panicking. return 0,0,0"
|
f"{utils.prefix()}::butils::calc_point_on_bezier_spline: whoops, no points. panicking. return 0,0,0"
|
||||||
)
|
)
|
||||||
if output_tangent:
|
if output_tangent:
|
||||||
return mathutils.Vector((0, 0, 0)), mathutils.Vector((1, 0, 0))
|
return mathutils.Vector((0, 0, 0)), mathutils.Vector((1, 0, 0))
|
||||||
|
@ -275,8 +277,9 @@ def calc_point_on_bezier_spline(
|
||||||
# if the bezier points sit on each other we have same issue
|
# if the bezier points sit on each other we have same issue
|
||||||
# but that is then to be fixed in the bezier
|
# but that is then to be fixed in the bezier
|
||||||
if p.handle_left == p.co and len(bezier_spline_obj.bezier_points) > 1:
|
if p.handle_left == p.co and len(bezier_spline_obj.bezier_points) > 1:
|
||||||
beziers, lengths, total_length = get_real_beziers_and_lengths(bezier_spline_obj,
|
beziers, lengths, total_length = get_real_beziers_and_lengths(
|
||||||
resolution_factor)
|
bezier_spline_obj, resolution_factor
|
||||||
|
)
|
||||||
travel_point = calc_point_on_bezier(beziers[0][1], beziers[0][0], 0.001)
|
travel_point = calc_point_on_bezier(beziers[0][1], beziers[0][0], 0.001)
|
||||||
travel = travel_point.normalized() * distance
|
travel = travel_point.normalized() * distance
|
||||||
|
|
||||||
|
@ -288,8 +291,9 @@ def calc_point_on_bezier_spline(
|
||||||
else:
|
else:
|
||||||
return location
|
return location
|
||||||
|
|
||||||
beziers, lengths, total_length = get_real_beziers_and_lengths(bezier_spline_obj,
|
beziers, lengths, total_length = get_real_beziers_and_lengths(
|
||||||
resolution_factor)
|
bezier_spline_obj, resolution_factor
|
||||||
|
)
|
||||||
|
|
||||||
iterated_distance = 0
|
iterated_distance = 0
|
||||||
for i in range(0, len(beziers)):
|
for i in range(0, len(beziers)):
|
||||||
|
@ -368,6 +372,7 @@ def calc_point_on_bezier_curve(
|
||||||
# and should not happen usually
|
# and should not happen usually
|
||||||
return bezier_curve_obj.matrix_world @ mathutils.Vector((distance, 0, 0))
|
return bezier_curve_obj.matrix_world @ mathutils.Vector((distance, 0, 0))
|
||||||
|
|
||||||
|
|
||||||
# def get_objects_by_name(name, startswith="", endswith=""):
|
# def get_objects_by_name(name, startswith="", endswith=""):
|
||||||
# return [obj for obj in bpy.context.scene.objects if obj.name.startswith(startswith) and if obj.name.endswith(endswith)]
|
# return [obj for obj in bpy.context.scene.objects if obj.name.startswith(startswith) and if obj.name.endswith(endswith)]
|
||||||
|
|
||||||
|
@ -397,13 +402,14 @@ def find_objects_by_custom_property(objects, property_name="", property_value=""
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def turn_collection_hierarchy_into_path(obj):
|
# not verified
|
||||||
parent_collection = obj.users_collection[0]
|
# def turn_collection_hierarchy_into_path(obj):
|
||||||
parent_names = []
|
# parent_collection = obj.users_collection[0]
|
||||||
parent_names.append(parent_collection.name)
|
# parent_names = []
|
||||||
get_parent_collection_names(parent_collection, parent_names)
|
# parent_names.append(parent_collection.name)
|
||||||
parent_names.reverse()
|
# get_parent_collection_names(parent_collection, parent_names)
|
||||||
return "\\".join(parent_names)
|
# parent_names.reverse()
|
||||||
|
# return "\\".join(parent_names)
|
||||||
|
|
||||||
|
|
||||||
def find_font_object(fontcollection, font_name):
|
def find_font_object(fontcollection, font_name):
|
||||||
|
@ -458,7 +464,9 @@ def move_in_fontcollection(obj, fontcollection, allow_duplicates=False):
|
||||||
fontcollection.objects.link(glyphs_obj)
|
fontcollection.objects.link(glyphs_obj)
|
||||||
glyphs_obj.parent = face_obj
|
glyphs_obj.parent = face_obj
|
||||||
elif len(glyphs_objs) > 1:
|
elif len(glyphs_objs) > 1:
|
||||||
print("found more glyphs objects than expected")
|
print(
|
||||||
|
f"{utils.prefix()}::move_in_fontcollection: found more glyphs objects than expected"
|
||||||
|
)
|
||||||
|
|
||||||
# now it must exist
|
# now it must exist
|
||||||
glyphs_obj = find_objects_by_name(face_obj.children, startswith="glyphs")[0]
|
glyphs_obj = find_objects_by_name(face_obj.children, startswith="glyphs")[0]
|
||||||
|
@ -565,8 +573,12 @@ def load_font_from_filepath(filepath, glyphs="", font_name="", face_name=""):
|
||||||
mff_glyphs = []
|
mff_glyphs = []
|
||||||
face: Font.FontFace = Font.get_font_face(mff["font_name"], mff["face_name"])
|
face: Font.FontFace = Font.get_font_face(mff["font_name"], mff["face_name"])
|
||||||
if face is None:
|
if face is None:
|
||||||
print(f"{utils.prefix()}::load_font_from_path({filepath=}, {glyphs=}, {font_name=}, {face_name=}) failed")
|
print(
|
||||||
print(f"modified font face {mff=} could not be accessed.")
|
f"{utils.prefix()}::load_font_from_path({filepath=}, {glyphs=}, {font_name=}, {face_name=}) failed"
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f"{utils.prefix()}:: modified font face {mff=} could not be accessed."
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
# iterate glyphs
|
# iterate glyphs
|
||||||
for g in face.glyphs:
|
for g in face.glyphs:
|
||||||
|
@ -592,6 +604,143 @@ def load_font_from_filepath(filepath, glyphs="", font_name="", face_name=""):
|
||||||
# completely_delete_objects(remove_list)
|
# completely_delete_objects(remove_list)
|
||||||
|
|
||||||
|
|
||||||
|
def is_glyph_used(glyph_alternates):
|
||||||
|
fontcollection: bpy_types.Collection = bpy.data.collections.get("ABC3D")
|
||||||
|
glyph = bpy.types.PointerProperty
|
||||||
|
for glyph in glyph_alternates:
|
||||||
|
for o in bpy.context.scene.objects:
|
||||||
|
# only check other glyphs
|
||||||
|
if is_glyph_object(o):
|
||||||
|
# then attempt to compare properties
|
||||||
|
if (
|
||||||
|
get_key("font_name") in o
|
||||||
|
and get_key("face_name") in o
|
||||||
|
and get_key("glyph_id") in o
|
||||||
|
and o[get_key("font_name")] == glyph["font_name"]
|
||||||
|
and o[get_key("face_name")] == glyph["face_name"]
|
||||||
|
and o[get_key("glyph_id")] == glyph["glyph"]
|
||||||
|
):
|
||||||
|
# following check is not necessary,
|
||||||
|
# but we leave it in for backwards compatibility
|
||||||
|
# properties in the fontcollection start with prefix
|
||||||
|
# and so they should be caught by previous check
|
||||||
|
if fontcollection.users == 0 or not (
|
||||||
|
fontcollection in o.users_collection
|
||||||
|
and len(o.users_collection) <= 1
|
||||||
|
):
|
||||||
|
# it's in the scene and has the correct properties
|
||||||
|
# it is used
|
||||||
|
return True
|
||||||
|
# following check is possibly overkill
|
||||||
|
# but we also check for objects that use the data
|
||||||
|
# and are not glyph objects, in that case we don't pull the data
|
||||||
|
# from under their feet
|
||||||
|
if is_mesh(o) and o.data == glyph.data:
|
||||||
|
# in this case, yes we need to check if it is a glyph in the fontcollection
|
||||||
|
if fontcollection.users == 0 or not (
|
||||||
|
fontcollection in o.users_collection
|
||||||
|
and len(o.users_collection) <= 1
|
||||||
|
):
|
||||||
|
# bam!
|
||||||
|
return True
|
||||||
|
# whoosh!
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def clean_fontcollection(fontcollection=None):
|
||||||
|
if fontcollection is None:
|
||||||
|
fontcollection = bpy.data.collections.get("ABC3D")
|
||||||
|
if fontcollection is None:
|
||||||
|
print(
|
||||||
|
f"{utils.prefix()}::clean_fontcollection: failed beacause fontcollection is none"
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
|
collection_fonts = find_objects_by_custom_property(
|
||||||
|
fontcollection.all_objects, "is_font", True
|
||||||
|
)
|
||||||
|
|
||||||
|
delete_these_fonts = []
|
||||||
|
delete_these_font_faces = []
|
||||||
|
delete_these_glyph_moms = []
|
||||||
|
for font_and_face in Font.get_loaded_fonts_and_faces():
|
||||||
|
font_name = font_and_face[0]
|
||||||
|
face_name = font_and_face[1]
|
||||||
|
|
||||||
|
collection_font_list = find_objects_by_custom_property(
|
||||||
|
collection_fonts, "font_name", font_name
|
||||||
|
)
|
||||||
|
for collection_font in collection_font_list:
|
||||||
|
collection_font_face_list = find_objects_by_custom_property(
|
||||||
|
collection_font.children, "face_name", face_name
|
||||||
|
)
|
||||||
|
count_font_faces = 0
|
||||||
|
for collection_font_face in collection_font_face_list:
|
||||||
|
glyphs_mom_list = find_objects_by_name(
|
||||||
|
collection_font_face.children, startswith="glyphs"
|
||||||
|
)
|
||||||
|
count_glyphs_moms = 0
|
||||||
|
for glyphs_mom in glyphs_mom_list:
|
||||||
|
if len(glyphs_mom.children) == 0:
|
||||||
|
delete_these_glyph_moms.append(glyphs_mom)
|
||||||
|
count_glyphs_moms += 1
|
||||||
|
if len(collection_font_face.children) == count_glyphs_moms:
|
||||||
|
delete_these_font_faces.append(collection_font_face)
|
||||||
|
count_font_faces += 1
|
||||||
|
if len(collection_font.children) == count_font_faces:
|
||||||
|
delete_these_fonts.append(collection_font)
|
||||||
|
|
||||||
|
completely_delete_objects(delete_these_glyph_moms)
|
||||||
|
completely_delete_objects(delete_these_font_faces)
|
||||||
|
completely_delete_objects(delete_these_fonts)
|
||||||
|
|
||||||
|
|
||||||
|
def unload_unused_glyph(font_name, face_name, glyph_id, do_clean_fontcollection=True):
|
||||||
|
fontcollection: bpy_types.Collection = bpy.data.collections.get("ABC3D")
|
||||||
|
glyph_variations = Font.get_glyphs(font_name, face_name, glyph_id)
|
||||||
|
if is_glyph_used(glyph_variations):
|
||||||
|
return False
|
||||||
|
|
||||||
|
delete_these = []
|
||||||
|
for glyph_pointer in glyph_variations:
|
||||||
|
for o in fontcollection.all_objects:
|
||||||
|
if (
|
||||||
|
is_glyph_object(o)
|
||||||
|
and o["font_name"] == font_name
|
||||||
|
and o["face_name"] == face_name
|
||||||
|
and o["glyph"] == glyph_id
|
||||||
|
):
|
||||||
|
if len(o.users_collection) <= 1:
|
||||||
|
delete_these.append(o)
|
||||||
|
completely_delete_objects(delete_these)
|
||||||
|
|
||||||
|
Font.unloaded_glyph(font_name, face_name, glyph_id)
|
||||||
|
if do_clean_fontcollection:
|
||||||
|
clean_fontcollection(fontcollection)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def unload_unused_glyphs(do_clean_fontcollection=True):
|
||||||
|
fontcollection: bpy_types.Collection = bpy.data.collections.get("ABC3D")
|
||||||
|
if fontcollection is not None:
|
||||||
|
for font_and_face in Font.get_loaded_fonts_and_faces():
|
||||||
|
font_name = font_and_face[0]
|
||||||
|
face_name = font_and_face[1]
|
||||||
|
face: Font.FontFace | None = Font.get_font_face(font_name, face_name)
|
||||||
|
if face is None:
|
||||||
|
print(
|
||||||
|
f"{utils.prefix()}::unload_unused_glyphs: face is None {font_name=} {face_name=}"
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
unloaded_these = []
|
||||||
|
for glyph_id in face.loaded_glyphs.copy():
|
||||||
|
unload_unused_glyph(
|
||||||
|
font_name, face_name, glyph_id, do_clean_fontcollection=False
|
||||||
|
)
|
||||||
|
if do_clean_fontcollection:
|
||||||
|
clean_fontcollection(fontcollection)
|
||||||
|
|
||||||
|
|
||||||
def update_available_fonts():
|
def update_available_fonts():
|
||||||
abc3d_data = bpy.context.scene.abc3d_data
|
abc3d_data = bpy.context.scene.abc3d_data
|
||||||
|
|
||||||
|
@ -606,7 +755,9 @@ def update_available_fonts():
|
||||||
f = abc3d_data.available_fonts.add()
|
f = abc3d_data.available_fonts.add()
|
||||||
f.font_name = font_name
|
f.font_name = font_name
|
||||||
f.face_name = face_name
|
f.face_name = face_name
|
||||||
print(f"{utils.prefix()}::update_available_fonts: {__name__} added {font_name} {face_name}")
|
print(
|
||||||
|
f"{utils.prefix()}::update_available_fonts: {__name__} added {font_name} {face_name}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# def update_available_texts():
|
# def update_available_texts():
|
||||||
|
@ -724,6 +875,8 @@ def completely_delete_objects(objs, recursive=True):
|
||||||
except ReferenceError:
|
except ReferenceError:
|
||||||
# not important
|
# not important
|
||||||
pass
|
pass
|
||||||
|
except RuntimeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def is_mesh(o):
|
def is_mesh(o):
|
||||||
|
@ -753,7 +906,7 @@ def is_glyph_object(o):
|
||||||
return o[f"{utils.prefix()}_type"] == "glyph"
|
return o[f"{utils.prefix()}_type"] == "glyph"
|
||||||
try:
|
try:
|
||||||
return (
|
return (
|
||||||
type(o.parent) is not type(None)
|
o.parent is not None
|
||||||
and "glyphs" in o.parent.name
|
and "glyphs" in o.parent.name
|
||||||
and is_mesh(o)
|
and is_mesh(o)
|
||||||
and not is_metrics_object(o)
|
and not is_metrics_object(o)
|
||||||
|
@ -792,6 +945,7 @@ def get_glyph_advance(glyph_obj):
|
||||||
return abs(c.bound_box[4][0] - c.bound_box[0][0])
|
return abs(c.bound_box[4][0] - c.bound_box[0][0])
|
||||||
return abs(glyph_obj.bound_box[4][0] - glyph_obj.bound_box[0][0])
|
return abs(glyph_obj.bound_box[4][0] - glyph_obj.bound_box[0][0])
|
||||||
|
|
||||||
|
|
||||||
def get_glyph_prepost_advances(glyph_obj):
|
def get_glyph_prepost_advances(glyph_obj):
|
||||||
for c in glyph_obj.children:
|
for c in glyph_obj.children:
|
||||||
if is_metrics_object(c):
|
if is_metrics_object(c):
|
||||||
|
@ -807,14 +961,16 @@ def get_glyph_height(glyph_obj):
|
||||||
|
|
||||||
|
|
||||||
def prepare_text(font_name, face_name, text, allow_replacement=True):
|
def prepare_text(font_name, face_name, text, allow_replacement=True):
|
||||||
availability = Font.test_glyphs_availability(
|
availability = Font.test_glyphs_availability(font_name, face_name, text)
|
||||||
font_name, face_name, text
|
|
||||||
)
|
|
||||||
if isinstance(availability, int):
|
if isinstance(availability, int):
|
||||||
if availability == Font.MISSING_FONT:
|
if availability == Font.MISSING_FONT:
|
||||||
print(f"{utils.prefix()}::prepare_text({font_name=}, {face_name=}, {text=}) failed with MISSING_FONT")
|
print(
|
||||||
|
f"{utils.prefix()}::prepare_text({font_name=}, {face_name=}, {text=}) failed with MISSING_FONT"
|
||||||
|
)
|
||||||
if availability is Font.MISSING_FACE:
|
if availability is Font.MISSING_FACE:
|
||||||
print(f"{utils.prefix()}::prepare_text({font_name=}, {face_name=}, {text=}) failed with MISSING_FACE")
|
print(
|
||||||
|
f"{utils.prefix()}::prepare_text({font_name=}, {face_name=}, {text=}) failed with MISSING_FACE"
|
||||||
|
)
|
||||||
return False
|
return False
|
||||||
loadable = availability.unloaded
|
loadable = availability.unloaded
|
||||||
# possibly replace upper and lower case letters with each other
|
# possibly replace upper and lower case letters with each other
|
||||||
|
@ -832,9 +988,16 @@ def prepare_text(font_name, face_name, text, allow_replacement=True):
|
||||||
load_font_from_filepath(filepath, loadable, font_name, face_name)
|
load_font_from_filepath(filepath, loadable, font_name, face_name)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def predict_actual_text(text_properties):
|
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(
|
||||||
AVAILABILITY = Font.test_availability(text_properties.font_name, text_properties.face_name, text_properties.text.swapcase())
|
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
|
t_text = text_properties.text
|
||||||
for c in availability.missing:
|
for c in availability.missing:
|
||||||
t_text = t_text.replace(c, "")
|
t_text = t_text.replace(c, "")
|
||||||
|
@ -842,6 +1005,7 @@ def predict_actual_text(text_properties):
|
||||||
t_text = t_text.replace(c, "")
|
t_text = t_text.replace(c, "")
|
||||||
return t_text
|
return t_text
|
||||||
|
|
||||||
|
|
||||||
def is_bezier(curve):
|
def is_bezier(curve):
|
||||||
if curve.type != "CURVE":
|
if curve.type != "CURVE":
|
||||||
return False
|
return False
|
||||||
|
@ -906,6 +1070,7 @@ COMPARE_TEXT_OBJECT_SAME = 0
|
||||||
COMPARE_TEXT_OBJECT_DIFFER = 1
|
COMPARE_TEXT_OBJECT_DIFFER = 1
|
||||||
COMPARE_TEXT_OBJECT_REGENERATE = 2
|
COMPARE_TEXT_OBJECT_REGENERATE = 2
|
||||||
|
|
||||||
|
|
||||||
def find_free_text_id():
|
def find_free_text_id():
|
||||||
scene = bpy.context.scene
|
scene = bpy.context.scene
|
||||||
abc3d_data = scene.abc3d_data
|
abc3d_data = scene.abc3d_data
|
||||||
|
@ -922,21 +1087,27 @@ def find_free_text_id():
|
||||||
found_free = True
|
found_free = True
|
||||||
return text_id
|
return text_id
|
||||||
|
|
||||||
|
|
||||||
def compare_text_properties_to_text_object(text_properties, o):
|
def compare_text_properties_to_text_object(text_properties, o):
|
||||||
for key in text_object_keys:
|
for key in text_object_keys:
|
||||||
if key in ignore_keys_in_text_object_comparison:
|
if key in ignore_keys_in_text_object_comparison:
|
||||||
continue
|
continue
|
||||||
object_key = get_key(key)
|
object_key = get_key(key)
|
||||||
text_property = text_properties[key] if key in text_properties else getattr(text_properties, 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
|
text_object_property = o[object_key] if object_key in o else False
|
||||||
if text_property != text_object_property:
|
if text_property != text_object_property:
|
||||||
if key in keys_trigger_regeneration:
|
if key in keys_trigger_regeneration:
|
||||||
return COMPARE_TEXT_OBJECT_REGENERATE
|
return COMPARE_TEXT_OBJECT_REGENERATE
|
||||||
elif key in ["translation", "orientation"]:
|
elif key in ["translation", "orientation"]:
|
||||||
if (
|
if (
|
||||||
text_property[0] != text_object_property[0] or
|
text_property[0] != text_object_property[0]
|
||||||
text_property[1] != text_object_property[1] or
|
or text_property[1] != text_object_property[1]
|
||||||
text_property[2] != text_object_property[2]):
|
or text_property[2] != text_object_property[2]
|
||||||
|
):
|
||||||
return COMPARE_TEXT_OBJECT_DIFFER
|
return COMPARE_TEXT_OBJECT_DIFFER
|
||||||
# else same
|
# else same
|
||||||
else:
|
else:
|
||||||
|
@ -950,16 +1121,17 @@ def transfer_text_properties_to_text_object(text_properties, o):
|
||||||
if key in ignore_keys_in_text_object_comparison:
|
if key in ignore_keys_in_text_object_comparison:
|
||||||
continue
|
continue
|
||||||
object_key = get_key(key)
|
object_key = get_key(key)
|
||||||
text_property = text_properties[key] if key in text_properties else getattr(text_properties, key)
|
text_property = (
|
||||||
|
text_properties[key]
|
||||||
|
if key in text_properties
|
||||||
|
else getattr(text_properties, key)
|
||||||
|
)
|
||||||
o[object_key] = text_property
|
o[object_key] = text_property
|
||||||
o[get_key("type")] = "textobject"
|
o[get_key("type")] = "textobject"
|
||||||
|
|
||||||
|
|
||||||
def get_glyph(glyph_id, font_name, face_name, notify_on_replacement=False):
|
def get_glyph(glyph_id, font_name, face_name, notify_on_replacement=False):
|
||||||
glyph_tmp = Font.get_glyph(font_name,
|
glyph_tmp = Font.get_glyph(font_name, face_name, glyph_id, -1)
|
||||||
face_name,
|
|
||||||
glyph_id,
|
|
||||||
-1)
|
|
||||||
if glyph_tmp is None:
|
if glyph_tmp is None:
|
||||||
space_width = Font.is_space(glyph_id)
|
space_width = Font.is_space(glyph_id)
|
||||||
if space_width:
|
if space_width:
|
||||||
|
@ -973,7 +1145,7 @@ def get_glyph(glyph_id, font_name, face_name, notify_on_replacement=False):
|
||||||
text_properties.font_name,
|
text_properties.font_name,
|
||||||
text_properties.face_name,
|
text_properties.face_name,
|
||||||
possible_replacement,
|
possible_replacement,
|
||||||
-1
|
-1,
|
||||||
)
|
)
|
||||||
if glyph_tmp is not None:
|
if glyph_tmp is not None:
|
||||||
message = message + f" (replaced with '{possible_replacement}')"
|
message = message + f" (replaced with '{possible_replacement}')"
|
||||||
|
@ -991,6 +1163,7 @@ def get_glyph(glyph_id, font_name, face_name, notify_on_replacement=False):
|
||||||
|
|
||||||
return glyph_tmp.original
|
return glyph_tmp.original
|
||||||
|
|
||||||
|
|
||||||
def get_text_properties(text_id, scene=None):
|
def get_text_properties(text_id, scene=None):
|
||||||
if scene is None:
|
if scene is None:
|
||||||
scene = bpy.context.scene
|
scene = bpy.context.scene
|
||||||
|
@ -1000,7 +1173,15 @@ def get_text_properties(text_id, scene = None):
|
||||||
return t
|
return t
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def duplicate(obj, data=True, actions=True, add_to_collection=True, collection=None, recursive=True):
|
|
||||||
|
def duplicate(
|
||||||
|
obj,
|
||||||
|
data=True,
|
||||||
|
actions=True,
|
||||||
|
add_to_collection=True,
|
||||||
|
collection=None,
|
||||||
|
recursive=True,
|
||||||
|
):
|
||||||
obj_copy = obj.copy()
|
obj_copy = obj.copy()
|
||||||
if add_to_collection:
|
if add_to_collection:
|
||||||
if collection:
|
if collection:
|
||||||
|
@ -1019,8 +1200,13 @@ def duplicate(obj, data=True, actions=True, add_to_collection=True, collection=N
|
||||||
# child_copy.matrix_parent_inverse = obj_copy.matrix_world.inverted()
|
# child_copy.matrix_parent_inverse = obj_copy.matrix_world.inverted()
|
||||||
return obj_copy
|
return obj_copy
|
||||||
|
|
||||||
def transfer_text_object_to_text_properties(text_object, text_properties, id_from_text_properties=True):
|
|
||||||
possible_brother_text_id = text_object[get_key("text_id")] if get_key("text_id") in text_object else ""
|
def transfer_text_object_to_text_properties(
|
||||||
|
text_object, text_properties, id_from_text_properties=True
|
||||||
|
):
|
||||||
|
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:
|
for key in text_object_keys:
|
||||||
if key in ignore_keys_in_text_object_comparison:
|
if key in ignore_keys_in_text_object_comparison:
|
||||||
continue
|
continue
|
||||||
|
@ -1028,12 +1214,17 @@ def transfer_text_object_to_text_properties(text_object, text_properties, id_fro
|
||||||
if id_from_text_properties and key == "text_id":
|
if id_from_text_properties and key == "text_id":
|
||||||
text_object[object_key] = text_properties["text_id"]
|
text_object[object_key] = text_properties["text_id"]
|
||||||
else:
|
else:
|
||||||
text_object_property = text_object[object_key] if object_key in text_object else False
|
text_object_property = (
|
||||||
|
text_object[object_key] if object_key in text_object else False
|
||||||
|
)
|
||||||
if text_object_property is not False:
|
if text_object_property is not False:
|
||||||
text_properties[key] = text_object_property
|
text_properties[key] = text_object_property
|
||||||
|
|
||||||
if len(text_object.children) == 0:
|
if len(text_object.children) == 0:
|
||||||
if possible_brother_text_id != text_properties["text_id"] and possible_brother_text_id != "":
|
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_properties = get_text_properties(possible_brother_text_id)
|
||||||
possible_brother_object = possible_brother_properties.text_object
|
possible_brother_object = possible_brother_properties.text_object
|
||||||
if possible_brother_object is not None:
|
if possible_brother_object is not None:
|
||||||
|
@ -1047,11 +1238,7 @@ def transfer_text_object_to_text_properties(text_object, text_properties, id_fro
|
||||||
|
|
||||||
found_reconstructable_glyphs = False
|
found_reconstructable_glyphs = False
|
||||||
glyph_objects_with_indices = []
|
glyph_objects_with_indices = []
|
||||||
required_keys = [
|
required_keys = ["glyph_index", "glyph_id", "type"]
|
||||||
"glyph_index",
|
|
||||||
"glyph_id",
|
|
||||||
"type"
|
|
||||||
]
|
|
||||||
for glyph_object in text_object.children:
|
for glyph_object in text_object.children:
|
||||||
if is_glyph_object(glyph_object):
|
if is_glyph_object(glyph_object):
|
||||||
has_required_keys = True
|
has_required_keys = True
|
||||||
|
@ -1111,8 +1298,10 @@ def transfer_text_object_to_text_properties(text_object, text_properties, id_fro
|
||||||
text_properties.glyphs.clear()
|
text_properties.glyphs.clear()
|
||||||
unfortunate_children = text_object.children
|
unfortunate_children = text_object.children
|
||||||
completely_delete_objects(unfortunate_children)
|
completely_delete_objects(unfortunate_children)
|
||||||
|
|
||||||
def kill_children():
|
def kill_children():
|
||||||
completely_delete_objects(unfortunate_children)
|
completely_delete_objects(unfortunate_children)
|
||||||
|
|
||||||
run_in_main_thread(kill_children)
|
run_in_main_thread(kill_children)
|
||||||
|
|
||||||
if "font_name" in text_properties and "face_name" in text_properties:
|
if "font_name" in text_properties and "face_name" in text_properties:
|
||||||
|
@ -1128,9 +1317,11 @@ def link_text_object_with_new_text_properties(text_object, scene=None):
|
||||||
text_properties = scene.abc3d_data.available_texts.add()
|
text_properties = scene.abc3d_data.available_texts.add()
|
||||||
text_properties["text_id"] = text_id
|
text_properties["text_id"] = text_id
|
||||||
# text_object[get_key("text_id")] = text_id
|
# text_object[get_key("text_id")] = text_id
|
||||||
prepare_text(text_object[get_key("font_name")],
|
prepare_text(
|
||||||
|
text_object[get_key("font_name")],
|
||||||
text_object[get_key("face_name")],
|
text_object[get_key("face_name")],
|
||||||
text_object[get_key("text")])
|
text_object[get_key("text")],
|
||||||
|
)
|
||||||
text_properties.text_object = text_object
|
text_properties.text_object = text_object
|
||||||
transfer_text_object_to_text_properties(text_object, text_properties)
|
transfer_text_object_to_text_properties(text_object, text_properties)
|
||||||
|
|
||||||
|
@ -1144,6 +1335,7 @@ def test_finding():
|
||||||
o = bpy.context.active_object
|
o = bpy.context.active_object
|
||||||
transfer_text_object_to_text_properties(o, t)
|
transfer_text_object_to_text_properties(o, t)
|
||||||
|
|
||||||
|
|
||||||
# def detect_texts():
|
# def detect_texts():
|
||||||
# scene = bpy.context.scene
|
# scene = bpy.context.scene
|
||||||
# abc3d_data = scene.abc3d_data
|
# abc3d_data = scene.abc3d_data
|
||||||
|
@ -1153,28 +1345,38 @@ def test_finding():
|
||||||
# and o[get_key("t
|
# and o[get_key("t
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def link_text_object_and_text_properties(o, text_properties):
|
def link_text_object_and_text_properties(o, text_properties):
|
||||||
text_id = text_properties.text_id
|
text_id = text_properties.text_id
|
||||||
o["text_id"] = text_id
|
o["text_id"] = text_id
|
||||||
text_properties.textobject = o
|
text_properties.textobject = o
|
||||||
|
|
||||||
|
|
||||||
def get_glyph_object_property(text_properties, glyph_properties, key):
|
def get_glyph_object_property(text_properties, glyph_properties, key):
|
||||||
if key in glyph_properties:
|
if key in glyph_properties:
|
||||||
return glyph_properties[key]
|
return glyph_properties[key]
|
||||||
if hasattr(glyph_properties, key):
|
if hasattr(glyph_properties, key):
|
||||||
return getattr(glyph_properties, key)
|
return getattr(glyph_properties, key)
|
||||||
return text_properties[key] if key in text_properties else getattr(text_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):
|
|
||||||
|
def transfer_properties_to_glyph_object(
|
||||||
|
text_properties, glyph_properties, glyph_object
|
||||||
|
):
|
||||||
for key in glyph_object_keys:
|
for key in glyph_object_keys:
|
||||||
if key in ignore_keys_in_glyph_object_transfer:
|
if key in ignore_keys_in_glyph_object_transfer:
|
||||||
continue
|
continue
|
||||||
object_key = get_key(key)
|
object_key = get_key(key)
|
||||||
glyph_object[object_key] = get_glyph_object_property(text_properties, glyph_properties, key)
|
glyph_object[object_key] = get_glyph_object_property(
|
||||||
|
text_properties, glyph_properties, key
|
||||||
|
)
|
||||||
glyph_object[get_key("type")] = "glyph"
|
glyph_object[get_key("type")] = "glyph"
|
||||||
glyph_object[get_key("text_id")] = text_properties["text_id"]
|
glyph_object[get_key("text_id")] = text_properties["text_id"]
|
||||||
|
|
||||||
|
|
||||||
def transfer_glyph_object_to_glyph_properties(glyph_object, glyph_properties):
|
def transfer_glyph_object_to_glyph_properties(glyph_object, glyph_properties):
|
||||||
for key in glyph_object_keys:
|
for key in glyph_object_keys:
|
||||||
if key in ignore_keys_in_glyph_object_transfer:
|
if key in ignore_keys_in_glyph_object_transfer:
|
||||||
|
@ -1182,6 +1384,7 @@ def transfer_glyph_object_to_glyph_properties(glyph_object, glyph_properties):
|
||||||
glyph_properties[key] = glyph_object[get_key(key)]
|
glyph_properties[key] = glyph_object[get_key(key)]
|
||||||
glyph_properties["text_id"] = glyph_object[get_key("text_id")]
|
glyph_properties["text_id"] = glyph_object[get_key("text_id")]
|
||||||
|
|
||||||
|
|
||||||
def would_regenerate(text_properties):
|
def would_regenerate(text_properties):
|
||||||
predicted_text = predict_actual_text(text_properties)
|
predicted_text = predict_actual_text(text_properties)
|
||||||
if text_properties.actual_text != predicted_text:
|
if text_properties.actual_text != predicted_text:
|
||||||
|
@ -1218,9 +1421,10 @@ def update_matrices(obj):
|
||||||
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.matrix_parent_inverse * \
|
obj.parent.matrix_world * obj.matrix_parent_inverse * obj.matrix_basis
|
||||||
obj.matrix_basis
|
)
|
||||||
|
|
||||||
|
|
||||||
def is_or_has_parent(o, parent, if_is_parent=True, max_depth=10):
|
def is_or_has_parent(o, parent, if_is_parent=True, max_depth=10):
|
||||||
if o == parent and if_is_parent:
|
if o == parent and if_is_parent:
|
||||||
|
@ -1234,8 +1438,9 @@ def is_or_has_parent(o, parent, if_is_parent=True, max_depth=10):
|
||||||
return False
|
return False
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def parent_to_curve(o, c):
|
def parent_to_curve(o, c):
|
||||||
o.parent_type = 'OBJECT'
|
o.parent_type = "OBJECT"
|
||||||
o.parent = c
|
o.parent = c
|
||||||
# o.matrix_parent_inverse = c.matrix_world.inverted()
|
# o.matrix_parent_inverse = c.matrix_world.inverted()
|
||||||
|
|
||||||
|
@ -1244,13 +1449,15 @@ def parent_to_curve(o, c):
|
||||||
i = -1 if c.data.splines[0].use_cyclic_u else 0
|
i = -1 if c.data.splines[0].use_cyclic_u else 0
|
||||||
p = c.data.splines[0].bezier_points[i].co
|
p = c.data.splines[0].bezier_points[i].co
|
||||||
o.matrix_parent_inverse.translation = p * -1.0
|
o.matrix_parent_inverse.translation = p * -1.0
|
||||||
elif c.data.splines[0].type == 'NURBS':
|
elif c.data.splines[0].type == "NURBS":
|
||||||
cm = c.to_mesh()
|
cm = c.to_mesh()
|
||||||
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
|
||||||
|
|
||||||
An earlier reset cancels the other.
|
An earlier reset cancels the other.
|
||||||
|
@ -1274,7 +1481,6 @@ def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4,
|
||||||
|
|
||||||
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
|
||||||
#
|
#
|
||||||
|
@ -1330,10 +1536,9 @@ def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4,
|
||||||
|
|
||||||
############### GET GLYPH
|
############### GET GLYPH
|
||||||
|
|
||||||
glyph_tmp = Font.get_glyph(text_properties.font_name,
|
glyph_tmp = Font.get_glyph(
|
||||||
text_properties.face_name,
|
text_properties.font_name, text_properties.face_name, glyph_id, -1
|
||||||
glyph_id,
|
)
|
||||||
-1)
|
|
||||||
if glyph_tmp is None:
|
if glyph_tmp is None:
|
||||||
space_width = Font.is_space(glyph_id)
|
space_width = Font.is_space(glyph_id)
|
||||||
if space_width:
|
if space_width:
|
||||||
|
@ -1348,7 +1553,7 @@ def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4,
|
||||||
text_properties.font_name,
|
text_properties.font_name,
|
||||||
text_properties.face_name,
|
text_properties.face_name,
|
||||||
possible_replacement,
|
possible_replacement,
|
||||||
-1
|
-1,
|
||||||
)
|
)
|
||||||
if glyph_tmp is not None:
|
if glyph_tmp is not None:
|
||||||
message = message + f" (replaced with '{possible_replacement}')"
|
message = message + f" (replaced with '{possible_replacement}')"
|
||||||
|
@ -1368,7 +1573,11 @@ def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4,
|
||||||
|
|
||||||
############### GLYPH PROPERTIES
|
############### GLYPH PROPERTIES
|
||||||
|
|
||||||
glyph_properties = text_properties.glyphs[glyph_index] if not regenerate else text_properties.glyphs.add()
|
glyph_properties = (
|
||||||
|
text_properties.glyphs[glyph_index]
|
||||||
|
if not regenerate
|
||||||
|
else text_properties.glyphs.add()
|
||||||
|
)
|
||||||
|
|
||||||
if regenerate:
|
if regenerate:
|
||||||
glyph_properties["glyph_id"] = glyph_id
|
glyph_properties["glyph_id"] = glyph_id
|
||||||
|
@ -1383,14 +1592,16 @@ 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)
|
||||||
transfer_properties_to_glyph_object(text_properties, glyph_properties, outer_node)
|
transfer_properties_to_glyph_object(
|
||||||
|
text_properties, glyph_properties, outer_node
|
||||||
|
)
|
||||||
|
|
||||||
# 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)
|
||||||
|
|
||||||
# Parenting is hard.
|
# Parenting is hard.
|
||||||
inner_node.parent_type = 'OBJECT'
|
inner_node.parent_type = "OBJECT"
|
||||||
inner_node.parent = outer_node
|
inner_node.parent = outer_node
|
||||||
inner_node.matrix_parent_inverse = outer_node.matrix_world.inverted()
|
inner_node.matrix_parent_inverse = outer_node.matrix_world.inverted()
|
||||||
parent_to_curve(outer_node, mom)
|
parent_to_curve(outer_node, mom)
|
||||||
|
@ -1426,7 +1637,9 @@ def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4,
|
||||||
outer_node.constraints.new(type="FOLLOW_PATH")
|
outer_node.constraints.new(type="FOLLOW_PATH")
|
||||||
outer_node.constraints["Follow Path"].target = mom
|
outer_node.constraints["Follow Path"].target = mom
|
||||||
outer_node.constraints["Follow Path"].use_fixed_location = True
|
outer_node.constraints["Follow Path"].use_fixed_location = True
|
||||||
outer_node.constraints["Follow Path"].offset_factor = applied_advance / curve_length
|
outer_node.constraints["Follow Path"].offset_factor = (
|
||||||
|
applied_advance / curve_length
|
||||||
|
)
|
||||||
outer_node.constraints["Follow Path"].use_curve_follow = True
|
outer_node.constraints["Follow Path"].use_curve_follow = True
|
||||||
outer_node.constraints["Follow Path"].forward_axis = "FORWARD_X"
|
outer_node.constraints["Follow Path"].forward_axis = "FORWARD_X"
|
||||||
outer_node.constraints["Follow Path"].up_axis = "UP_Y"
|
outer_node.constraints["Follow Path"].up_axis = "UP_Y"
|
||||||
|
@ -1442,7 +1655,9 @@ def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4,
|
||||||
previous_inner_node_rotation_mode = inner_node.rotation_mode
|
previous_inner_node_rotation_mode = inner_node.rotation_mode
|
||||||
|
|
||||||
# get info from bezier
|
# get info from bezier
|
||||||
location, tangent, spline_index = calc_point_on_bezier_curve(mom, applied_advance, True, True)
|
location, tangent, spline_index = calc_point_on_bezier_curve(
|
||||||
|
mom, applied_advance, True, True
|
||||||
|
)
|
||||||
|
|
||||||
# check if we are on a new line
|
# check if we are on a new line
|
||||||
if spline_index != previous_spline_index:
|
if spline_index != previous_spline_index:
|
||||||
|
@ -1457,13 +1672,19 @@ def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4,
|
||||||
vectors = [tangent]
|
vectors = [tangent]
|
||||||
factors = [1.0]
|
factors = [1.0]
|
||||||
local_main_axis = mathutils.Vector((1.0, 0.0, 0.0))
|
local_main_axis = mathutils.Vector((1.0, 0.0, 0.0))
|
||||||
motor = align_rotations_auto_pivot(
|
motor = (
|
||||||
|
align_rotations_auto_pivot(
|
||||||
mask, input_rotations, vectors, factors, local_main_axis
|
mask, input_rotations, vectors, factors, local_main_axis
|
||||||
) if not text_properties.ignore_orientation else [mathutils.Matrix()]
|
)
|
||||||
|
if not text_properties.ignore_orientation
|
||||||
|
else [mathutils.Matrix()]
|
||||||
|
)
|
||||||
|
|
||||||
q = mathutils.Quaternion()
|
q = mathutils.Quaternion()
|
||||||
q.rotate(text_properties.orientation)
|
q.rotate(text_properties.orientation)
|
||||||
outer_node.rotation_quaternion = (motor[0].to_3x3() @ q.to_matrix()).to_quaternion()
|
outer_node.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,
|
# # 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:
|
# # but this would ignore the curve objects orientation:
|
||||||
|
@ -1482,16 +1703,16 @@ def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4,
|
||||||
############### PREPARE FOR THE NEXT
|
############### PREPARE FOR THE NEXT
|
||||||
|
|
||||||
glyph_advance = (
|
glyph_advance = (
|
||||||
glyph_post_advance * scalor + text_properties.letter_spacing + glyph_properties.letter_spacing
|
glyph_post_advance * scalor
|
||||||
|
+ text_properties.letter_spacing
|
||||||
|
+ glyph_properties.letter_spacing
|
||||||
)
|
)
|
||||||
|
|
||||||
# now we need to compensate for curvature
|
# now we need to compensate for curvature
|
||||||
# otherwise letters will be closer together the curvier the bezier is
|
# otherwise letters will be closer together the curvier the bezier is
|
||||||
# NOTE: this could be done more efficiently
|
# NOTE: this could be done more efficiently
|
||||||
curve_compensation = 0
|
curve_compensation = 0
|
||||||
if distribution_type == "CALCULATE" and (
|
if distribution_type == "CALCULATE" and (not is_newline or spline_index == 0):
|
||||||
not is_newline or spline_index == 0
|
|
||||||
):
|
|
||||||
if text_properties.compensate_curvature and glyph_advance > 0:
|
if text_properties.compensate_curvature and glyph_advance > 0:
|
||||||
previous_location, psi = calc_point_on_bezier_curve(
|
previous_location, psi = calc_point_on_bezier_curve(
|
||||||
mom, advance, False, True
|
mom, advance, False, True
|
||||||
|
@ -1503,8 +1724,10 @@ def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4,
|
||||||
n_max = 100
|
n_max = 100
|
||||||
n = 0
|
n = 0
|
||||||
while (
|
while (
|
||||||
previous_location - new_location
|
(previous_location - new_location).length > glyph_advance
|
||||||
).length > glyph_advance and psi == si and n < n_max:
|
and psi == si
|
||||||
|
and n < n_max
|
||||||
|
):
|
||||||
curve_compensation = curve_compensation - glyph_advance * 0.01
|
curve_compensation = curve_compensation - glyph_advance * 0.01
|
||||||
tmp_new_location, si = calc_point_on_bezier_curve(
|
tmp_new_location, si = calc_point_on_bezier_curve(
|
||||||
mom,
|
mom,
|
||||||
|
@ -1513,14 +1736,18 @@ def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4,
|
||||||
output_spline_index=True,
|
output_spline_index=True,
|
||||||
)
|
)
|
||||||
if tmp_new_location == new_location:
|
if tmp_new_location == new_location:
|
||||||
print(f"{utils.prefix()}::set_text_on_curve::compensate_curvature while loop overstaying welcome")
|
print(
|
||||||
|
f"{utils.prefix()}::set_text_on_curve::compensate_curvature while loop overstaying welcome"
|
||||||
|
)
|
||||||
break
|
break
|
||||||
new_location = tmp_new_location
|
new_location = tmp_new_location
|
||||||
n += 1
|
n += 1
|
||||||
n = 0
|
n = 0
|
||||||
while (
|
while (
|
||||||
previous_location - new_location
|
(previous_location - new_location).length < glyph_advance
|
||||||
).length < glyph_advance and psi == si and n < n_max:
|
and psi == si
|
||||||
|
and n < n_max
|
||||||
|
):
|
||||||
curve_compensation = curve_compensation + glyph_advance * 0.01
|
curve_compensation = curve_compensation + glyph_advance * 0.01
|
||||||
tmp_new_location, si = calc_point_on_bezier_curve(
|
tmp_new_location, si = calc_point_on_bezier_curve(
|
||||||
mom,
|
mom,
|
||||||
|
@ -1529,7 +1756,9 @@ def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4,
|
||||||
output_spline_index=True,
|
output_spline_index=True,
|
||||||
)
|
)
|
||||||
if tmp_new_location == new_location:
|
if tmp_new_location == new_location:
|
||||||
print(f"{utils.prefix()}::set_text_on_curve::compensate_curvature while loop overstaying welcome")
|
print(
|
||||||
|
f"{utils.prefix()}::set_text_on_curve::compensate_curvature while loop overstaying welcome"
|
||||||
|
)
|
||||||
break
|
break
|
||||||
new_location = tmp_new_location
|
new_location = tmp_new_location
|
||||||
n += 1
|
n += 1
|
||||||
|
@ -1813,6 +2042,8 @@ def add_default_metrics_to_objects(objects=None, overwrite_existing=False):
|
||||||
targets = []
|
targets = []
|
||||||
reference_bound_box = None
|
reference_bound_box = None
|
||||||
for o in objects:
|
for o in objects:
|
||||||
|
if not hasattr(o, "parent"):
|
||||||
|
print(f"{o.name} has not a PARENTNTNTNTNTNNTNTNTNTNTN")
|
||||||
is_possibly_glyph = is_glyph(o)
|
is_possibly_glyph = is_glyph(o)
|
||||||
if is_possibly_glyph:
|
if is_possibly_glyph:
|
||||||
metrics = []
|
metrics = []
|
||||||
|
@ -1932,6 +2163,7 @@ def align_metrics_of_objects(objects=None):
|
||||||
add_metrics_obj_from_bound_box(t, bound_box)
|
add_metrics_obj_from_bound_box(t, bound_box)
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def align_origins_to_active_object(objects=None, axis=2):
|
def align_origins_to_active_object(objects=None, axis=2):
|
||||||
if objects is None:
|
if objects is None:
|
||||||
objects = bpy.context.selected_objects
|
objects = bpy.context.selected_objects
|
||||||
|
@ -1957,6 +2189,7 @@ def align_origins_to_active_object(objects=None, axis=2):
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
# NOTE:
|
# NOTE:
|
||||||
# Following code is not necessary anymore,
|
# Following code is not necessary anymore,
|
||||||
# as we derive the advance through metrics
|
# as we derive the advance through metrics
|
||||||
|
@ -2037,4 +2270,3 @@ 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 ""
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
from typing import Dict
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import NamedTuple
|
from typing import Dict, NamedTuple
|
||||||
|
|
||||||
# convenience dictionary for translating names to glyph ids
|
# convenience dictionary for translating names to glyph ids
|
||||||
# note: overwritten/extended by the content of "glypNamesToUnicode.txt"
|
# note: overwritten/extended by the content of "glypNamesToUnicode.txt"
|
||||||
|
@ -161,7 +160,6 @@ class Font:
|
||||||
self.faces = faces
|
self.faces = faces
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def register_font(font_name, face_name, glyphs_in_fontfile, filepath):
|
def register_font(font_name, face_name, glyphs_in_fontfile, filepath):
|
||||||
if not fonts.keys().__contains__(font_name):
|
if not fonts.keys().__contains__(font_name):
|
||||||
fonts[font_name] = Font({})
|
fonts[font_name] = Font({})
|
||||||
|
@ -250,7 +248,10 @@ def get_glyphs(font_name, face_name, glyph_id):
|
||||||
face = get_font_face(font_name, face_name)
|
face = get_font_face(font_name, face_name)
|
||||||
if face is None:
|
if face is None:
|
||||||
print(f"ABC3D::get_glyph: font({font_name}) face({face_name}) not found")
|
print(f"ABC3D::get_glyph: font({font_name}) face({face_name}) not found")
|
||||||
|
try:
|
||||||
print(fonts[font_name].faces.keys())
|
print(fonts[font_name].faces.keys())
|
||||||
|
except:
|
||||||
|
print(fonts.keys())
|
||||||
return []
|
return []
|
||||||
|
|
||||||
glyphs_for_id = face.glyphs.get(glyph_id)
|
glyphs_for_id = face.glyphs.get(glyph_id)
|
||||||
|
@ -291,6 +292,19 @@ def get_glyph(font_name, face_name, glyph_id, alternate=0):
|
||||||
return glyphs[alternate]
|
return glyphs[alternate]
|
||||||
|
|
||||||
|
|
||||||
|
def unloaded_glyph(font_name, face_name, glyph_id):
|
||||||
|
face = get_font_face(font_name, face_name)
|
||||||
|
if face is None:
|
||||||
|
print(f"ABC3D::get_glyph: font({font_name}) face({face_name}) not found")
|
||||||
|
return
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
fonts[font_name].faces[face_name].loaded_glyphs.remove(glyph_id)
|
||||||
|
del fonts[font_name].faces[face_name].glyphs[glyph_id]
|
||||||
|
except ValueError:
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
class GlyphsAvailability(NamedTuple):
|
class GlyphsAvailability(NamedTuple):
|
||||||
loaded: str
|
loaded: str
|
||||||
missing: str
|
missing: str
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue