Compare commits
12 commits
71dda9f316
...
56afa0b453
Author | SHA1 | Date | |
---|---|---|---|
56afa0b453 | |||
8094e56e5a | |||
f911f2f23a | |||
7c9a725338 | |||
d56ca84236 | |||
6943a9189c | |||
d88c0c50cd | |||
ed5db93613 | |||
6708fd0491 | |||
34eeb4af94 | |||
be5f060c98 | |||
da382f5fab |
6 changed files with 548 additions and 174 deletions
|
@ -5,7 +5,7 @@
|
||||||
/ ___ \| |_) | |___ ___) | |_| |
|
/ ___ \| |_) | |___ ___) | |_| |
|
||||||
/_/ \_\____/ \____|____/|____/
|
/_/ \_\____/ \____|____/|____/
|
||||||
```
|
```
|
||||||
v0.0.5
|
v0.0.6
|
||||||
|
|
||||||
Convenience tool to work with 3D typography in Blender and Cinema4D.
|
Convenience tool to work with 3D typography in Blender and Cinema4D.
|
||||||
|
|
||||||
|
|
238
__init__.py
238
__init__.py
|
@ -16,13 +16,13 @@ from .common import Font, utils
|
||||||
bl_info = {
|
bl_info = {
|
||||||
"name": "ABC3D",
|
"name": "ABC3D",
|
||||||
"author": "Jakob Schlötter, Studio Pointer*",
|
"author": "Jakob Schlötter, Studio Pointer*",
|
||||||
"version": (0, 0, 5),
|
"version": (0, 0, 6),
|
||||||
"blender": (4, 1, 0),
|
"blender": (4, 1, 0),
|
||||||
"location": "VIEW3D",
|
"location": "VIEW3D",
|
||||||
"description": "Convenience addon for 3D fonts",
|
"description": "Convenience addon for 3D fonts",
|
||||||
"category": "Typography",
|
"category": "Typography",
|
||||||
}
|
}
|
||||||
# NOTE: also change version in common/utils.py
|
# NOTE: also change version in common/utils.py and README.md
|
||||||
|
|
||||||
# make sure that modules are reloadable
|
# make sure that modules are reloadable
|
||||||
# when registering
|
# when registering
|
||||||
|
@ -134,11 +134,26 @@ class ABC3D_available_font(bpy.types.PropertyGroup):
|
||||||
|
|
||||||
|
|
||||||
class ABC3D_glyph_properties(bpy.types.PropertyGroup):
|
class ABC3D_glyph_properties(bpy.types.PropertyGroup):
|
||||||
|
|
||||||
|
def update_callback(self, context):
|
||||||
|
if self.text_id >= 0:
|
||||||
|
butils.set_text_on_curve(
|
||||||
|
context.scene.abc3d_data.available_texts[self.text_id]
|
||||||
|
)
|
||||||
|
|
||||||
glyph_id: bpy.props.StringProperty(maxlen=1)
|
glyph_id: bpy.props.StringProperty(maxlen=1)
|
||||||
|
text_id: bpy.props.IntProperty(
|
||||||
|
default=-1,
|
||||||
|
)
|
||||||
|
alternate: bpy.props.IntProperty(
|
||||||
|
default=-1,
|
||||||
|
update=update_callback,
|
||||||
|
)
|
||||||
glyph_object: bpy.props.PointerProperty(type=bpy.types.Object)
|
glyph_object: bpy.props.PointerProperty(type=bpy.types.Object)
|
||||||
letter_spacing: bpy.props.FloatProperty(
|
letter_spacing: bpy.props.FloatProperty(
|
||||||
name="Letter Spacing",
|
name="Letter Spacing",
|
||||||
description="Letter Spacing",
|
description="Letter Spacing",
|
||||||
|
update=update_callback,
|
||||||
)
|
)
|
||||||
|
|
||||||
class ABC3D_text_properties(bpy.types.PropertyGroup):
|
class ABC3D_text_properties(bpy.types.PropertyGroup):
|
||||||
|
@ -564,6 +579,95 @@ class ABC3D_PT_TextManagement(bpy.types.Panel):
|
||||||
layout.row().operator(f"{__name__}.remove_text", text="Remove Textobject")
|
layout.row().operator(f"{__name__}.remove_text", text="Remove Textobject")
|
||||||
|
|
||||||
|
|
||||||
|
class ABC3D_PG_FontCreation(bpy.types.PropertyGroup):
|
||||||
|
bl_label = "Font Creation Properties"
|
||||||
|
# bl_parent_id = "ABC3D_PG_NamingHelper"
|
||||||
|
# bl_category = "ABC3D"
|
||||||
|
# bl_space_type = "VIEW_3D"
|
||||||
|
# bl_region_type = "UI"
|
||||||
|
# bl_options = {"DEFAULT_CLOSED"}
|
||||||
|
|
||||||
|
def naming_glyph_id_update_callback(self, context):
|
||||||
|
glyph_name = Font.glyph_to_name(self.naming_glyph_id)
|
||||||
|
if self.naming_glyph_full:
|
||||||
|
self.naming_glyph_name = f"{glyph_name}_{self.font_name}_{self.face_name}"
|
||||||
|
else:
|
||||||
|
self.naming_glyph_name = glyph_name
|
||||||
|
|
||||||
|
naming_glyph_id: bpy.props.StringProperty(
|
||||||
|
name="",
|
||||||
|
description="find proper naming for a glyph",
|
||||||
|
default="",
|
||||||
|
maxlen=32,
|
||||||
|
update=naming_glyph_id_update_callback,
|
||||||
|
)
|
||||||
|
|
||||||
|
naming_glyph_name: bpy.props.StringProperty(
|
||||||
|
name="",
|
||||||
|
description="find proper naming for a glyph",
|
||||||
|
default="",
|
||||||
|
maxlen=1024,
|
||||||
|
)
|
||||||
|
|
||||||
|
naming_glyph_full: bpy.props.BoolProperty(
|
||||||
|
default=True,
|
||||||
|
description="Generate full name",
|
||||||
|
update=naming_glyph_id_update_callback,
|
||||||
|
)
|
||||||
|
|
||||||
|
font_name: bpy.props.StringProperty(
|
||||||
|
name="",
|
||||||
|
description="Font name",
|
||||||
|
default="NM_Origin",
|
||||||
|
update=naming_glyph_id_update_callback,
|
||||||
|
)
|
||||||
|
|
||||||
|
face_name: bpy.props.StringProperty(
|
||||||
|
name="",
|
||||||
|
description="FontFace name",
|
||||||
|
default="Tender",
|
||||||
|
update=naming_glyph_id_update_callback,
|
||||||
|
)
|
||||||
|
|
||||||
|
class ABC3D_OT_NamingHelper(bpy.types.Operator):
|
||||||
|
bl_label = "Font Creation Naming Helper Apply To Active Object"
|
||||||
|
bl_idname = f"{__name__}.apply_naming_helper"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
abc3d_font_creation = context.scene.abc3d_font_creation
|
||||||
|
name = abc3d_font_creation.naming_glyph_name
|
||||||
|
context.active_object.name = name
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class ABC3D_PT_NamingHelper(bpy.types.Panel):
|
||||||
|
bl_label = "Naming Helper"
|
||||||
|
bl_parent_id = "ABC3D_PT_FontCreation"
|
||||||
|
bl_category = "ABC3D"
|
||||||
|
bl_space_type = "VIEW_3D"
|
||||||
|
bl_region_type = "UI"
|
||||||
|
bl_options = {"DEFAULT_CLOSED"}
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
scene = context.scene
|
||||||
|
|
||||||
|
abc3d_font_creation = scene.abc3d_font_creation
|
||||||
|
|
||||||
|
box = layout.box()
|
||||||
|
box.label(text="Glyph Naming Helper")
|
||||||
|
box.row().prop(abc3d_font_creation, "naming_glyph_full")
|
||||||
|
box.label(text="Glyph Character Input")
|
||||||
|
box.row().prop(abc3d_font_creation, "naming_glyph_id")
|
||||||
|
box.label(text="Font name:")
|
||||||
|
box.row().prop(abc3d_font_creation, "font_name")
|
||||||
|
box.label(text="FontFace name:")
|
||||||
|
box.row().prop(abc3d_font_creation, "face_name")
|
||||||
|
box.label(text="Glyph Output Name")
|
||||||
|
box.row().prop(abc3d_font_creation, "naming_glyph_name")
|
||||||
|
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"
|
||||||
bl_parent_id = "ABC3D_PT_Panel"
|
bl_parent_id = "ABC3D_PT_Panel"
|
||||||
|
@ -575,9 +679,6 @@ class ABC3D_PT_FontCreation(bpy.types.Panel):
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
wm = context.window_manager
|
wm = context.window_manager
|
||||||
scene = context.scene
|
|
||||||
|
|
||||||
abc3d_data = scene.abc3d_data
|
|
||||||
|
|
||||||
layout.row().operator(
|
layout.row().operator(
|
||||||
f"{__name__}.toggle_abc3d_collection", text="Toggle Collection"
|
f"{__name__}.toggle_abc3d_collection", text="Toggle Collection"
|
||||||
|
@ -588,6 +689,7 @@ class ABC3D_PT_FontCreation(bpy.types.Panel):
|
||||||
layout.row().operator(
|
layout.row().operator(
|
||||||
f"{__name__}.save_font_to_file", text="Export Font To File"
|
f"{__name__}.save_font_to_file", text="Export Font To File"
|
||||||
)
|
)
|
||||||
|
|
||||||
box = layout.box()
|
box = layout.box()
|
||||||
box.label(text="metrics")
|
box.label(text="metrics")
|
||||||
box.row().operator(
|
box.row().operator(
|
||||||
|
@ -602,6 +704,10 @@ class ABC3D_PT_FontCreation(bpy.types.Panel):
|
||||||
layout.row().operator(
|
layout.row().operator(
|
||||||
f"{__name__}.temporaryhelper", text="Debug Function Do Not Use"
|
f"{__name__}.temporaryhelper", text="Debug Function Do Not Use"
|
||||||
)
|
)
|
||||||
|
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_metrics", text="Align origins to Metrics (left)")
|
||||||
|
# box.row().operator(f"{__name__}.fix_objects_metrics_origins", text="Fix objects metrics origins")
|
||||||
|
|
||||||
|
|
||||||
class ABC3D_PT_TextPropertiesPanel(bpy.types.Panel):
|
class ABC3D_PT_TextPropertiesPanel(bpy.types.Panel):
|
||||||
|
@ -613,14 +719,38 @@ class ABC3D_PT_TextPropertiesPanel(bpy.types.Panel):
|
||||||
|
|
||||||
def get_active_text_properties(self):
|
def get_active_text_properties(self):
|
||||||
# and bpy.context.object.select_get():
|
# and bpy.context.object.select_get():
|
||||||
if type(bpy.context.active_object) != type(None):
|
a_o = bpy.context.active_object
|
||||||
|
if a_o is not None:
|
||||||
|
if f"{utils.prefix()}_linked_textobject" in a_o:
|
||||||
|
text_index = a_o[f"{utils.prefix()}_linked_textobject"]
|
||||||
|
return bpy.context.scene.abc3d_data.available_texts[text_index]
|
||||||
|
elif f"{utils.prefix()}_linked_textobject" in a_o.parent:
|
||||||
|
text_index = a_o.parent[f"{utils.prefix()}_linked_textobject"]
|
||||||
|
return bpy.context.scene.abc3d_data.available_texts[text_index]
|
||||||
|
else:
|
||||||
for t in bpy.context.scene.abc3d_data.available_texts:
|
for t in bpy.context.scene.abc3d_data.available_texts:
|
||||||
if bpy.context.active_object == t.text_object:
|
if butils.is_or_has_parent(bpy.context.active_object, t.text_object, max_depth=4):
|
||||||
return t
|
|
||||||
if bpy.context.active_object.parent == t.text_object:
|
|
||||||
return t
|
return t
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
# NOTE: HERE
|
||||||
|
def get_active_glyph_properties(self):
|
||||||
|
a_o = bpy.context.active_object
|
||||||
|
if a_o is not None:
|
||||||
|
if (f"{utils.prefix()}_linked_textobject" in a_o
|
||||||
|
and f"{utils.prefix()}_glyph_index" in a_o):
|
||||||
|
text_index = a_o[f"{utils.prefix()}_linked_textobject"]
|
||||||
|
glyph_index = a_o[f"{utils.prefix()}_glyph_index"]
|
||||||
|
return bpy.context.scene.abc3d_data.available_texts[text_index].glyphs[glyph_index]
|
||||||
|
else:
|
||||||
|
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):
|
||||||
|
for g in t.glyphs:
|
||||||
|
if butils.is_or_has_parent(a_o, g.glyph_object, max_depth=4):
|
||||||
|
return g
|
||||||
|
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()
|
||||||
|
@ -650,7 +780,7 @@ class ABC3D_PT_TextPropertiesPanel(bpy.types.Panel):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(self, context):
|
def poll(self, context):
|
||||||
return type(self.get_active_text_properties(self)) != type(None)
|
return self.get_active_text_properties(self) is not None
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
|
@ -659,11 +789,16 @@ class ABC3D_PT_TextPropertiesPanel(bpy.types.Panel):
|
||||||
abc3d_data = scene.abc3d_data
|
abc3d_data = scene.abc3d_data
|
||||||
|
|
||||||
props = self.get_active_text_properties()
|
props = self.get_active_text_properties()
|
||||||
|
glyph_props = self.get_active_glyph_properties()
|
||||||
|
|
||||||
if type(props) == type(None) or type(props.text_object) == type(None):
|
if props is None or props.text_object is None:
|
||||||
# this should not happen
|
# this should not happen
|
||||||
# as then polling does not work
|
# as then polling does not work
|
||||||
# however, we are paranoid
|
# however, we are paranoid
|
||||||
|
if props is None:
|
||||||
|
layout.label(text="props is none")
|
||||||
|
elif props.text_object is None:
|
||||||
|
layout.label(text="props.text_object is none")
|
||||||
return
|
return
|
||||||
|
|
||||||
layout.label(text=f"Mom: {props.text_object.name}")
|
layout.label(text=f"Mom: {props.text_object.name}")
|
||||||
|
@ -677,6 +812,13 @@ class ABC3D_PT_TextPropertiesPanel(bpy.types.Panel):
|
||||||
layout.column().prop(props, "translation")
|
layout.column().prop(props, "translation")
|
||||||
layout.column().prop(props, "orientation")
|
layout.column().prop(props, "orientation")
|
||||||
|
|
||||||
|
if glyph_props is None:
|
||||||
|
return
|
||||||
|
box = layout.box()
|
||||||
|
box.label(text=f"{glyph_props.glyph_id}")
|
||||||
|
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.
|
||||||
|
@ -923,6 +1065,72 @@ 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):
|
||||||
|
"""Align origins of selected objects to origin of active object on one axis."""
|
||||||
|
|
||||||
|
bl_idname = f"{__name__}.align_origins_to_active_object"
|
||||||
|
bl_label = "Align origins to Active Object"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
enum_axis = (('0','X',''),('1','Y',''),('2','Z',''))
|
||||||
|
axis: bpy.props.EnumProperty(items = enum_axis, default='2')
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
objects = bpy.context.selected_objects
|
||||||
|
butils.align_origins_to_active_object(objects, int(self.axis))
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
# class ABC3D_OT_AlignOriginsToMetrics(bpy.types.Operator):
|
||||||
|
# """Align origins of selected objects to their metrics left border.
|
||||||
|
|
||||||
|
# Be aware that shifting the origin will also shift the pivot point around which an object rotates.
|
||||||
|
|
||||||
|
# If an object does not have metrics, it will be ignored."""
|
||||||
|
|
||||||
|
# bl_idname = f"{__name__}.align_origins_to_metrics"
|
||||||
|
# bl_label = "Align origins to metrics metrics"
|
||||||
|
# bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
# ignore_warning: bpy.props.BoolProperty(
|
||||||
|
# name="Do not warn in the future",
|
||||||
|
# description="Do not warn in the future",
|
||||||
|
# default=False,
|
||||||
|
# )
|
||||||
|
|
||||||
|
# def draw(self, context):
|
||||||
|
# layout = self.layout
|
||||||
|
# layout.row().label(text="Warning!")
|
||||||
|
# layout.row().label(text="This also shifts the pivot point around which the glyph rotates.")
|
||||||
|
# layout.row().label(text="This may not be what you want.")
|
||||||
|
# layout.row().label(text="Glyph advance derives from metrics boundaries, not origin points.")
|
||||||
|
# layout.row().label(text="If you are sure about what you're doing, please continue.")
|
||||||
|
# layout.row().prop(self, "ignore_warning")
|
||||||
|
|
||||||
|
# def invoke(self, context, event):
|
||||||
|
# if not self.ignore_warning:
|
||||||
|
# wm = context.window_manager
|
||||||
|
# return wm.invoke_props_dialog(self)
|
||||||
|
# return self.execute(context)
|
||||||
|
|
||||||
|
# def execute(self, context):
|
||||||
|
# objects = bpy.context.selected_objects
|
||||||
|
# butils.align_origins_to_metrics(objects)
|
||||||
|
# butils.fix_objects_metrics_origins(objects)
|
||||||
|
# return {"FINISHED"}
|
||||||
|
|
||||||
|
# class ABC3D_OT_FixObjectsMetricsOrigins(bpy.types.Operator):
|
||||||
|
# """Align metrics origins of selected objects to their metrics bounding box.
|
||||||
|
|
||||||
|
# If an object does not have metrics, it will be ignored."""
|
||||||
|
|
||||||
|
# bl_idname = f"{__name__}.fix_objects_metrics_origins"
|
||||||
|
# bl_label = "Fix metrics origin of all selected objects"
|
||||||
|
# bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
# def execute(self, context):
|
||||||
|
# objects = bpy.context.selected_objects
|
||||||
|
# butils.fix_objects_metrics_origins(objects)
|
||||||
|
# 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."""
|
||||||
|
@ -1517,6 +1725,9 @@ classes = (
|
||||||
ABC3D_PT_TextPlacement,
|
ABC3D_PT_TextPlacement,
|
||||||
ABC3D_PT_TextManagement,
|
ABC3D_PT_TextManagement,
|
||||||
ABC3D_PT_FontCreation,
|
ABC3D_PT_FontCreation,
|
||||||
|
ABC3D_PG_FontCreation,
|
||||||
|
ABC3D_OT_NamingHelper,
|
||||||
|
ABC3D_PT_NamingHelper,
|
||||||
ABC3D_PT_TextPropertiesPanel,
|
ABC3D_PT_TextPropertiesPanel,
|
||||||
ABC3D_OT_OpenAssetDirectory,
|
ABC3D_OT_OpenAssetDirectory,
|
||||||
ABC3D_OT_LoadInstalledFonts,
|
ABC3D_OT_LoadInstalledFonts,
|
||||||
|
@ -1525,6 +1736,9 @@ classes = (
|
||||||
ABC3D_OT_RemoveMetrics,
|
ABC3D_OT_RemoveMetrics,
|
||||||
ABC3D_OT_AlignMetricsToActiveObject,
|
ABC3D_OT_AlignMetricsToActiveObject,
|
||||||
ABC3D_OT_AlignMetrics,
|
ABC3D_OT_AlignMetrics,
|
||||||
|
ABC3D_OT_AlignOriginsToActiveObject,
|
||||||
|
# ABC3D_OT_AlignOriginsToMetrics,
|
||||||
|
# ABC3D_OT_FixObjectsMetricsOrigins,
|
||||||
ABC3D_OT_TemporaryHelper,
|
ABC3D_OT_TemporaryHelper,
|
||||||
ABC3D_OT_RemoveText,
|
ABC3D_OT_RemoveText,
|
||||||
ABC3D_OT_PlaceText,
|
ABC3D_OT_PlaceText,
|
||||||
|
@ -1673,6 +1887,7 @@ 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.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
|
||||||
|
@ -1716,6 +1931,7 @@ def unregister():
|
||||||
bpy.app.handlers.depsgraph_update_post.remove(on_depsgraph_update)
|
bpy.app.handlers.depsgraph_update_post.remove(on_depsgraph_update)
|
||||||
|
|
||||||
del bpy.types.Scene.abc3d_data
|
del bpy.types.Scene.abc3d_data
|
||||||
|
del bpy.types.Scene.abc3d_font_creation
|
||||||
print(f"UNREGISTER {utils.prefix()}")
|
print(f"UNREGISTER {utils.prefix()}")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
""""""""""""""""""""""""""""""""" JEDI
|
|
||||||
|
|
||||||
let g:jedi#auto_initialization = 1
|
|
||||||
let g:jedi#use_tabs_not_buffers = 1
|
|
||||||
let g:jedi#environment_path = "venv"
|
|
||||||
|
|
||||||
""""""""""""""""""""""""""""""""" ALE
|
|
||||||
|
|
||||||
"let g:ale_python_pylint_executable = '/home/jrkb/git/pointer/neomatter/font3d/abc3d/venv/bin/pylint'
|
|
||||||
"let g:ale_python_executable='/home/jrkb/git/pointer/neomatter/font3d/abc3d/venv/bin/python'
|
|
||||||
"let g:ale_python_pylint_use_global=1
|
|
||||||
"let g:ale_use_global_executables=1
|
|
||||||
"let g:ale_python_auto_pipenv=1
|
|
||||||
"let g:ale_python_auto_virtualenv=1
|
|
||||||
"let g:ale_virtualenv_dir_names = ['venv']
|
|
||||||
|
|
||||||
"let g:ale_linters = { 'javascript': ['eslint', 'tsserver'], 'python': ['jedils', 'pylint', 'flake8'], 'cpp': ['cc', 'clangcheck', 'clangd', 'clangtidy', 'clazy', 'cppcheck', 'cpplint', 'cquery', 'cspell', 'flawfinder'], 'php': ['php_cs_fixer'] }
|
|
||||||
"let g:ale_fixers = { '*': ['remove_trailing_lines', 'trim_whitespace'], 'python': ['autopep8'], 'cpp': ['uncrustify'], 'javascript': js_fixers, 'css': ['prettier'], 'json': ['prettier'], 'php': ['php_cs_fixer'] }
|
|
||||||
|
|
||||||
"let g:ale_pattern_options = {'\.py$': {'ale_enabled': 0}}
|
|
442
butils.py
442
butils.py
|
@ -485,7 +485,7 @@ def load_font_from_filepath(filepath, glyphs="", font_name="", face_name=""):
|
||||||
|
|
||||||
modified_font_faces = []
|
modified_font_faces = []
|
||||||
all_glyph_os = []
|
all_glyph_os = []
|
||||||
all_objects = []
|
|
||||||
for o in bpy.context.scene.objects:
|
for o in bpy.context.scene.objects:
|
||||||
if marker_property in o:
|
if marker_property in o:
|
||||||
if "type" in o and o["type"] == "glyph":
|
if "type" in o and o["type"] == "glyph":
|
||||||
|
@ -511,17 +511,17 @@ def load_font_from_filepath(filepath, glyphs="", font_name="", face_name=""):
|
||||||
modified_font_faces.append({"font_name": font_name, "face_name": face_name})
|
modified_font_faces.append({"font_name": font_name, "face_name": face_name})
|
||||||
|
|
||||||
for mff in modified_font_faces:
|
for mff in modified_font_faces:
|
||||||
glyphs = []
|
mff_glyphs = []
|
||||||
face = Font.fonts[mff["font_name"]].faces[mff["face_name"]]
|
face = Font.fonts[mff["font_name"]].faces[mff["face_name"]]
|
||||||
# iterate glyphs
|
# iterate glyphs
|
||||||
for g in face.glyphs:
|
for g in face.glyphs:
|
||||||
# iterate alternates
|
# iterate alternates
|
||||||
for glyph in face.glyphs[g]:
|
for glyph in face.glyphs[g]:
|
||||||
glyphs.append(get_original(glyph))
|
mff_glyphs.append(get_original(glyph))
|
||||||
if len(glyphs) > 0:
|
if len(mff_glyphs) > 0:
|
||||||
add_default_metrics_to_objects(glyphs)
|
add_default_metrics_to_objects(mff_glyphs)
|
||||||
# calculate unit factor
|
# calculate unit factor
|
||||||
h = get_glyph_height(glyphs[0])
|
h = get_glyph_height(mff_glyphs[0])
|
||||||
if h != 0:
|
if h != 0:
|
||||||
face.unit_factor = 1 / h
|
face.unit_factor = 1 / h
|
||||||
|
|
||||||
|
@ -733,6 +733,12 @@ 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):
|
||||||
|
for c in glyph_obj.children:
|
||||||
|
if is_metrics_object(c):
|
||||||
|
return -1 * c.bound_box[0][0], c.bound_box[4][0]
|
||||||
|
return -1 * glyph_obj.bound_box[0][0], glyph_obj.bound_box[4][0]
|
||||||
|
|
||||||
|
|
||||||
def get_glyph_height(glyph_obj):
|
def get_glyph_height(glyph_obj):
|
||||||
for c in glyph_obj.children:
|
for c in glyph_obj.children:
|
||||||
|
@ -802,6 +808,41 @@ def will_regenerate(text_properties):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def update_matrices(obj):
|
||||||
|
if obj.parent is None:
|
||||||
|
obj.matrix_world = obj.matrix_basis
|
||||||
|
|
||||||
|
else:
|
||||||
|
obj.matrix_world = obj.parent.matrix_world * \
|
||||||
|
obj.matrix_parent_inverse * \
|
||||||
|
obj.matrix_basis
|
||||||
|
|
||||||
|
def is_or_has_parent(o, parent, if_is_parent=True, max_depth=10):
|
||||||
|
if o == parent and if_is_parent:
|
||||||
|
return True
|
||||||
|
for i in range(0, max_depth):
|
||||||
|
o = o.parent
|
||||||
|
if o == parent:
|
||||||
|
return True
|
||||||
|
if o is None:
|
||||||
|
return False
|
||||||
|
return False
|
||||||
|
|
||||||
|
def parent_to_curve(o, c):
|
||||||
|
o.parent_type = 'OBJECT'
|
||||||
|
o.parent = c
|
||||||
|
# o.matrix_parent_inverse = c.matrix_world.inverted()
|
||||||
|
|
||||||
|
if c.data.use_path and len(c.data.splines) > 0:
|
||||||
|
if c.data.splines[0].type == "BEZIER":
|
||||||
|
i = -1 if c.data.splines[0].use_cyclic_u else 0
|
||||||
|
p = c.data.splines[0].bezier_points[i].co
|
||||||
|
o.matrix_parent_inverse.translation = p * -1.0
|
||||||
|
elif c.data.splines[0].type == 'NURBS':
|
||||||
|
cm = c.to_mesh()
|
||||||
|
p = cm.vertices[0].co
|
||||||
|
o.matrix_parent_inverse.translation = p * -1.0
|
||||||
|
|
||||||
def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4):
|
def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4):
|
||||||
"""set_text_on_curve
|
"""set_text_on_curve
|
||||||
|
|
||||||
|
@ -815,8 +856,9 @@ def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4)
|
||||||
:param reset_depsgraph_n: reset external parameters after n-th depsgraph update. (<= 0) = immediate, (> 0) = reset after n-th depsgraph update, (False) = no depsgraph reset
|
:param reset_depsgraph_n: reset external parameters after n-th depsgraph update. (<= 0) = immediate, (> 0) = reset after n-th depsgraph update, (False) = no depsgraph reset
|
||||||
:type reset_depsgraph_n: int
|
:type reset_depsgraph_n: int
|
||||||
"""
|
"""
|
||||||
|
# NOTE: depsgraph update not locked
|
||||||
global lock_depsgraph_update_n_times
|
# as we fixed data_path with parent_to_curve trick
|
||||||
|
# global lock_depsgraph_update_n_times
|
||||||
|
|
||||||
# starttime = time.perf_counter_ns()
|
# starttime = time.perf_counter_ns()
|
||||||
mom = text_properties.text_object
|
mom = text_properties.text_object
|
||||||
|
@ -825,28 +867,44 @@ 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
|
||||||
|
# as we fixed data_path with parent_to_curve trick
|
||||||
|
#
|
||||||
# use_path messes with parenting
|
# use_path messes with parenting
|
||||||
# however, we need it for follow_path
|
# however, we need it for follow_path
|
||||||
# https://projects.blender.org/blender/blender/issues/100661
|
# https://projects.blender.org/blender/blender/issues/100661
|
||||||
previous_use_path = mom.data.use_path
|
# previous_use_path = mom.data.use_path
|
||||||
if distribution_type == "CALCULATE":
|
# if distribution_type == "CALCULATE":
|
||||||
mom.data.use_path = False
|
# mom.data.use_path = False
|
||||||
elif distribution_type == "FOLLOW_PATH":
|
# elif distribution_type == "FOLLOW_PATH":
|
||||||
mom.data.use_path = True
|
# mom.data.use_path = True
|
||||||
|
|
||||||
regenerate = will_regenerate(text_properties)
|
regenerate = will_regenerate(text_properties)
|
||||||
|
|
||||||
# if we regenerate.... delete objects
|
# if we regenerate.... delete objects
|
||||||
if regenerate and text_properties.get("glyphs"):
|
if regenerate and text_properties.get("glyphs"):
|
||||||
|
for g in text_properties.glyphs:
|
||||||
|
print(dict(g))
|
||||||
glyph_objects = [g["glyph_object"] for g in text_properties["glyphs"]]
|
glyph_objects = [g["glyph_object"] for g in text_properties["glyphs"]]
|
||||||
completely_delete_objects(glyph_objects, True)
|
completely_delete_objects(glyph_objects, True)
|
||||||
text_properties.glyphs.clear()
|
text_properties.glyphs.clear()
|
||||||
|
|
||||||
|
mom[f"{utils.prefix()}_type"] = "textobject"
|
||||||
|
mom[f"{utils.prefix()}_linked_textobject"] = text_properties.text_id
|
||||||
|
mom[f"{utils.prefix()}_font_name"] = text_properties.font_name
|
||||||
|
mom[f"{utils.prefix()}_face_name"] = text_properties.face_name
|
||||||
|
mom[f"{utils.prefix()}_font_size"] = text_properties.font_size
|
||||||
|
mom[f"{utils.prefix()}_letter_spacing"] = text_properties.letter_spacing
|
||||||
|
mom[f"{utils.prefix()}_orientation"] = text_properties.orientation
|
||||||
|
mom[f"{utils.prefix()}_translation"] = text_properties.translation
|
||||||
|
|
||||||
curve_length = get_curve_length(mom)
|
curve_length = get_curve_length(mom)
|
||||||
advance = text_properties.offset
|
advance = text_properties.offset
|
||||||
glyph_advance = 0
|
glyph_advance = 0
|
||||||
|
glyph_index = 0
|
||||||
is_command = False
|
is_command = False
|
||||||
previous_spline_index = -1
|
previous_spline_index = -1
|
||||||
|
|
||||||
for i, c in enumerate(text_properties.text):
|
for i, c in enumerate(text_properties.text):
|
||||||
face = Font.fonts[text_properties.font_name].faces[text_properties.face_name]
|
face = Font.fonts[text_properties.font_name].faces[text_properties.face_name]
|
||||||
scalor = face.unit_factor * text_properties.font_size
|
scalor = face.unit_factor * text_properties.font_size
|
||||||
|
@ -859,7 +917,6 @@ def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4)
|
||||||
is_newline = True
|
is_newline = True
|
||||||
next_line_advance = get_next_line_advance(mom, advance, glyph_advance)
|
next_line_advance = get_next_line_advance(mom, advance, glyph_advance)
|
||||||
if advance == next_line_advance:
|
if advance == next_line_advance:
|
||||||
# self.report({'INFO'}, f"would like to add new line for {text_properties.text} please")
|
|
||||||
print(
|
print(
|
||||||
f"would like to add new line for {text_properties.text} please"
|
f"would like to add new line for {text_properties.text} please"
|
||||||
)
|
)
|
||||||
|
@ -869,9 +926,14 @@ def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4)
|
||||||
is_command = False
|
is_command = False
|
||||||
glyph_id = c
|
glyph_id = c
|
||||||
|
|
||||||
glyph_tmp = Font.get_glyph(
|
spline_index = 0
|
||||||
text_properties.font_name, text_properties.face_name, glyph_id
|
|
||||||
)
|
############### GET GLYPH
|
||||||
|
|
||||||
|
glyph_tmp = Font.get_glyph(text_properties.font_name,
|
||||||
|
text_properties.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:
|
||||||
|
@ -886,6 +948,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
|
||||||
)
|
)
|
||||||
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}')"
|
||||||
|
@ -899,61 +962,85 @@ def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4)
|
||||||
)
|
)
|
||||||
if not replaced:
|
if not replaced:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
glyph = glyph_tmp.original
|
glyph = glyph_tmp.original
|
||||||
|
|
||||||
ob = None
|
############### GLYPH PROPERTIES
|
||||||
obg = None
|
|
||||||
|
glyph_properties = text_properties.glyphs[glyph_index] if not regenerate else text_properties.glyphs.add()
|
||||||
|
|
||||||
if regenerate:
|
if regenerate:
|
||||||
ob = bpy.data.objects.new(f"{glyph_id}", None)
|
glyph_properties["glyph_id"] = glyph_id
|
||||||
ob.hide_viewport = True
|
glyph_properties["text_id"] = text_properties.text_id
|
||||||
obg = bpy.data.objects.new(f"{glyph_id}_mesh", glyph.data)
|
glyph_properties["letter_spacing"] = 0
|
||||||
ob[f"{utils.prefix()}_type"] = "glyph"
|
|
||||||
ob[f"{utils.prefix()}_linked_textobject"] = text_properties.text_id
|
############### NODE SCENE MANAGEMENT
|
||||||
ob[f"{utils.prefix()}_font_name"] = text_properties.font_name
|
|
||||||
ob[f"{utils.prefix()}_face_name"] = text_properties.face_name
|
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)
|
||||||
|
outer_node[f"{utils.prefix()}_type"] = "glyph"
|
||||||
|
outer_node[f"{utils.prefix()}_linked_textobject"] = text_properties.text_id
|
||||||
|
outer_node[f"{utils.prefix()}_glyph_index"] = glyph_index
|
||||||
|
outer_node[f"{utils.prefix()}_font_name"] = text_properties.font_name
|
||||||
|
outer_node[f"{utils.prefix()}_face_name"] = text_properties.face_name
|
||||||
|
|
||||||
|
# Add into the scene.
|
||||||
|
mom.users_collection[0].objects.link(outer_node)
|
||||||
|
mom.users_collection[0].objects.link(inner_node)
|
||||||
|
# bpy.context.scene.collection.objects.link(inner_node)
|
||||||
|
|
||||||
|
# Parenting is hard.
|
||||||
|
inner_node.parent_type = 'OBJECT'
|
||||||
|
inner_node.parent = outer_node
|
||||||
|
inner_node.matrix_parent_inverse = outer_node.matrix_world.inverted()
|
||||||
|
parent_to_curve(outer_node, mom)
|
||||||
|
|
||||||
|
glyph_properties["glyph_object"] = outer_node
|
||||||
else:
|
else:
|
||||||
ob = text_properties.glyphs[i].glyph_object
|
outer_node = glyph_properties.glyph_object
|
||||||
for c in ob.children:
|
outer_node[f"{utils.prefix()}_glyph_index"] = glyph_index
|
||||||
|
for c in outer_node.children:
|
||||||
if c.name.startswith(f"{glyph_id}_mesh"):
|
if c.name.startswith(f"{glyph_id}_mesh"):
|
||||||
obg = c
|
inner_node = c
|
||||||
|
|
||||||
|
############### TRANSFORMS
|
||||||
|
|
||||||
|
# origins could be shifted
|
||||||
|
# so we need to apply a pre_advance
|
||||||
|
glyph_pre_advance, glyph_post_advance = get_glyph_prepost_advances(glyph)
|
||||||
|
advance += glyph_pre_advance * scalor
|
||||||
|
|
||||||
if distribution_type == "FOLLOW_PATH":
|
if distribution_type == "FOLLOW_PATH":
|
||||||
ob.constraints.new(type="FOLLOW_PATH")
|
outer_node.constraints.new(type="FOLLOW_PATH")
|
||||||
ob.constraints["Follow Path"].target = mom
|
outer_node.constraints["Follow Path"].target = mom
|
||||||
ob.constraints["Follow Path"].use_fixed_location = True
|
outer_node.constraints["Follow Path"].use_fixed_location = True
|
||||||
ob.constraints["Follow Path"].offset_factor = advance / curve_length
|
outer_node.constraints["Follow Path"].offset_factor = advance / curve_length
|
||||||
ob.constraints["Follow Path"].use_curve_follow = True
|
outer_node.constraints["Follow Path"].use_curve_follow = True
|
||||||
ob.constraints["Follow Path"].forward_axis = "FORWARD_X"
|
outer_node.constraints["Follow Path"].forward_axis = "FORWARD_X"
|
||||||
ob.constraints["Follow Path"].up_axis = "UP_Y"
|
outer_node.constraints["Follow Path"].up_axis = "UP_Y"
|
||||||
spline_index = 0
|
spline_index = 0
|
||||||
elif distribution_type == "CALCULATE":
|
elif distribution_type == "CALCULATE":
|
||||||
previous_ob_rotation_mode = None
|
previous_outer_node_rotation_mode = None
|
||||||
previous_obg_rotation_mode = None
|
previous_inner_node_rotation_mode = None
|
||||||
if ob.rotation_mode != "QUATERNION":
|
if outer_node.rotation_mode != "QUATERNION":
|
||||||
ob.rotation_mode = "QUATERNION"
|
outer_node.rotation_mode = "QUATERNION"
|
||||||
previous_ob_rotation_mode = ob.rotation_mode
|
previous_outer_node_rotation_mode = outer_node.rotation_mode
|
||||||
if obg.rotation_mode != "QUATERNION":
|
if inner_node.rotation_mode != "QUATERNION":
|
||||||
obg.rotation_mode = "QUATERNION"
|
inner_node.rotation_mode = "QUATERNION"
|
||||||
previous_obg_rotation_mode = obg.rotation_mode
|
previous_inner_node_rotation_mode = inner_node.rotation_mode
|
||||||
|
|
||||||
location, tangent, spline_index = calc_point_on_bezier_curve(
|
# get info from bezier
|
||||||
mom, advance, True, True
|
location, tangent, spline_index = calc_point_on_bezier_curve(mom, advance, True, True)
|
||||||
)
|
|
||||||
|
# check if we are on a new line
|
||||||
if spline_index != previous_spline_index:
|
if spline_index != previous_spline_index:
|
||||||
is_newline = True
|
is_newline = True
|
||||||
|
|
||||||
if regenerate:
|
# position
|
||||||
# ob.location = mom.matrix_world @ (
|
outer_node.location = location + text_properties.translation
|
||||||
# location + text_properties.translation
|
|
||||||
# )
|
|
||||||
ob.location = location + text_properties.translation
|
|
||||||
mom.users_collection[0].objects.link(obg)
|
|
||||||
mom.users_collection[0].objects.link(ob)
|
|
||||||
ob.parent = mom
|
|
||||||
obg.parent = ob
|
|
||||||
obg.location = mathutils.Vector((0.0, 0.0, 0.0))
|
|
||||||
else:
|
|
||||||
ob.location = location + text_properties.translation
|
|
||||||
|
|
||||||
# orientation / rotation
|
# orientation / rotation
|
||||||
mask = [0]
|
mask = [0]
|
||||||
|
@ -967,26 +1054,28 @@ def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4)
|
||||||
|
|
||||||
q = mathutils.Quaternion()
|
q = mathutils.Quaternion()
|
||||||
q.rotate(text_properties.orientation)
|
q.rotate(text_properties.orientation)
|
||||||
ob.rotation_quaternion = (motor[0].to_3x3() @ q.to_matrix()).to_quaternion()
|
outer_node.rotation_quaternion = (motor[0].to_3x3() @ q.to_matrix()).to_quaternion()
|
||||||
# if regenerate:
|
|
||||||
# obg.rotation_quaternion = q
|
|
||||||
# ob.rotation_quaternion = (
|
|
||||||
# mom.matrix_world @ motor[0]
|
|
||||||
# ).to_quaternion()
|
|
||||||
# else:
|
|
||||||
# ob.rotation_quaternion = motor[0].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:
|
||||||
# ob.rotation_quaternion = (mom.matrix_world.inverted().to_3x3() @ motor[0].to_3x3() @ q.to_matrix()).to_quaternion()
|
# outer_node.rotation_quaternion = (mom.matrix_world.inverted().to_3x3() @ motor[0].to_3x3() @ q.to_matrix()).to_quaternion()
|
||||||
|
|
||||||
if previous_ob_rotation_mode:
|
# # scale
|
||||||
ob.rotation_mode = previous_ob_rotation_mode
|
outer_node.scale = (scalor, scalor, scalor)
|
||||||
if previous_obg_rotation_mode:
|
|
||||||
obg.rotation_mode = previous_obg_rotation_mode
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
# outer_node.hide_viewport = True
|
||||||
|
outer_node.hide_set(True)
|
||||||
|
|
||||||
|
############### PREPARE FOR THE NEXT
|
||||||
|
|
||||||
|
print(f"{glyph_id}: {glyph_properties.letter_spacing=}")
|
||||||
glyph_advance = (
|
glyph_advance = (
|
||||||
get_glyph_advance(glyph) * scalor + text_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
|
||||||
|
@ -995,7 +1084,7 @@ def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4)
|
||||||
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
|
||||||
): # TODO: fix newline hack
|
):
|
||||||
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
|
||||||
|
@ -1025,67 +1114,46 @@ def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4)
|
||||||
output_spline_index=True,
|
output_spline_index=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
ob.scale = (scalor, scalor, scalor)
|
|
||||||
|
|
||||||
advance = advance + glyph_advance + curve_compensation
|
advance = advance + glyph_advance + curve_compensation
|
||||||
|
glyph_index += 1
|
||||||
previous_spline_index = spline_index
|
previous_spline_index = spline_index
|
||||||
|
|
||||||
if regenerate:
|
# NOTE: depsgraph update not locked
|
||||||
glyph_data = text_properties.glyphs.add()
|
# as we fixed data_path with parent_to_curve trick
|
||||||
glyph_data.glyph_id = glyph_id
|
# if lock_depsgraph_update_n_times < 0:
|
||||||
glyph_data.glyph_object = ob
|
# lock_depsgraph_update_n_times = len(
|
||||||
glyph_data.letter_spacing = 0
|
# bpy.context.selected_objects
|
||||||
|
# )
|
||||||
if regenerate:
|
# else:
|
||||||
mom[f"{utils.prefix()}_type"] = "textobject"
|
# lock_depsgraph_update_n_times += len(
|
||||||
mom[f"{utils.prefix()}_linked_textobject"] = text_properties.text_id
|
# bpy.context.selected_objects
|
||||||
mom[f"{utils.prefix()}_font_name"] = text_properties.font_name
|
# )
|
||||||
mom[f"{utils.prefix()}_face_name"] = text_properties.face_name
|
# # NOTE: we reset with a timeout, as setting and resetting certain things
|
||||||
mom[f"{utils.prefix()}_font_size"] = text_properties.font_size
|
# # in fast succession will cause visual glitches (e.g. {}.data.use_path).
|
||||||
mom[f"{utils.prefix()}_letter_spacing"] = text_properties.letter_spacing
|
# def reset():
|
||||||
mom[f"{utils.prefix()}_orientation"] = text_properties.orientation
|
# mom.data.use_path = previous_use_path
|
||||||
mom[f"{utils.prefix()}_translation"] = text_properties.translation
|
# if counted_reset in bpy.app.handlers.depsgraph_update_post:
|
||||||
|
# bpy.app.handlers.depsgraph_update_post.remove(counted_reset)
|
||||||
if lock_depsgraph_update_n_times < 0:
|
# if bpy.app.timers.is_registered(reset):
|
||||||
lock_depsgraph_update_n_times = len(
|
# bpy.app.timers.unregister(reset)
|
||||||
bpy.context.selected_objects
|
# molotov = reset_depsgraph_n + 0
|
||||||
)
|
# def counted_reset(scene, depsgraph):
|
||||||
else:
|
# nonlocal molotov
|
||||||
lock_depsgraph_update_n_times += len(
|
# if molotov == 0:
|
||||||
bpy.context.selected_objects
|
# reset()
|
||||||
)
|
# else:
|
||||||
|
# molotov -= 1
|
||||||
# NOTE: we reset with a timeout, as setting and resetting certain things
|
# # unregister previous resets to avoid multiple execution
|
||||||
# in fast succession will cause visual glitches (e.g. {}.data.use_path).
|
# if bpy.app.timers.is_registered(reset):
|
||||||
def reset():
|
# bpy.app.timers.unregister(reset)
|
||||||
mom.data.use_path = previous_use_path
|
# if counted_reset in bpy.app.handlers.depsgraph_update_post:
|
||||||
if counted_reset in bpy.app.handlers.depsgraph_update_post:
|
# bpy.app.handlers.depsgraph_update_post.remove(counted_reset)
|
||||||
bpy.app.handlers.depsgraph_update_post.remove(counted_reset)
|
# if not isinstance(reset_timeout_s, bool):
|
||||||
if bpy.app.timers.is_registered(reset):
|
# if reset_timeout_s > 0:
|
||||||
bpy.app.timers.unregister(reset)
|
# bpy.app.timers.register(reset, first_interval=reset_timeout_s)
|
||||||
|
# elif reset_timeout <= 0:
|
||||||
molotov = reset_depsgraph_n + 0
|
# reset()
|
||||||
|
# bpy.app.handlers.depsgraph_update_post.append(counted_reset)
|
||||||
def counted_reset(scene, depsgraph):
|
|
||||||
nonlocal molotov
|
|
||||||
if molotov == 0:
|
|
||||||
reset()
|
|
||||||
else:
|
|
||||||
molotov -= 1
|
|
||||||
|
|
||||||
# unregister previous resets to avoid multiple execution
|
|
||||||
if bpy.app.timers.is_registered(reset):
|
|
||||||
bpy.app.timers.unregister(reset)
|
|
||||||
if counted_reset in bpy.app.handlers.depsgraph_update_post:
|
|
||||||
bpy.app.handlers.depsgraph_update_post.remove(counted_reset)
|
|
||||||
|
|
||||||
if not isinstance(reset_timeout_s, bool):
|
|
||||||
if reset_timeout_s > 0:
|
|
||||||
bpy.app.timers.register(reset, first_interval=reset_timeout_s)
|
|
||||||
elif reset_timeout <= 0:
|
|
||||||
reset()
|
|
||||||
|
|
||||||
bpy.app.handlers.depsgraph_update_post.append(counted_reset)
|
|
||||||
|
|
||||||
# endtime = time.perf_counter_ns()
|
# endtime = time.perf_counter_ns()
|
||||||
# elapsedtime = endtime - starttime
|
# elapsedtime = endtime - starttime
|
||||||
|
@ -1502,3 +1570,109 @@ 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):
|
||||||
|
if objects is None:
|
||||||
|
objects = bpy.context.selected_objects
|
||||||
|
if len(objects) == 0:
|
||||||
|
return "no objects selected"
|
||||||
|
|
||||||
|
if bpy.context.active_object is None:
|
||||||
|
return "no active object selected"
|
||||||
|
|
||||||
|
reference_origin_position = bpy.context.active_object.matrix_world.translation[axis]
|
||||||
|
|
||||||
|
# do it
|
||||||
|
for o in objects:
|
||||||
|
is_possibly_glyph = is_glyph(o)
|
||||||
|
if is_possibly_glyph and o is not bpy.context.active_object:
|
||||||
|
if is_mesh(o):
|
||||||
|
diff = reference_origin_position - o.matrix_world.translation[axis]
|
||||||
|
|
||||||
|
for v in o.data.vertices:
|
||||||
|
v.co[axis] -= diff
|
||||||
|
|
||||||
|
o.matrix_world.translation[axis] = reference_origin_position
|
||||||
|
|
||||||
|
return ""
|
||||||
|
|
||||||
|
# NOTE:
|
||||||
|
# Following code is not necessary anymore,
|
||||||
|
# as we derive the advance through metrics
|
||||||
|
# boundaries
|
||||||
|
|
||||||
|
# def divide_vectors(v1=mathutils.Vector((1.0,1.0,1.0)), v2=mathutils.Vector((1.0,1.0,1.0))):
|
||||||
|
# return mathutils.Vector([v1[i] / v2[i] for i in range(3)])
|
||||||
|
|
||||||
|
# def get_origin_shift_metrics(o, axis=0):
|
||||||
|
# if not is_metrics_object(o):
|
||||||
|
# return False
|
||||||
|
# min_value = sys.float_info.max
|
||||||
|
# for v in o.data.vertices:
|
||||||
|
# if v.co[axis] < min_value:
|
||||||
|
# min_value = v.co[axis]
|
||||||
|
# if min_value == sys.float_info.max:
|
||||||
|
# return False
|
||||||
|
# return min_value
|
||||||
|
|
||||||
|
# def fix_origin_shift_metrics(o, axis=0):
|
||||||
|
# shift = get_origin_shift_metrics(o)
|
||||||
|
# if not shift:
|
||||||
|
# print("False")
|
||||||
|
# return False
|
||||||
|
# for v in o.data.vertices:
|
||||||
|
# v.co[axis] -= shift
|
||||||
|
# shift_vector = mathutils.Vector((0.0, 0.0, 0.0))
|
||||||
|
# shift_vector[axis] = shift
|
||||||
|
# # o.location = o.location - (divide_vectors(v2=o.matrix_world.to_scale()) * (o.matrix_world @ shift_vector))
|
||||||
|
# o.matrix_local.translation = o.matrix_local.translation + (shift_vector @ o.matrix_local.inverted())
|
||||||
|
# # update_matrices(o)
|
||||||
|
# return True
|
||||||
|
|
||||||
|
|
||||||
|
# def fix_objects_metrics_origins(objects=None, axis=0, handle_metrics_directly=True):
|
||||||
|
# if objects is None:
|
||||||
|
# objects = bpy.context.selected_objects
|
||||||
|
# if len(objects) == 0:
|
||||||
|
# return "no objects selected"
|
||||||
|
|
||||||
|
# for o in objects:
|
||||||
|
# is_possibly_glyph = is_glyph(o)
|
||||||
|
# if is_possibly_glyph:
|
||||||
|
# for c in o.children:
|
||||||
|
# if is_metrics_object(c):
|
||||||
|
# fix_origin_shift_metrics(c, axis)
|
||||||
|
# elif is_metrics_object(o) and handle_metrics_directly:
|
||||||
|
# fix_origin_shift_metrics(o, axis)
|
||||||
|
# return ""
|
||||||
|
|
||||||
|
# def align_origins_to_metrics(objects=None):
|
||||||
|
# if objects is None:
|
||||||
|
# objects = bpy.context.selected_objects
|
||||||
|
# if len(objects) == 0:
|
||||||
|
# return "no objects selected"
|
||||||
|
|
||||||
|
# for o in objects:
|
||||||
|
# is_possibly_glyph = is_glyph(o)
|
||||||
|
# if is_possibly_glyph:
|
||||||
|
# min_x = 9999999999
|
||||||
|
# for c in o.children:
|
||||||
|
# if is_metrics_object(c):
|
||||||
|
# for v in c.data.vertices:
|
||||||
|
# if v.co[0] < min_x:
|
||||||
|
# min_x = v.co[0]
|
||||||
|
|
||||||
|
# metrics_origin_x = c.matrix_world.translation[0] + min_x
|
||||||
|
|
||||||
|
# diff = metrics_origin_x - o.matrix_world.translation[0]
|
||||||
|
|
||||||
|
# for v in o.data.vertices:
|
||||||
|
# v.co[0] -= diff
|
||||||
|
|
||||||
|
# o.location += mathutils.Vector((diff, 0.0, 0.0)) @ o.matrix_world.inverted()
|
||||||
|
|
||||||
|
# for c in o.children:
|
||||||
|
# if is_metrics_object(c):
|
||||||
|
# c.location -= mathutils.Vector((diff, 0.0, 0.0)) @ o.matrix_world.inverted()
|
||||||
|
|
||||||
|
# return ""
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
from typing import TypedDict
|
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from dataclasses import dataclass
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
# convenience dictionary for translating names to glyph ids
|
# convenience dictionary for translating names to glyph ids
|
||||||
|
@ -45,9 +43,9 @@ known_misspellings = {
|
||||||
"overdot": "dotaccent",
|
"overdot": "dotaccent",
|
||||||
"diaresis": "dieresis",
|
"diaresis": "dieresis",
|
||||||
"diaeresis": "dieresis",
|
"diaeresis": "dieresis",
|
||||||
|
# different conventions
|
||||||
|
"doubleacute": "hungarumlaut",
|
||||||
# character does not exist.. maybe something else
|
# character does not exist.. maybe something else
|
||||||
"Odoubleacute": "Ohungarumlaut",
|
|
||||||
"Udoubleacute": "Uhungarumlaut",
|
|
||||||
"Wcaron": "Wcircumflex",
|
"Wcaron": "Wcircumflex",
|
||||||
"Neng": "Nlongrightleg",
|
"Neng": "Nlongrightleg",
|
||||||
"Lgrave": "Lacute",
|
"Lgrave": "Lacute",
|
||||||
|
@ -77,6 +75,13 @@ def name_to_glyph(name):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def glyph_to_name(glyph_id):
|
||||||
|
for k in name_to_glyph_d:
|
||||||
|
if glyph_id == name_to_glyph_d[k]:
|
||||||
|
return k
|
||||||
|
return glyph_id
|
||||||
|
|
||||||
|
|
||||||
def is_space(character):
|
def is_space(character):
|
||||||
for name in space_d:
|
for name in space_d:
|
||||||
if character == space_d[name][0]:
|
if character == space_d[name][0]:
|
||||||
|
|
|
@ -8,7 +8,7 @@ def get_version_minor():
|
||||||
|
|
||||||
|
|
||||||
def get_version_patch():
|
def get_version_patch():
|
||||||
return 5
|
return 6
|
||||||
|
|
||||||
|
|
||||||
def get_version_string():
|
def get_version_string():
|
||||||
|
@ -22,7 +22,6 @@ def prefix():
|
||||||
import datetime
|
import datetime
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from mathutils import Vector
|
|
||||||
|
|
||||||
|
|
||||||
def get_timestamp():
|
def get_timestamp():
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue