Compare commits

...

12 commits

Author SHA1 Message Date
themancalledjakob
36ae68761d cosmetics 2024-11-05 16:03:09 +01:00
themancalledjakob
56904287a3 fix import
forgotten to add to older commit
2024-11-05 16:02:58 +01:00
themancalledjakob
a7e6bdf082 nothing
for local development, documenting optional setup
2024-11-05 16:01:52 +01:00
themancalledjakob
30251a635f cosmetics 2024-11-05 16:01:15 +01:00
themancalledjakob
4a10584710 possibly better description 2024-11-05 16:00:52 +01:00
themancalledjakob
77fdf7d93a add auto_updater 2024-11-05 15:45:50 +01:00
themancalledjakob
5e74787bb0 documentation 2024-11-05 15:42:14 +01:00
themancalledjakob
c302676ae3 bump version 2024-10-31 19:34:47 +01:00
themancalledjakob
e8fd0d8243 reset text on depsgraph update 2024-10-31 19:34:31 +01:00
themancalledjakob
b6d76ae958 update types automatically 2024-10-31 19:33:49 +01:00
themancalledjakob
e5e8a1b053 open asset directory 2024-10-31 19:33:22 +01:00
themancalledjakob
dfd08de27d fix offset when altering first point
fix #2
2024-10-31 19:31:45 +01:00
8 changed files with 3893 additions and 342 deletions

1
.gitignore vendored
View file

@ -5,3 +5,4 @@ venv
# vim # vim
*.swo *.swo
*.swp *.swp
/abc3d_updater/*

View file

@ -5,6 +5,12 @@ source venv/bin/activate
pip install bpy pip install bpy
``` ```
to install mathutils, this was necessary for me:
```
sudo xbps-install -Sy python3.11-devel
CFLAGS=$(python3.11-config --cflags) LDFLAGS=$(python3.11-config --ldflags) pip install mathutils
```
# install addon: # install addon:
```bash ```bash
cd <root directory> cd <root directory>

View file

@ -4,13 +4,21 @@
A 3D font helper A 3D font helper
""" """
import os
from bpy.app.handlers import persistent
from bpy.types import Panel
import functools
import io
import bpy
import importlib
bl_info = { bl_info = {
"name": "ABC3D", "name": "ABC3D",
"author": "Jakob Schlötter, Studio Pointer*", "author": "Jakob Schlötter, Studio Pointer*",
"version": (0, 0, 1), "version": (0, 0, 2),
"blender": (4, 1, 0), "blender": (4, 1, 0),
"location": "VIEW3D", "location": "VIEW3D",
"description": "Does ABC3D stuff", "description": "Convenience addon for 3D fonts",
"category": "Typography", "category": "Typography",
} }
@ -18,39 +26,26 @@ bl_info = {
# when registering # when registering
# handy for development # handy for development
# first import dependencies for the method # first import dependencies for the method
import importlib if "Font" in locals():
# then import dependencies for our addon
if "bpy" in locals():
importlib.reload(Font) importlib.reload(Font)
importlib.reload(utils) importlib.reload(utils)
importlib.reload(butils) importlib.reload(butils)
importlib.reload(bimport) importlib.reload(bimport)
importlib.reload(addon_updater_ops)
else: else:
from .common import Font from .common import Font
from .common import utils from .common import utils
from . import butils from . import butils
from . import bimport from . import bimport
from . import addon_updater_ops
import bpy
import math
import mathutils
import io
import functools
from bpy.types import Panel
from bpy.app.handlers import persistent
from random import uniform
import time
import datetime
import os
import re
def getPreferences(context): def getPreferences(context):
preferences = context.preferences preferences = context.preferences
return preferences.addons[__name__].preferences return preferences.addons[__name__].preferences
@addon_updater_ops.make_annotations
class ABC3D_addonPreferences(bpy.types.AddonPreferences): class ABC3D_addonPreferences(bpy.types.AddonPreferences):
"""ABC3D Addon Preferences """ABC3D Addon Preferences
@ -58,6 +53,39 @@ class ABC3D_addonPreferences(bpy.types.AddonPreferences):
bl_idname = __name__ bl_idname = __name__
# Addon updater preferences.
auto_check_update = bpy.props.BoolProperty(
name="Auto-check for Update",
description="If enabled, auto-check for updates using an interval",
default=False)
updater_interval_months = bpy.props.IntProperty(
name='Months',
description="Number of months between checking for updates",
default=0,
min=0)
updater_interval_days = bpy.props.IntProperty(
name='Days',
description="Number of days between checking for updates",
default=7,
min=0,
max=31)
updater_interval_hours = bpy.props.IntProperty(
name='Hours',
description="Number of hours between checking for updates",
default=0,
min=0,
max=23)
updater_interval_minutes = bpy.props.IntProperty(
name='Minutes',
description="Number of minutes between checking for updates",
default=0,
min=0,
max=59)
def get_default_assets_dir(): def get_default_assets_dir():
return bpy.utils.user_resource( return bpy.utils.user_resource(
'DATAFILES', 'DATAFILES',
@ -92,6 +120,13 @@ class ABC3D_addonPreferences(bpy.types.AddonPreferences):
layout.label(text="Directory for storage of fonts and other assets:") layout.label(text="Directory for storage of fonts and other assets:")
layout.prop(self, "assets_dir") layout.prop(self, "assets_dir")
# Works best if a column, or even just self.layout.
mainrow = layout.row()
col = mainrow.column()
# Updater draw function, could also pass in col as third arg.
addon_updater_ops.update_settings_ui(self, context)
class ABC3D_available_font(bpy.types.PropertyGroup): class ABC3D_available_font(bpy.types.PropertyGroup):
font_name: bpy.props.StringProperty(name="") font_name: bpy.props.StringProperty(name="")
@ -207,8 +242,12 @@ class ABC3D_text_properties(bpy.types.PropertyGroup):
glyphs: bpy.props.CollectionProperty(type=ABC3D_glyph_properties) glyphs: bpy.props.CollectionProperty(type=ABC3D_glyph_properties)
# TODO: simply, merge, cut cut cut # TODO: simply, merge, cut cut cut
class ABC3D_data(bpy.types.PropertyGroup): class ABC3D_data(bpy.types.PropertyGroup):
available_fonts: bpy.props.CollectionProperty(type=ABC3D_available_font, name="Available fonts") available_fonts: bpy.props.CollectionProperty(
type=ABC3D_available_font, name="Available fonts")
def active_font_index_update(self, context): def active_font_index_update(self, context):
if len(self.available_fonts) <= self.active_font_index: if len(self.available_fonts) <= self.active_font_index:
self.active_font_index = len(self.available_fonts) - 1 self.active_font_index = len(self.available_fonts) - 1
@ -217,7 +256,9 @@ class ABC3D_data(bpy.types.PropertyGroup):
default=-1, default=-1,
update=active_font_index_update, update=active_font_index_update,
) )
available_texts: bpy.props.CollectionProperty(type=ABC3D_text_properties, name="Available texts") available_texts: bpy.props.CollectionProperty(
type=ABC3D_text_properties, name="Available texts")
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:
o = self.available_texts[self.active_text_index].text_object o = self.available_texts[self.active_text_index].text_object
@ -253,20 +294,24 @@ class ABC3D_data(bpy.types.PropertyGroup):
class ABC3D_UL_fonts(bpy.types.UIList): class ABC3D_UL_fonts(bpy.types.UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
layout.label(text=f"{index}: {item.font_name} {item.face_name}") # avoids renaming the item by accident # avoids renaming the item by accident
layout.label(text=f"{index}: {item.font_name} {item.face_name}")
def invoke(self, context, event): def invoke(self, context, event):
pass pass
class ABC3D_UL_texts(bpy.types.UIList): class ABC3D_UL_texts(bpy.types.UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
split = layout.split(factor=0.3) split = layout.split(factor=0.3)
split.label(text="Id: %d" % (item.text_id)) split.label(text="Id: %d" % (item.text_id))
split.label(text=f"{item.text}") # avoids renaming the item by accident # avoids renaming the item by accident
split.label(text=f"{item.text}")
def invoke(self, context, event): def invoke(self, context, event):
pass pass
class ABC3D_PT_Panel(bpy.types.Panel): class ABC3D_PT_Panel(bpy.types.Panel):
bl_label = f"{__name__} panel" bl_label = f"{__name__} panel"
bl_category = "ABC3D" bl_category = "ABC3D"
@ -281,7 +326,10 @@ class ABC3D_PT_Panel(bpy.types.Panel):
icon = 'ERROR' icon = 'ERROR'
layout.row().label(text='no fonts loaded yet') layout.row().label(text='no fonts loaded yet')
layout.operator(f"{__name__}.load_installed_fonts", text="load installed fonts", icon=icon) layout.operator(f"{__name__}.load_installed_fonts",
text="load installed fonts", icon=icon)
layout.operator(f"{__name__}.open_asset_directory",
text="open asset directory", icon='FILEBROWSER')
class ABC3D_PT_LoadFontPanel(bpy.types.Panel): class ABC3D_PT_LoadFontPanel(bpy.types.Panel):
@ -320,13 +368,16 @@ class ABC3D_PT_FontList(bpy.types.Panel):
abc3d_data = scene.abc3d_data abc3d_data = scene.abc3d_data
layout.label(text="Available Fonts") layout.label(text="Available Fonts")
layout.template_list("ABC3D_UL_fonts", "", abc3d_data, "available_fonts", abc3d_data, "active_font_index") layout.template_list("ABC3D_UL_fonts", "", abc3d_data,
"available_fonts", abc3d_data, "active_font_index")
if abc3d_data.active_font_index >= 0: if abc3d_data.active_font_index >= 0:
available_font = abc3d_data.available_fonts[abc3d_data.active_font_index] available_font = abc3d_data.available_fonts[abc3d_data.active_font_index]
font_name = available_font.font_name font_name = available_font.font_name
face_name = available_font.face_name face_name = available_font.face_name
available_glyphs = sorted(Font.fonts[font_name].faces[face_name].glyphs_in_fontfile) available_glyphs = sorted(
loaded_glyphs = sorted(Font.fonts[font_name].faces[face_name].loaded_glyphs) Font.fonts[font_name].faces[face_name].glyphs_in_fontfile)
loaded_glyphs = sorted(
Font.fonts[font_name].faces[face_name].loaded_glyphs)
box = layout.box() box = layout.box()
box.row().label(text=f"Font Name: {font_name}") box.row().label(text=f"Font Name: {font_name}")
box.row().label(text=f"Face Name: {face_name}") box.row().label(text=f"Face Name: {face_name}")
@ -335,20 +386,26 @@ class ABC3D_PT_FontList(bpy.types.Panel):
box.row().label(text=f"Glyphs:") box.row().label(text=f"Glyphs:")
subbox = box.box() subbox = box.box()
for i in range(0, n_rows + 1): for i in range(0, n_rows + 1):
text = ''.join([f"{u}" for ui, u in enumerate(available_glyphs) if ui < (i+1) * n and ui >= i * n]) text = ''.join([f"{u}" for ui, u in enumerate(
available_glyphs) if ui < (i+1) * n and ui >= i * n])
scale_y = 0.5 scale_y = 0.5
row = subbox.row(); row.scale_y = scale_y; row.alignment = 'CENTER' row = subbox.row()
row.scale_y = scale_y
row.alignment = 'CENTER'
row.label(text=text) row.label(text=text)
n_rows = int(len(loaded_glyphs) / n) n_rows = int(len(loaded_glyphs) / n)
box.row().label(text=f"Loaded/Used Glyphs:") box.row().label(text=f"Loaded/Used Glyphs:")
subbox = box.box() subbox = box.box()
for i in range(0, n_rows + 1): for i in range(0, n_rows + 1):
text = ''.join([f"{u}" for ui, u in enumerate(loaded_glyphs) if ui < (i+1) * n and ui >= i * n]) text = ''.join([f"{u}" for ui, u in enumerate(
loaded_glyphs) if ui < (i+1) * n and ui >= i * n])
scale_y = 0.5 scale_y = 0.5
row = subbox.row(); row.scale_y = scale_y row = subbox.row()
row.scale_y = scale_y
row.label(text=text) row.label(text=text)
row = layout.row() row = layout.row()
oper = row.operator(f"{__name__}.load_font", text='Load all glyphs in memory') oper = row.operator(f"{__name__}.load_font",
text='Load all glyphs in memory')
oper.font_name = font_name oper.font_name = font_name
oper.face_name = face_name oper.face_name = face_name
@ -384,6 +441,7 @@ class ABC3D_PT_TextPlacement(bpy.types.Panel):
layout.label(text="Cannot place Text.") layout.label(text="Cannot place Text.")
layout.label(text="Select a curve as active object.") layout.label(text="Select a curve as active object.")
class ABC3D_PT_TextManagement(bpy.types.Panel): class ABC3D_PT_TextManagement(bpy.types.Panel):
bl_label = "Text Management" bl_label = "Text Management"
bl_parent_id = "ABC3D_PT_Panel" bl_parent_id = "ABC3D_PT_Panel"
@ -398,6 +456,7 @@ class ABC3D_PT_TextManagement(bpy.types.Panel):
scene = context.scene scene = context.scene
abc3d_data = scene.abc3d_data abc3d_data = scene.abc3d_data
# TODO: update available_texts # TODO: update available_texts
def update(): def update():
if bpy.context.screen.is_animation_playing: if bpy.context.screen.is_animation_playing:
return return
@ -416,7 +475,8 @@ class ABC3D_PT_TextManagement(bpy.types.Panel):
# these might be there in t.glyphs, but linked to removed objects # these might be there in t.glyphs, but linked to removed objects
# or they might be lost # or they might be lost
if type(next((g for g in t.glyphs if type(g.glyph_object) == type(None)), None)) == type(None): if type(next((g for g in t.glyphs if type(g.glyph_object) == type(None)), None)) == type(None):
g = next((g for g in t.glyphs if type(g.glyph_object) == type(None)), None) g = next((g for g in t.glyphs if type(
g.glyph_object) == type(None)), None)
# for g in t.glyphs: # for g in t.glyphs:
# if type(g) == type(None): # if type(g) == type(None):
# print("IS NONE") # print("IS NONE")
@ -433,6 +493,7 @@ class ABC3D_PT_TextManagement(bpy.types.Panel):
for i in remove_list: for i in remove_list:
if type(abc3d_data.available_texts[i].text_object) != type(None): if type(abc3d_data.available_texts[i].text_object) != type(None):
mom = abc3d_data.available_texts[i].text_object mom = abc3d_data.available_texts[i].text_object
def delif(o, p): def delif(o, p):
if p in o: if p in o:
del o[p] del o[p]
@ -468,8 +529,11 @@ class ABC3D_PT_TextManagement(bpy.types.Panel):
abc3d_data = scene.abc3d_data abc3d_data = scene.abc3d_data
layout.label(text="Text Objects") layout.label(text="Text Objects")
layout.template_list("ABC3D_UL_texts", "", abc3d_data, "available_texts", abc3d_data, "active_text_index") layout.template_list("ABC3D_UL_texts", "", abc3d_data,
layout.row().operator(f"{__name__}.remove_text", text="Remove Textobject") "available_texts", abc3d_data, "active_text_index")
layout.row().operator(
f"{__name__}.remove_text", text="Remove Textobject")
class ABC3D_PT_FontCreation(bpy.types.Panel): class ABC3D_PT_FontCreation(bpy.types.Panel):
bl_label = "Font Creation" bl_label = "Font Creation"
@ -486,22 +550,29 @@ class ABC3D_PT_FontCreation(bpy.types.Panel):
abc3d_data = scene.abc3d_data abc3d_data = scene.abc3d_data
layout.row().operator(f"{__name__}.create_font_from_objects", text='Create/Extend Font') layout.row().operator(
f"{__name__}.create_font_from_objects", text='Create/Extend Font')
box = layout.box() box = layout.box()
box.row().label(text="Exporting a fontfile") box.row().label(text="Exporting a fontfile")
box.row().label(text="1. Select export directory:") box.row().label(text="1. Select export directory:")
box.prop(abc3d_data, 'export_dir') box.prop(abc3d_data, 'export_dir')
box.row().label(text="2. More options and export:") box.row().label(text="2. More options and export:")
box.row().operator(f"{__name__}.save_font_to_file", text='Export Font To File') box.row().operator(f"{__name__}.save_font_to_file",
layout.row().operator(f"{__name__}.toggle_abc3d_collection", text='Toggle Collection') text='Export Font To File')
layout.row().operator(
f"{__name__}.toggle_abc3d_collection", text='Toggle Collection')
box = layout.box() box = layout.box()
box.label(text="metrics") box.label(text="metrics")
box.row().operator(f"{__name__}.add_default_metrics", text='Add Default Metrics') box.row().operator(
f"{__name__}.add_default_metrics", text='Add Default Metrics')
box.row().operator(f"{__name__}.remove_metrics", text='Remove Metrics') box.row().operator(f"{__name__}.remove_metrics", text='Remove Metrics')
box.row().operator(f"{__name__}.align_metrics", text='Align Metrics') box.row().operator(f"{__name__}.align_metrics", text='Align Metrics')
box.row().operator(f"{__name__}.align_metrics_to_active_object", text='Align Metrics to Active Object') box.row().operator(
layout.row().operator(f"{__name__}.temporaryhelper", text='Debug Function Do Not Use') f"{__name__}.align_metrics_to_active_object", text='Align Metrics to Active Object')
layout.row().operator(
f"{__name__}.temporaryhelper", text='Debug Function Do Not Use')
class ABC3D_PT_TextPropertiesPanel(bpy.types.Panel): class ABC3D_PT_TextPropertiesPanel(bpy.types.Panel):
bl_label = "Text Properties" bl_label = "Text Properties"
@ -511,7 +582,8 @@ class ABC3D_PT_TextPropertiesPanel(bpy.types.Panel):
bl_region_type = "UI" bl_region_type = "UI"
def get_active_text_properties(self): def get_active_text_properties(self):
if type(bpy.context.active_object) != type(None):# and bpy.context.object.select_get(): # and bpy.context.object.select_get():
if type(bpy.context.active_object) != type(None):
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 bpy.context.active_object == t.text_object:
return t return t
@ -575,6 +647,7 @@ 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")
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)"""
@ -614,7 +687,8 @@ class ABC3D_OT_InstallFont(bpy.types.Operator):
layout.row().prop(self, "install_in_assets") layout.row().prop(self, "install_in_assets")
if not self.install_in_assets and not self.load_into_memory: if not self.install_in_assets and not self.load_into_memory:
layout.label(text="If the fontfile is not installed,") layout.label(text="If the fontfile is not installed,")
layout.label(text="and the font is not loaded in memory completely,") layout.label(
text="and the font is not loaded in memory completely,")
layout.label(text="the fontfile should not be moved.") layout.label(text="the fontfile should not be moved.")
layout.row().prop(self, "load_into_memory") layout.row().prop(self, "load_into_memory")
if self.load_into_memory: if self.load_into_memory:
@ -674,6 +748,29 @@ class ABC3D_OT_InstallFont(bpy.types.Operator):
return {'FINISHED'} return {'FINISHED'}
class ABC3D_OT_OpenAssetDirectory(bpy.types.Operator):
"""Open Asset Directory"""
bl_idname = f"{__name__}.open_asset_directory"
bl_label = "Opens asset directory."
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
preferences = getPreferences(context)
directory = os.path.realpath(preferences.assets_dir)
if os.path.exists(directory):
utils.open_file_browser(directory)
return {'FINISHED'}
else:
butils.ShowMessageBox(
title=f"{__name__} Warning",
icon="ERROR",
message=("Asset directory does not exist.",
f"Command failed trying to access '{directory}'.",
"Please, make sure it exists or chose another directory."))
return {'CANCELLED'}
class ABC3D_OT_LoadInstalledFonts(bpy.types.Operator): class ABC3D_OT_LoadInstalledFonts(bpy.types.Operator):
"""Load installed fontfiles from datapath.""" """Load installed fontfiles from datapath."""
bl_idname = f"{__name__}.load_installed_fonts" bl_idname = f"{__name__}.load_installed_fonts"
@ -714,6 +811,7 @@ class ABC3D_OT_LoadInstalledFonts(bpy.types.Operator):
return {'FINISHED'} return {'FINISHED'}
class ABC3D_OT_LoadFont(bpy.types.Operator): class ABC3D_OT_LoadFont(bpy.types.Operator):
"""Load all glyphs from a specific font in memory.\nThis can take a while and slow down Blender.""" """Load all glyphs from a specific font in memory.\nThis can take a while and slow down Blender."""
bl_idname = f"{__name__}.load_font" bl_idname = f"{__name__}.load_font"
@ -729,6 +827,7 @@ class ABC3D_OT_LoadFont(bpy.types.Operator):
butils.load_font_from_filepath(f) butils.load_font_from_filepath(f)
return {'FINISHED'} return {'FINISHED'}
class ABC3D_OT_AddDefaultMetrics(bpy.types.Operator): class ABC3D_OT_AddDefaultMetrics(bpy.types.Operator):
"""Add default metrics to selected objects""" """Add default metrics to selected objects"""
bl_idname = f"{__name__}.add_default_metrics" bl_idname = f"{__name__}.add_default_metrics"
@ -740,6 +839,7 @@ class ABC3D_OT_AddDefaultMetrics(bpy.types.Operator):
butils.add_default_metrics_to_objects(objects) butils.add_default_metrics_to_objects(objects)
return {'FINISHED'} return {'FINISHED'}
class ABC3D_OT_RemoveMetrics(bpy.types.Operator): class ABC3D_OT_RemoveMetrics(bpy.types.Operator):
"""Remove metrics from selected objects""" """Remove metrics from selected objects"""
bl_idname = f"{__name__}.remove_metrics" bl_idname = f"{__name__}.remove_metrics"
@ -751,6 +851,7 @@ class ABC3D_OT_RemoveMetrics(bpy.types.Operator):
butils.remove_metrics_from_objects(objects) butils.remove_metrics_from_objects(objects)
return {'FINISHED'} return {'FINISHED'}
class ABC3D_OT_AlignMetricsToActiveObject(bpy.types.Operator): class ABC3D_OT_AlignMetricsToActiveObject(bpy.types.Operator):
"""Align metrics of selected objects to metrics of active object""" """Align metrics of selected objects to metrics of active object"""
bl_idname = f"{__name__}.align_metrics_to_active_object" bl_idname = f"{__name__}.align_metrics_to_active_object"
@ -762,6 +863,7 @@ class ABC3D_OT_AlignMetricsToActiveObject(bpy.types.Operator):
butils.align_metrics_of_objects_to_active_object(objects) butils.align_metrics_of_objects_to_active_object(objects)
return {'FINISHED'} return {'FINISHED'}
class ABC3D_OT_AlignMetrics(bpy.types.Operator): class ABC3D_OT_AlignMetrics(bpy.types.Operator):
"""Align metrics of selected objects to each other""" """Align metrics of selected objects to each other"""
bl_idname = f"{__name__}.align_metrics" bl_idname = f"{__name__}.align_metrics"
@ -773,6 +875,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_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."""
bl_idname = f"{__name__}.temporaryhelper" bl_idname = f"{__name__}.temporaryhelper"
@ -800,6 +903,7 @@ class ABC3D_OT_TemporaryHelper(bpy.types.Operator):
return {'FINISHED'} return {'FINISHED'}
class ABC3D_OT_RemoveText(bpy.types.Operator): class ABC3D_OT_RemoveText(bpy.types.Operator):
"""Remove Text 3D""" """Remove Text 3D"""
bl_idname = f"{__name__}.remove_text" bl_idname = f"{__name__}.remove_text"
@ -827,6 +931,7 @@ class ABC3D_OT_RemoveText(bpy.types.Operator):
i = abc3d_data.active_text_index i = abc3d_data.active_text_index
if type(abc3d_data.available_texts[i].text_object) != type(None): if type(abc3d_data.available_texts[i].text_object) != type(None):
mom = abc3d_data.available_texts[i].text_object mom = abc3d_data.available_texts[i].text_object
def delif(o, p): def delif(o, p):
if p in o: if p in o:
del o[p] del o[p]
@ -973,6 +1078,7 @@ class ABC3D_OT_PlaceText(bpy.types.Operator):
return {'FINISHED'} return {'FINISHED'}
class ABC3D_OT_ToggleABC3DCollection(bpy.types.Operator): class ABC3D_OT_ToggleABC3DCollection(bpy.types.Operator):
"""Toggle ABC3D Collection""" """Toggle ABC3D Collection"""
bl_idname = f"{__name__}.toggle_abc3d_collection" bl_idname = f"{__name__}.toggle_abc3d_collection"
@ -984,7 +1090,8 @@ class ABC3D_OT_ToggleABC3DCollection(bpy.types.Operator):
fontcollection = bpy.data.collections.get("ABC3D") fontcollection = bpy.data.collections.get("ABC3D")
if fontcollection is None: if fontcollection is None:
self.report({'INFO'}, f"{bl_info['name']}: There is no collection. Did you use or create any glyphs yet?") self.report(
{'INFO'}, f"{bl_info['name']}: There is no collection. Did you use or create any glyphs yet?")
elif scene.collection.children.find(fontcollection.name) < 0: elif scene.collection.children.find(fontcollection.name) < 0:
scene.collection.children.link(fontcollection) scene.collection.children.link(fontcollection)
self.report({'INFO'}, f"{bl_info['name']}: show collection") self.report({'INFO'}, f"{bl_info['name']}: show collection")
@ -1006,27 +1113,32 @@ class ABC3D_OT_SaveFontToFile(bpy.types.Operator):
preferences = getPreferences(context) preferences = getPreferences(context)
abc3d_data = context.scene.abc3d_data abc3d_data = context.scene.abc3d_data
if abc3d_data.export_dir == "": if abc3d_data.export_dir == "":
abc3d_data.export_dir = os.path.join(preferences.assets_dir, "fonts") abc3d_data.export_dir = os.path.join(
preferences.assets_dir, "fonts")
return wm.invoke_props_dialog(self) return wm.invoke_props_dialog(self)
def draw(self, context): def draw(self, context):
abc3d_data = context.scene.abc3d_data abc3d_data = context.scene.abc3d_data
layout = self.layout layout = self.layout
layout.label(text="Available Fonts") layout.label(text="Available Fonts")
layout.template_list("ABC3D_UL_fonts", "", abc3d_data, "available_fonts", abc3d_data, "active_font_index") layout.template_list("ABC3D_UL_fonts", "", abc3d_data,
"available_fonts", abc3d_data, "active_font_index")
available_font = abc3d_data.available_fonts[abc3d_data.active_font_index] available_font = abc3d_data.available_fonts[abc3d_data.active_font_index]
font_name = available_font.font_name font_name = available_font.font_name
face_name = available_font.face_name face_name = available_font.face_name
loaded_glyphs = sorted(Font.fonts[font_name].faces[face_name].loaded_glyphs) loaded_glyphs = sorted(
Font.fonts[font_name].faces[face_name].loaded_glyphs)
n = 16 n = 16
n_rows = int(len(loaded_glyphs) / n) n_rows = int(len(loaded_glyphs) / n)
box = layout.box() box = layout.box()
box.row().label(text=f"Glyphs to be exported:") box.row().label(text=f"Glyphs to be exported:")
subbox = box.box() subbox = box.box()
for i in range(0, n_rows + 1): for i in range(0, n_rows + 1):
text = ''.join([f"{u}" for ui, u in enumerate(loaded_glyphs) if ui < (i+1) * n and ui >= i * n]) text = ''.join([f"{u}" for ui, u in enumerate(
loaded_glyphs) if ui < (i+1) * n and ui >= i * n])
scale_y = 0.5 scale_y = 0.5
row = subbox.row(); row.scale_y = scale_y row = subbox.row()
row.scale_y = scale_y
row.label(text=text) row.label(text=text)
layout.prop(abc3d_data, 'export_dir') layout.prop(abc3d_data, 'export_dir')
@ -1043,15 +1155,18 @@ class ABC3D_OT_SaveFontToFile(bpy.types.Operator):
return {'CANCELLED'} return {'CANCELLED'}
if abc3d_data.active_font_index < 0: if abc3d_data.active_font_index < 0:
self.report({'INFO'}, f"{bl_info['name']}: There is no active font") self.report(
{'INFO'}, f"{bl_info['name']}: There is no active font")
return {'CANCELLED'} return {'CANCELLED'}
if len(abc3d_data.available_fonts) <= abc3d_data.active_font_index: if len(abc3d_data.available_fonts) <= abc3d_data.active_font_index:
self.report({'INFO'}, f"{bl_info['name']}: Active font is not available") self.report(
{'INFO'}, f"{bl_info['name']}: Active font is not available")
return {'CANCELLED'} return {'CANCELLED'}
# save state to restore later # save state to restore later
was_fontcollection_linked = scene.collection.children.find(fontcollection.name) >= 0 was_fontcollection_linked = scene.collection.children.find(
fontcollection.name) >= 0
was_selection = [] was_selection = []
for obj in bpy.context.selected_objects: for obj in bpy.context.selected_objects:
was_selection.append(obj) was_selection.append(obj)
@ -1063,7 +1178,8 @@ class ABC3D_OT_SaveFontToFile(bpy.types.Operator):
selected_font = abc3d_data.available_fonts[abc3d_data.active_font_index] selected_font = abc3d_data.available_fonts[abc3d_data.active_font_index]
# print(selected_font.font_name) # print(selected_font.font_name)
self.report({'INFO'}, f"{bl_info['name']}: {selected_font.font_name} {selected_font.face_name}") self.report(
{'INFO'}, f"{bl_info['name']}: {selected_font.font_name} {selected_font.face_name}")
preferences = getPreferences(context) preferences = getPreferences(context)
print(f"assets folder: {preferences.assets_dir}") print(f"assets folder: {preferences.assets_dir}")
@ -1102,12 +1218,14 @@ class ABC3D_OT_SaveFontToFile(bpy.types.Operator):
bpy.ops.export_scene.gltf( bpy.ops.export_scene.gltf(
filepath=filepath, filepath=filepath,
check_existing=False, check_existing=False,
export_format='GLB', # GLB or GLTF_SEPARATE (also change filepath) # GLB or GLTF_SEPARATE (also change filepath)
export_format='GLB',
export_extras=True, export_extras=True,
use_selection=True, use_selection=True,
use_active_scene=True, use_active_scene=True,
) )
bpy.app.timers.register(lambda: bpy.ops.scene.delete(), first_interval=1) bpy.app.timers.register(
lambda: bpy.ops.scene.delete(), first_interval=1)
# bpy.ops.scene.delete() # bpy.ops.scene.delete()
# restore() # restore()
@ -1162,29 +1280,42 @@ class ABC3D_OT_CreateFontFromObjects(bpy.types.Operator):
row.prop(self, 'autodetect_names') row.prop(self, 'autodetect_names')
if self.autodetect_names: if self.autodetect_names:
scale_y = 0.5 scale_y = 0.5
row = layout.row(); row.scale_y = scale_y row = layout.row()
row.label(text="Watch out, follow convention in naming your meshes:") row.scale_y = scale_y
row = layout.row(); row.scale_y = scale_y row.label(
text="Watch out, follow convention in naming your meshes:")
row = layout.row()
row.scale_y = scale_y
row.label(text="'<glyph id>_<font name>_<face name>'") row.label(text="'<glyph id>_<font name>_<face name>'")
row = layout.row(); row.scale_y = scale_y row = layout.row()
row.scale_y = scale_y
row.label(text=" - glyph id: unicode glyph name or raw glyph") row.label(text=" - glyph id: unicode glyph name or raw glyph")
row = layout.row(); row.scale_y = scale_y row = layout.row()
row.scale_y = scale_y
row.label(text=" - font name: font name with underscore") row.label(text=" - font name: font name with underscore")
row = layout.row(); row.scale_y = scale_y row = layout.row()
row.scale_y = scale_y
row.label(text=" - face name: face name") row.label(text=" - face name: face name")
row = layout.row(); row.scale_y = scale_y row = layout.row()
row.scale_y = scale_y
row.label(text="working examples:") row.label(text="working examples:")
row = layout.row(); row.scale_y = scale_y row = layout.row()
row.scale_y = scale_y
row.label(text="- 'A_NM_Origin_Tender'") row.label(text="- 'A_NM_Origin_Tender'")
row = layout.row(); row.scale_y = scale_y row = layout.row()
row.scale_y = scale_y
row.label(text="- 'B_NM_Origin_Tender'") row.label(text="- 'B_NM_Origin_Tender'")
row = layout.row(); row.scale_y = scale_y row = layout.row()
row.scale_y = scale_y
row.label(text="- 'arrowright_NM_Origin_Tender'") row.label(text="- 'arrowright_NM_Origin_Tender'")
row = layout.row(); row.scale_y = scale_y row = layout.row()
row.scale_y = scale_y
row.label(text="- '→_NM_Origin_Tender' (equal to above)") row.label(text="- '→_NM_Origin_Tender' (equal to above)")
row = layout.row(); row.scale_y = scale_y row = layout.row()
row.scale_y = scale_y
row.label(text="- 'quotesingle_NM_Origin_Tender.001'") row.label(text="- 'quotesingle_NM_Origin_Tender.001'")
row = layout.row(); row.scale_y = scale_y row = layout.row()
row.scale_y = scale_y
row.label(text="- 'colon_NM_Origin_Tender_2'") row.label(text="- 'colon_NM_Origin_Tender_2'")
box = layout.box() box = layout.box()
box.enabled = not self.autodetect_names box.enabled = not self.autodetect_names
@ -1197,8 +1328,10 @@ class ABC3D_OT_CreateFontFromObjects(bpy.types.Operator):
character = "" character = ""
if Font.known_misspellings[k] in Font.name_to_glyph_d: if Font.known_misspellings[k] in Font.name_to_glyph_d:
character = f" ({Font.name_to_glyph_d[Font.known_misspellings[k]]})" character = f" ({Font.name_to_glyph_d[Font.known_misspellings[k]]})"
row = layout.row(); row.scale_y = 0.5 row = layout.row()
row.label(text=f"{k}{Font.known_misspellings[k]}{character}") row.scale_y = 0.5
row.label(
text=f"{k}{Font.known_misspellings[k]}{character}")
def execute(self, context): def execute(self, context):
print(f"executing {self.bl_idname}") print(f"executing {self.bl_idname}")
@ -1233,7 +1366,8 @@ class ABC3D_OT_CreateFontFromObjects(bpy.types.Operator):
if self.autodetect_names: if self.autodetect_names:
ifxsplit = o.name.split('_') ifxsplit = o.name.split('_')
if len(ifxsplit) < 4: if len(ifxsplit) < 4:
print(f"whoops name could not be autodetected {o.name}") print(
f"whoops name could not be autodetected {o.name}")
continue continue
font_name = f"{ifxsplit[1]}_{ifxsplit[2]}" font_name = f"{ifxsplit[1]}_{ifxsplit[2]}"
face_name = ifxsplit[3] face_name = ifxsplit[3]
@ -1274,11 +1408,14 @@ class ABC3D_OT_CreateFontFromObjects(bpy.types.Operator):
f.face_name = face_name f.face_name = face_name
else: else:
print(f"import warning: did not understand glyph {name}") print(
self.report({'INFO'}, f"did not understand glyph {name}") f"import warning: did not understand glyph {name}")
self.report(
{'INFO'}, f"did not understand glyph {name}")
return {'FINISHED'} return {'FINISHED'}
class ABC3D_PT_RightPropertiesPanel(bpy.types.Panel): class ABC3D_PT_RightPropertiesPanel(bpy.types.Panel):
"""Creates a Panel in the Object properties window""" """Creates a Panel in the Object properties window"""
bl_label = f"{bl_info['name']}" bl_label = f"{bl_info['name']}"
@ -1290,8 +1427,10 @@ class ABC3D_PT_RightPropertiesPanel(bpy.types.Panel):
@classmethod @classmethod
def poll(self, context): def poll(self, context):
# only show the panel, if it's a textobject or a glyph # only show the panel, if it's a textobject or a glyph
is_text = type(next((t for t in context.scene.abc3d_data.available_texts if t.text_object == context.active_object), None)) != type(None) is_text = type(next((t for t in context.scene.abc3d_data.available_texts if t.text_object ==
is_glyph = type(next((t for t in context.scene.abc3d_data.available_texts if t.text_object == context.active_object.parent), None)) != type(None) context.active_object), None)) != type(None)
is_glyph = type(next((t for t in context.scene.abc3d_data.available_texts if t.text_object ==
context.active_object.parent), None)) != type(None)
return is_text or is_glyph return is_text or is_glyph
def draw(self, context): def draw(self, context):
@ -1303,6 +1442,7 @@ class ABC3D_PT_RightPropertiesPanel(bpy.types.Panel):
def is_it_text(): def is_it_text():
return type(next((t for t in context.scene.abc3d_data.available_texts if t.text_object == context.active_object), None)) != type(None) return type(next((t for t in context.scene.abc3d_data.available_texts if t.text_object == context.active_object), None)) != type(None)
def is_it_glyph(): def is_it_glyph():
return type(next((t for t in context.scene.abc3d_data.available_texts if t.text_object == context.active_object.parent), None)) != type(None) return type(next((t for t in context.scene.abc3d_data.available_texts if t.text_object == context.active_object.parent), None)) != type(None)
@ -1334,6 +1474,7 @@ class ABC3D_PT_RightPropertiesPanel(bpy.types.Panel):
if is_glyph: if is_glyph:
layout.row().label(text="Glyph Properties:") layout.row().label(text="Glyph Properties:")
class ABC3D_OT_Reporter(bpy.types.Operator): class ABC3D_OT_Reporter(bpy.types.Operator):
bl_idname = f"{__name__}.reporter" bl_idname = f"{__name__}.reporter"
bl_label = "Report" bl_label = "Report"
@ -1354,6 +1495,7 @@ class ABC3D_OT_Reporter(bpy.types.Operator):
butils.ShowMessageBox('whatever', 'INFO', 'INFO') butils.ShowMessageBox('whatever', 'INFO', 'INFO')
return {'FINISHED'} return {'FINISHED'}
classes = ( classes = (
bimport.ImportGLTF2, bimport.ImportGLTF2,
bimport.GetFontFacesInFile, bimport.GetFontFacesInFile,
@ -1371,6 +1513,7 @@ classes = (
ABC3D_PT_TextManagement, ABC3D_PT_TextManagement,
ABC3D_PT_FontCreation, ABC3D_PT_FontCreation,
ABC3D_PT_TextPropertiesPanel, ABC3D_PT_TextPropertiesPanel,
ABC3D_OT_OpenAssetDirectory,
ABC3D_OT_LoadInstalledFonts, ABC3D_OT_LoadInstalledFonts,
ABC3D_OT_LoadFont, ABC3D_OT_LoadFont,
ABC3D_OT_AddDefaultMetrics, ABC3D_OT_AddDefaultMetrics,
@ -1388,6 +1531,7 @@ classes = (
ABC3D_OT_Reporter, ABC3D_OT_Reporter,
) )
def compare_text_object_with_object(t, o, strict=False): def compare_text_object_with_object(t, o, strict=False):
for k in o.keys(): for k in o.keys():
if k == f"{utils.prefix()}_type": if k == f"{utils.prefix()}_type":
@ -1406,6 +1550,7 @@ def compare_text_object_with_object(t, o, strict=False):
# if # if
return True return True
def detect_text(): def detect_text():
scene = bpy.context.scene scene = bpy.context.scene
abc3d_data = scene.abc3d_data abc3d_data = scene.abc3d_data
@ -1415,9 +1560,11 @@ def detect_text():
if len(abc3d_data.available_texts) > linked_textobject \ if len(abc3d_data.available_texts) > linked_textobject \
and abc3d_data.available_texts[linked_textobject].text_object == o: and abc3d_data.available_texts[linked_textobject].text_object == o:
t = abc3d_data.available_texts[linked_textobject] t = abc3d_data.available_texts[linked_textobject]
a = test_availability(o["font_name"], o["face_name"], o["text"]) a = test_availability(
o["font_name"], o["face_name"], o["text"])
butils.transfer_blender_object_to_text_properties(o, t) butils.transfer_blender_object_to_text_properties(o, t)
def load_used_glyphs(): def load_used_glyphs():
print("LOAD USED GLYPHS") print("LOAD USED GLYPHS")
scene = bpy.context.scene scene = bpy.context.scene
@ -1451,18 +1598,39 @@ def load_handler(self, dummy):
butils.run_in_main_thread(bpy.ops.abc3d.load_installed_fonts) butils.run_in_main_thread(bpy.ops.abc3d.load_installed_fonts)
butils.run_in_main_thread(load_used_glyphs) butils.run_in_main_thread(load_used_glyphs)
def load_handler_unload(): def load_handler_unload():
if bpy.app.timers.is_registered(butils.execute_queued_functions): if bpy.app.timers.is_registered(butils.execute_queued_functions):
bpy.app.timers.unregister(butils.execute_queued_functions) bpy.app.timers.unregister(butils.execute_queued_functions)
@persistent @persistent
def on_frame_changed(self, dummy): def on_frame_changed(self, dummy):
for t in bpy.context.scene.abc3d_data.available_texts: for t in bpy.context.scene.abc3d_data.available_texts:
# TODO PERFORMANCE: only on demand # TODO PERFORMANCE: only on demand
butils.set_text_on_curve(t) butils.set_text_on_curve(t)
@persistent
def on_depsgraph_update(scene, depsgraph):
for u in depsgraph.updates:
if f"{utils.prefix()}_linked_textobject" in u.id.keys() \
and f"{utils.prefix()}_type" in u.id.keys() \
and u.id[f"{utils.prefix()}_type"] == 'textobject':
linked_textobject = u.id[f"{utils.prefix()}_linked_textobject"]
if u.is_updated_geometry and len(scene.abc3d_data.available_texts) > linked_textobject and not "prevent_recursion" in u.id:
u.id["prevent_recursion"] = True
butils.set_text_on_curve(
scene.abc3d_data.available_texts[linked_textobject])
elif "prevent_recursion" in u.id.keys():
del u.id["prevent_recursion"]
def register(): def register():
addon_updater_ops.register(bl_info)
for cls in classes: for cls in classes:
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.Object.__del__ = lambda self: print(f"Bye {self.name}") # bpy.types.Object.__del__ = lambda self: print(f"Bye {self.name}")
@ -1477,16 +1645,23 @@ def register():
if on_frame_changed not in bpy.app.handlers.frame_change_post: if on_frame_changed not in bpy.app.handlers.frame_change_post:
bpy.app.handlers.frame_change_post.append(on_frame_changed) bpy.app.handlers.frame_change_post.append(on_frame_changed)
if on_depsgraph_update not in bpy.app.handlers.depsgraph_update_post:
bpy.app.handlers.depsgraph_update_post.append(on_depsgraph_update)
butils.run_in_main_thread(butils.clear_available_fonts) butils.run_in_main_thread(butils.clear_available_fonts)
# butils.run_in_main_thread(butils.load_installed_fonts) # butils.run_in_main_thread(butils.load_installed_fonts)
butils.run_in_main_thread(butils.update_available_fonts) butils.run_in_main_thread(butils.update_available_fonts)
butils.run_in_main_thread(butils.update_types)
# bpy.ops.abc3d.load_installed_fonts() # bpy.ops.abc3d.load_installed_fonts()
Font.name_to_glyph_d = Font.generate_name_to_glyph_d() Font.name_to_glyph_d = Font.generate_name_to_glyph_d()
def unregister(): def unregister():
for cls in classes: addon_updater_ops.unregister()
for cls in reversed(classes):
bpy.utils.unregister_class(cls) bpy.utils.unregister_class(cls)
# remove autostart when loading blend file # remove autostart when loading blend file
@ -1501,6 +1676,6 @@ def unregister():
del bpy.types.Scene.abc3d_data del bpy.types.Scene.abc3d_data
print(f"UNREGISTER {bl_info['name']}") print(f"UNREGISTER {bl_info['name']}")
if __name__ == '__main__': if __name__ == '__main__':
register() register()

View file

@ -6,12 +6,15 @@ let g:jedi#environment_path = "venv"
""""""""""""""""""""""""""""""""" ALE """"""""""""""""""""""""""""""""" ALE
"let g:ale_python_pylint_executable = '/home/jrkb/git/pointer/neomatter/font3d/font3d_blender_addon/venv/bin/pylint' "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/font3d_blender_addon/venv/bin/python' "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_python_pylint_use_global=1
"let g:ale_use_global_executables=1 "let g:ale_use_global_executables=1
"let g:ale_python_auto_pipenv=1 "let g:ale_python_auto_pipenv=1
"let g:ale_python_auto_virtualenv=1 "let g:ale_python_auto_virtualenv=1
"let g:ale_virtualenv_dir_names = ['venv'] "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}} let g:ale_pattern_options = {'\.py$': {'ale_enabled': 0}}

1787
addon_updater.py Normal file

File diff suppressed because it is too large Load diff

1539
addon_updater_ops.py Normal file

File diff suppressed because it is too large Load diff

View file

@ -605,9 +605,21 @@ def is_mesh(o):
return type(o.data) == bpy.types.Mesh return type(o.data) == bpy.types.Mesh
def is_metrics_object(o): def is_metrics_object(o):
if f"{utils.prefix()}_type" in o:
return o[f"{utils.prefix()}_type"] == 'metrics'
return (re.match(".*_metrics$", o.name) != None or re.match(".*_metrics.[\d]{3}$", o.name) != None) and is_mesh(o) return (re.match(".*_metrics$", o.name) != None or re.match(".*_metrics.[\d]{3}$", o.name) != None) and is_mesh(o)
def is_text_object(o):
if f"{utils.prefix()}_type" in o:
return o[f"{utils.prefix()}_type"] == 'textobject'
for t in bpy.context.scene.abc3d_data.available_texts:
if o == t.text_object:
return True
return False
def is_glyph(o): def is_glyph(o):
if f"{utils.prefix()}_type" in o:
return o[f"{utils.prefix()}_type"] == 'glyph'
try: try:
return type(o.parent) is not type(None) \ return type(o.parent) is not type(None) \
and "glyphs" in o.parent.name \ and "glyphs" in o.parent.name \
@ -616,6 +628,14 @@ def is_glyph(o):
except ReferenceError as e: except ReferenceError as e:
return False return False
def update_types():
scene = bpy.context.scene
abc3d_data = scene.abc3d_data
for t in abc3d_data.available_texts:
t.text_object[f"{utils.prefix()}_type"] = "textobject"
for g in t.glyphs:
g.glyph_object[f"{utils.prefix()}_type"] = "glyph"
# blender bound_box vertices # blender bound_box vertices
# #
# 3------7. # 3------7.
@ -678,6 +698,11 @@ def set_text_on_curve(text_properties, recursive=True):
if len(text_properties.text) != len(text_properties.glyphs): if len(text_properties.text) != len(text_properties.glyphs):
regenerate = True regenerate = True
# blender bug
# https://projects.blender.org/blender/blender/issues/100661
if mom.data.use_path:
regenerate = True
# if we regenerate.... delete objects # if we regenerate.... delete objects
if regenerate: if regenerate:
completely_delete_objects(glyph_objects) completely_delete_objects(glyph_objects)
@ -749,7 +774,12 @@ def set_text_on_curve(text_properties, recursive=True):
location, tangent, spline_index = calc_point_on_bezier_curve(mom, advance, True, True) location, tangent, spline_index = calc_point_on_bezier_curve(mom, advance, True, True)
if spline_index != previous_spline_index: if spline_index != previous_spline_index:
is_newline = True is_newline = True
if regenerate:
ob.location = mom.matrix_world @ (location + text_properties.translation) ob.location = mom.matrix_world @ (location + text_properties.translation)
else:
ob.location = (location + text_properties.translation)
if not text_properties.ignore_orientation: if not text_properties.ignore_orientation:
mask = [0] mask = [0]
input_rotations = [mathutils.Vector((0.0, 0.0, 0.0))] input_rotations = [mathutils.Vector((0.0, 0.0, 0.0))]
@ -765,7 +795,10 @@ def set_text_on_curve(text_properties, recursive=True):
ob.rotation_mode = 'QUATERNION' ob.rotation_mode = 'QUATERNION'
q = mathutils.Quaternion() q = mathutils.Quaternion()
q.rotate(text_properties.orientation) q.rotate(text_properties.orientation)
if regenerate:
ob.rotation_quaternion = (mom.matrix_world @ motor[0] @ q.to_matrix().to_4x4()).to_quaternion() ob.rotation_quaternion = (mom.matrix_world @ motor[0] @ q.to_matrix().to_4x4()).to_quaternion()
else:
ob.rotation_quaternion = (motor[0] @ q.to_matrix().to_4x4()).to_quaternion()
else: else:
q = mathutils.Quaternion() q = mathutils.Quaternion()
q.rotate(text_properties.orientation) q.rotate(text_properties.orientation)
@ -812,6 +845,8 @@ def set_text_on_curve(text_properties, recursive=True):
if regenerate: if regenerate:
mom.select_set(True) mom.select_set(True)
# https://projects.blender.org/blender/blender/issues/100661
mom.data.use_path = False
mom[f"{utils.prefix()}_type"] = "textobject" mom[f"{utils.prefix()}_type"] = "textobject"
mom[f"{utils.prefix()}_linked_textobject"] = text_properties.text_id mom[f"{utils.prefix()}_linked_textobject"] = text_properties.text_id
mom[f"{utils.prefix()}_font_name"] = text_properties.font_name mom[f"{utils.prefix()}_font_name"] = text_properties.font_name

View file

@ -4,7 +4,7 @@ def get_version_major():
def get_version_minor(): def get_version_minor():
return 0 return 0
def get_version_patch(): def get_version_patch():
return 1 return 2
def get_version_string(): def get_version_string():
return f"{get_version_major()}.{get_version_minor()}.{get_version_patch}" return f"{get_version_major()}.{get_version_minor()}.{get_version_patch}"
def prefix(): def prefix():
@ -30,6 +30,8 @@ def mapRange(in_value, in_min, in_max, out_min, out_max, clamp=False):
return max(out_max, min(out_min, output)) return max(out_max, min(out_min, output))
else: else:
return output return output
import warnings import warnings
import functools import functools
@ -47,6 +49,9 @@ def deprecated(func):
return func(*args, **kwargs) return func(*args, **kwargs)
return new_func return new_func
import subprocess
import sys
def open_file_browser(directory): def open_file_browser(directory):
if sys.platform=='win32': if sys.platform=='win32':
os.startfile(directory) os.startfile(directory)