2024-05-08 16:19:47 +02:00
# SPDX-License-Identifier: GPL-2.0-only
"""
A 3 D font helper
"""
bl_info = {
2024-08-14 11:26:19 +02:00
" name " : " ABC3D " ,
2024-05-08 16:19:47 +02:00
" author " : " Jakob Schlötter, Studio Pointer* " ,
2024-10-31 19:34:47 +01:00
" version " : ( 0 , 0 , 2 ) ,
2024-05-08 16:19:47 +02:00
" blender " : ( 4 , 1 , 0 ) ,
2024-08-04 12:52:37 +02:00
" location " : " VIEW3D " ,
2024-08-14 11:26:19 +02:00
" description " : " Does ABC3D stuff " ,
2024-05-08 16:19:47 +02:00
" category " : " Typography " ,
}
2024-05-21 18:00:49 +02:00
# make sure that modules are reloadable
# when registering
# handy for development
# first import dependencies for the method
import importlib
# then import dependencies for our addon
if " bpy " in locals ( ) :
importlib . reload ( Font )
importlib . reload ( utils )
importlib . reload ( butils )
2024-08-21 14:42:53 +02:00
importlib . reload ( bimport )
2024-11-05 15:45:50 +01:00
importlib . reload ( addon_updater_ops )
2024-05-21 18:00:49 +02:00
else :
from . common import Font
from . common import utils
from . import butils
2024-08-21 14:42:53 +02:00
from . import bimport
2024-11-05 15:45:50 +01:00
from . import addon_updater_ops
2024-05-21 18:00:49 +02:00
2024-05-08 16:19:47 +02:00
import bpy
import math
2024-05-28 14:11:32 +02:00
import mathutils
2024-05-08 16:19:47 +02:00
import io
import functools
from bpy . types import Panel
from bpy . app . handlers import persistent
from random import uniform
import time
import datetime
2024-05-28 14:11:32 +02:00
import os
2024-05-21 18:00:49 +02:00
import re
2024-05-28 14:11:32 +02:00
def getPreferences ( context ) :
preferences = context . preferences
return preferences . addons [ __name__ ] . preferences
2024-05-21 18:00:49 +02:00
2024-11-05 15:45:50 +01:00
@addon_updater_ops.make_annotations
2024-08-14 11:26:19 +02:00
class ABC3D_addonPreferences ( bpy . types . AddonPreferences ) :
""" ABC3D Addon Preferences
2024-05-28 14:11:32 +02:00
These are the preferences at Edit / Preferences / Add - ons """
bl_idname = __name__
2024-11-05 15:45:50 +01:00
# 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 )
2024-05-28 14:11:32 +02:00
def get_default_assets_dir ( ) :
return bpy . utils . user_resource (
' DATAFILES ' ,
path = f " { __name__ } " ,
create = True )
def on_change_assets_dir ( self , context ) :
if not os . path . isdir ( self . assets_dir ) :
butils . ShowMessageBox (
title = f " { __name__ } Warning " ,
icon = " ERROR " ,
message = ( " Chosen directory does not exist. " ,
" Please, reset to default, create it or chose another one. " ) )
elif not os . access ( self . assets_dir , os . W_OK ) :
butils . ShowMessageBox (
title = f " { __name__ } Warning " ,
icon = " ERROR " ,
message = ( " Chosen directory is not writable. " ,
" Please reset to default or chose another one. " ) )
print ( f " { __name__ } : change assets_dir to { self . assets_dir } " )
assets_dir : bpy . props . StringProperty (
name = " Assets Folder " ,
subtype = ' DIR_PATH ' ,
default = get_default_assets_dir ( ) ,
update = on_change_assets_dir ,
)
def draw ( self , context ) :
layout = self . layout
layout . label ( text = " Directory for storage of fonts and other assets: " )
layout . prop ( self , " assets_dir " )
2024-11-05 15:45:50 +01:00
# 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 )
2024-05-08 16:19:47 +02:00
2024-08-14 11:26:19 +02:00
class ABC3D_available_font ( bpy . types . PropertyGroup ) :
2024-06-27 14:56:43 +02:00
font_name : bpy . props . StringProperty ( name = " " )
2024-08-08 11:28:31 +02:00
face_name : bpy . props . StringProperty ( name = " " )
2024-06-27 14:56:43 +02:00
2024-08-23 16:32:40 +02:00
2024-08-14 11:26:19 +02:00
class ABC3D_glyph_properties ( bpy . types . PropertyGroup ) :
2024-06-27 14:56:43 +02:00
glyph_id : bpy . props . StringProperty ( maxlen = 1 )
glyph_object : bpy . props . PointerProperty ( type = bpy . types . Object )
letter_spacing : bpy . props . FloatProperty (
name = " Letter Spacing " ,
description = " Letter Spacing " ,
)
2024-08-17 11:02:26 +02:00
2024-08-14 11:26:19 +02:00
class ABC3D_text_properties ( bpy . types . PropertyGroup ) :
2024-08-17 11:02:26 +02:00
def font_items_callback ( self , context ) :
items = [ ]
for f in Font . get_loaded_fonts_and_faces ( ) :
items . append ( ( f " { f [ 0 ] } { f [ 1 ] } " , f " { f [ 0 ] } { f [ 1 ] } " , " " ) )
return items
def font_default_callback ( self , context ) :
d = context . scene . abc3d_data
items = self . font_items_callback ( context )
if len ( d . available_fonts ) > 0 :
if len ( d . available_fonts ) > d . active_text_index :
f = d . available_fonts [ d . active_text_index ]
return 0 #f"{f.font_name} {f.face_name}"
else :
f = d . available_fonts [ 0 ]
return 0 #f"{f.font_name} {f.face_name}"
if type ( self . font_name ) != type ( None ) and type ( self . face_name ) != type ( None ) :
return 0 #f"{self.font_name} {self.face_name}"
else :
return 0 #""
2024-08-21 14:42:53 +02:00
def glyphs_update_callback ( self , context ) :
butils . prepare_text ( self . font_name ,
self . face_name ,
self . text )
butils . set_text_on_curve ( self )
2024-06-27 14:56:43 +02:00
def update_callback ( self , context ) :
butils . set_text_on_curve ( self )
2024-08-17 11:02:26 +02:00
def font_update_callback ( self , context ) :
font_name , face_name = self . font . split ( " " )
2024-08-21 14:42:53 +02:00
self [ " font_name " ] = font_name
self [ " face_name " ] = face_name
self . glyphs_update_callback ( self )
2024-08-17 11:02:26 +02:00
2024-07-10 16:34:43 +02:00
text_id : bpy . props . IntProperty ( )
2024-08-17 11:02:26 +02:00
font : bpy . props . EnumProperty (
items = font_items_callback ,
update = font_update_callback ,
)
2024-08-21 14:42:53 +02:00
font_name : bpy . props . StringProperty (
update = glyphs_update_callback
)
face_name : bpy . props . StringProperty (
update = glyphs_update_callback
)
2024-06-27 14:56:43 +02:00
text_object : bpy . props . PointerProperty ( type = bpy . types . Object )
2024-07-10 16:34:43 +02:00
text : bpy . props . StringProperty (
2024-08-21 14:42:53 +02:00
update = glyphs_update_callback
2024-07-10 16:34:43 +02:00
)
2024-06-27 14:56:43 +02:00
letter_spacing : bpy . props . FloatProperty (
update = update_callback ,
name = " Letter Spacing " ,
description = " Letter Spacing " ,
2024-08-04 12:52:37 +02:00
options = { ' ANIMATABLE ' } ,
2024-06-27 14:56:43 +02:00
step = 0.01 ,
)
2024-08-04 12:52:37 +02:00
orientation : bpy . props . FloatVectorProperty (
update = update_callback ,
name = " Orientation " ,
default = ( 1.5707963267948966 , 0.0 , 0.0 ) , # 90 degrees in radians
subtype = ' EULER ' ,
)
2024-08-05 12:59:07 +02:00
translation : bpy . props . FloatVectorProperty (
update = update_callback ,
name = " Translation " ,
default = ( 0.0 , 0.0 , 0.0 ) ,
subtype = ' TRANSLATION ' ,
)
font_size : bpy . props . FloatProperty (
update = update_callback ,
name = " Font Size " ,
default = 1.0 ,
subtype = ' NONE ' ,
)
2024-08-21 17:31:19 +02:00
offset : bpy . props . FloatProperty (
update = update_callback ,
name = " Offset " ,
default = 0.0 ,
subtype = ' NONE ' ,
)
2024-08-08 11:30:05 +02:00
compensate_curvature : bpy . props . BoolProperty (
update = update_callback ,
name = " Compensate Curvature " ,
2024-08-23 13:54:28 +02:00
description = " Fixes curvature spacing issues for simple curves, don ' t use on curve with tiny loops. " ,
2024-08-08 11:30:05 +02:00
default = True ,
)
ignore_orientation : bpy . props . BoolProperty (
update = update_callback ,
2024-08-14 16:15:25 +02:00
name = " Ignore Curve Orientation " ,
2024-08-08 11:30:05 +02:00
default = False ,
)
2024-06-27 14:56:43 +02:00
distribution_type : bpy . props . StringProperty ( )
2024-08-14 11:26:19 +02:00
glyphs : bpy . props . CollectionProperty ( type = ABC3D_glyph_properties )
2024-05-21 18:00:49 +02:00
#TODO: simply, merge, cut cut cut
2024-08-14 11:26:19 +02:00
class ABC3D_data ( bpy . types . PropertyGroup ) :
2024-08-21 14:42:53 +02:00
available_fonts : bpy . props . CollectionProperty ( type = ABC3D_available_font , name = " Available fonts " )
2024-08-23 12:28:01 +02:00
def active_font_index_update ( self , context ) :
if len ( self . available_fonts ) < = self . active_font_index :
self . active_font_index = len ( self . available_fonts ) - 1
active_font_index : bpy . props . IntProperty (
default = - 1 ,
update = active_font_index_update ,
)
2024-08-21 14:42:53 +02:00
available_texts : bpy . props . CollectionProperty ( type = ABC3D_text_properties , name = " Available texts " )
2024-08-04 12:52:37 +02:00
def active_text_index_update ( self , context ) :
if self . active_text_index != - 1 :
o = self . available_texts [ self . active_text_index ] . text_object
# active_text_index changed. so let's update the selection
# check if it is already selected
# or perhaps one of the glyphs
if not o . select_get ( ) and not len ( [ c for c in o . children if c . select_get ( ) ] ) > 0 :
bpy . ops . object . select_all ( action = " DESELECT " )
o . select_set ( True )
bpy . context . view_layer . objects . active = o
# else:
# print("already selected")
active_text_index : bpy . props . IntProperty ( update = active_text_index_update )
2024-05-21 18:00:49 +02:00
2024-08-23 16:32:40 +02:00
# def font_path_update_callback(self, context):
# butils.ShowMessageBox("Friendly Reminder", message="do not forget to click on 'Install new font'")
# bpy.ops.abc3d.install_font()
# stupid hack for Mac OS
font_path : bpy . props . StringProperty (
name = " Font path " ,
description = " Install a *.glb or *.gltf fontfile from disk " ,
default = " " ,
maxlen = 1024 ,
# update=font_path_update_callback,
subtype = " FILE_PATH " )
2024-08-26 18:48:43 +02:00
export_dir : bpy . props . StringProperty (
name = " Export Directory " ,
description = f " The directory in which we will export fonts. \n If it is blank, we will export to the addon assets path. \n This is where the fonts are installed. " ,
subtype = " DIR_PATH " )
2024-08-23 16:32:40 +02:00
2024-08-14 11:26:19 +02:00
class ABC3D_UL_fonts ( bpy . types . UIList ) :
2024-05-21 18:00:49 +02:00
def draw_item ( self , context , layout , data , item , icon , active_data , active_propname , index ) :
2024-08-08 11:28:31 +02:00
layout . label ( text = f " { index } : { item . font_name } { item . face_name } " ) # avoids renaming the item by accident
2024-05-21 18:00:49 +02:00
def invoke ( self , context , event ) :
pass
2024-08-14 11:26:19 +02:00
class ABC3D_UL_texts ( bpy . types . UIList ) :
2024-06-27 14:56:43 +02:00
def draw_item ( self , context , layout , data , item , icon , active_data , active_propname , index ) :
split = layout . split ( factor = 0.3 )
2024-07-10 16:34:43 +02:00
split . label ( text = " Id: %d " % ( item . text_id ) )
2024-06-27 14:56:43 +02:00
split . label ( text = f " { item . text } " ) # avoids renaming the item by accident
def invoke ( self , context , event ) :
pass
2024-08-14 11:26:19 +02:00
class ABC3D_PT_Panel ( bpy . types . Panel ) :
2024-08-04 12:52:37 +02:00
bl_label = f " { __name__ } panel "
2024-08-14 11:26:19 +02:00
bl_category = " ABC3D "
2024-08-04 12:52:37 +02:00
bl_space_type = " VIEW_3D "
bl_region_type = " UI "
def draw ( self , context ) :
layout = self . layout
2024-08-14 13:39:47 +02:00
icon = ' NONE '
if len ( context . scene . abc3d_data . available_fonts ) == 0 :
icon = ' ERROR '
layout . row ( ) . label ( text = ' no fonts loaded yet ' )
layout . operator ( f " { __name__ } .load_installed_fonts " , text = " load installed fonts " , icon = icon )
2024-10-31 19:33:22 +01:00
layout . operator ( f " { __name__ } .open_asset_directory " , text = " open asset directory " , icon = ' FILEBROWSER ' )
2024-08-04 12:52:37 +02:00
2024-08-26 18:48:43 +02:00
class ABC3D_PT_LoadFontPanel ( bpy . types . Panel ) :
bl_label = " Install a new font "
bl_parent_id = " ABC3D_PT_Panel "
bl_category = " ABC3D "
bl_space_type = " VIEW_3D "
bl_region_type = " UI "
2024-08-04 12:52:37 +02:00
2024-08-26 18:48:43 +02:00
def draw ( self , context ) :
layout = self . layout
wm = context . window_manager
scene = context . scene
2024-08-04 12:52:37 +02:00
2024-08-26 18:48:43 +02:00
abc3d_data = scene . abc3d_data
2024-08-04 12:52:37 +02:00
2024-08-26 18:48:43 +02:00
box = layout . box ( )
box . row ( ) . label ( text = " 1. Select fontfile " )
box . row ( ) . prop ( context . scene . abc3d_data , " font_path " )
box . row ( ) . label ( text = " 2. Install it: " )
box . row ( ) . operator ( f " { __name__ } .install_font " , text = ' Install new font ' )
2024-08-04 12:52:37 +02:00
2024-08-14 11:26:19 +02:00
class ABC3D_PT_FontList ( bpy . types . Panel ) :
2024-08-04 12:52:37 +02:00
bl_label = " Font List "
2024-08-14 11:26:19 +02:00
bl_parent_id = " ABC3D_PT_Panel "
bl_category = " ABC3D "
2024-08-04 12:52:37 +02:00
bl_space_type = " VIEW_3D "
bl_region_type = " UI "
def draw ( self , context ) :
layout = self . layout
wm = context . window_manager
scene = context . scene
2024-08-14 11:26:19 +02:00
abc3d_data = scene . abc3d_data
2024-08-04 12:52:37 +02:00
2024-08-21 14:42:53 +02:00
layout . label ( text = " Available Fonts " )
2024-08-14 11:26:19 +02:00
layout . template_list ( " ABC3D_UL_fonts " , " " , abc3d_data , " available_fonts " , abc3d_data , " active_font_index " )
2024-08-21 14:42:53 +02:00
if abc3d_data . active_font_index > = 0 :
available_font = abc3d_data . available_fonts [ abc3d_data . active_font_index ]
font_name = available_font . font_name
face_name = available_font . face_name
available_glyphs = sorted ( 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 . row ( ) . label ( text = f " Font Name: { font_name } " )
box . row ( ) . label ( text = f " Face Name: { face_name } " )
n = 16
n_rows = int ( len ( available_glyphs ) / n )
box . row ( ) . label ( text = f " Glyphs: " )
2024-08-21 14:46:47 +02:00
subbox = box . box ( )
2024-08-21 14:42:53 +02:00
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 ] )
scale_y = 0.5
2024-08-21 14:46:47 +02:00
row = subbox . row ( ) ; row . scale_y = scale_y ; row . alignment = ' CENTER '
2024-08-21 14:42:53 +02:00
row . label ( text = text )
n_rows = int ( len ( loaded_glyphs ) / n )
2024-08-21 15:09:53 +02:00
box . row ( ) . label ( text = f " Loaded/Used Glyphs: " )
2024-08-21 14:46:47 +02:00
subbox = box . box ( )
2024-08-21 14:42:53 +02:00
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 ] )
scale_y = 0.5
2024-08-21 14:46:47 +02:00
row = subbox . row ( ) ; row . scale_y = scale_y
2024-08-21 14:42:53 +02:00
row . label ( text = text )
2024-08-28 17:25:06 +02:00
row = layout . row ( )
oper = row . operator ( f " { __name__ } .load_font " , text = ' Load all glyphs in memory ' )
oper . font_name = font_name
oper . face_name = face_name
2024-08-21 14:42:53 +02:00
2024-08-04 12:52:37 +02:00
2024-08-14 11:26:19 +02:00
class ABC3D_PT_TextPlacement ( bpy . types . Panel ) :
2024-08-04 12:52:37 +02:00
bl_label = " Place Text "
2024-08-14 11:26:19 +02:00
bl_parent_id = " ABC3D_PT_Panel "
bl_category = " ABC3D "
2024-08-04 12:52:37 +02:00
bl_space_type = " VIEW_3D "
bl_region_type = " UI "
2024-08-04 13:32:01 +02:00
can_place = False
@classmethod
def poll ( self , context ) :
if type ( context . active_object ) != type ( None ) and context . active_object . type == ' CURVE ' :
self . can_place = True
else :
self . can_place = False
return True
2024-08-04 12:52:37 +02:00
def draw ( self , context ) :
layout = self . layout
wm = context . window_manager
scene = context . scene
2024-08-14 11:26:19 +02:00
abc3d_data = scene . abc3d_data
2024-08-04 12:52:37 +02:00
2024-08-04 13:32:01 +02:00
placerow = layout . row ( )
placerow . enabled = self . can_place
2024-08-05 12:59:45 +02:00
placerow . operator ( f " { __name__ } .placetext " , text = ' Place Text ' )
2024-08-04 13:32:01 +02:00
if not self . can_place :
layout . label ( text = " Cannot place Text. " )
layout . label ( text = " Select a curve as active object. " )
2024-05-08 16:19:47 +02:00
2024-08-14 11:26:19 +02:00
class ABC3D_PT_TextManagement ( bpy . types . Panel ) :
2024-08-04 12:52:37 +02:00
bl_label = " Text Management "
2024-08-14 11:26:19 +02:00
bl_parent_id = " ABC3D_PT_Panel "
bl_category = " ABC3D "
2024-05-08 16:19:47 +02:00
bl_space_type = " VIEW_3D "
bl_region_type = " UI "
2024-08-21 17:34:13 +02:00
bl_options = { " DEFAULT_CLOSED " }
2024-05-08 16:19:47 +02:00
2024-08-04 12:52:37 +02:00
# TODO: perhaps this should be done in a periodic timer
2024-06-27 14:56:43 +02:00
@classmethod
def poll ( self , context ) :
scene = context . scene
2024-08-14 11:26:19 +02:00
abc3d_data = scene . abc3d_data
2024-08-04 12:52:37 +02:00
# TODO: update available_texts
2024-06-28 11:24:01 +02:00
def update ( ) :
2024-08-04 12:52:37 +02:00
if bpy . context . screen . is_animation_playing :
return
active_text_index = - 1
2024-06-28 11:24:01 +02:00
remove_list = [ ]
2024-08-14 11:26:19 +02:00
for i , t in enumerate ( abc3d_data . available_texts ) :
2024-06-28 11:24:01 +02:00
if type ( t . text_object ) == type ( None ) :
remove_list . append ( i )
2024-07-02 12:22:24 +02:00
continue
remove_me = True
for c in t . text_object . children :
2024-08-14 14:57:34 +02:00
if len ( c . users_collection ) > 0 and ( c . get ( f " { utils . prefix ( ) } _linked_textobject " ) ) != type ( None ) and c . get ( f " { utils . prefix ( ) } _linked_textobject " ) == t . text_id :
2024-07-02 12:22:24 +02:00
remove_me = False
2024-07-10 16:34:43 +02:00
# not sure how to solve this reliably atm,
# we need to reassign the glyph, but also get the proper properties from glyph_properties
# these might be there in t.glyphs, but linked to removed objects
# or they might be lost
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 )
2024-08-04 12:52:37 +02:00
# for g in t.glyphs:
# if type(g) == type(None):
# print("IS NONE")
# if type(g.glyph_object) == type(None):
# print("go IS NONE")
# else:
# if g.glyph_object == c:
# # print(g.glyph_object.name)
# pass
2024-07-10 16:34:43 +02:00
2024-07-02 12:22:24 +02:00
if remove_me :
remove_list . append ( i )
2024-06-28 11:24:01 +02:00
for i in remove_list :
2024-08-14 14:57:34 +02:00
if type ( abc3d_data . available_texts [ i ] . text_object ) != type ( None ) :
2024-08-21 14:42:53 +02:00
mom = abc3d_data . available_texts [ i ] . text_object
def delif ( o , p ) :
if p in o :
del o [ p ]
delif ( mom , f " { utils . prefix ( ) } _linked_textobject " )
delif ( mom , f " { utils . prefix ( ) } _font_name " )
delif ( mom , f " { utils . prefix ( ) } _face_name " )
delif ( mom , f " { utils . prefix ( ) } _font_size " )
delif ( mom , f " { utils . prefix ( ) } _letter_spacing " )
delif ( mom , f " { utils . prefix ( ) } _orientation " )
delif ( mom , f " { utils . prefix ( ) } _translation " )
2024-08-21 17:31:19 +02:00
delif ( mom , f " { utils . prefix ( ) } _offset " )
2024-08-14 11:26:19 +02:00
abc3d_data . available_texts . remove ( i )
2024-06-28 11:24:01 +02:00
2024-08-14 11:26:19 +02:00
for i , t in enumerate ( abc3d_data . available_texts ) :
2024-07-10 16:34:43 +02:00
if context . active_object == t . text_object :
2024-08-04 12:52:37 +02:00
active_text_index = i
2024-07-10 16:34:43 +02:00
if ( hasattr ( context . active_object , " parent " ) and
context . active_object . parent == t . text_object ) :
2024-08-04 12:52:37 +02:00
active_text_index = i
2024-08-14 11:26:19 +02:00
if active_text_index != abc3d_data . active_text_index :
abc3d_data . active_text_index = active_text_index
2024-06-27 14:56:43 +02:00
2024-08-21 14:42:53 +02:00
# butils.run_in_main_thread(update)
2024-06-27 14:56:43 +02:00
return True
2024-05-08 16:19:47 +02:00
def draw ( self , context ) :
layout = self . layout
wm = context . window_manager
scene = context . scene
2024-08-14 11:26:19 +02:00
abc3d_data = scene . abc3d_data
2024-05-21 18:00:49 +02:00
2024-06-27 14:56:43 +02:00
layout . label ( text = " Text Objects " )
2024-08-14 11:26:19 +02:00
layout . template_list ( " ABC3D_UL_texts " , " " , abc3d_data , " available_texts " , abc3d_data , " active_text_index " )
2024-08-21 16:51:00 +02:00
layout . row ( ) . operator ( f " { __name__ } .remove_text " , text = " Remove Textobject " )
2024-08-04 12:52:37 +02:00
2024-08-14 11:26:19 +02:00
class ABC3D_PT_FontCreation ( bpy . types . Panel ) :
2024-08-04 12:52:37 +02:00
bl_label = " Font Creation "
2024-08-14 11:26:19 +02:00
bl_parent_id = " ABC3D_PT_Panel "
bl_category = " ABC3D "
2024-08-04 12:52:37 +02:00
bl_space_type = " VIEW_3D "
bl_region_type = " UI "
bl_options = { " DEFAULT_CLOSED " }
def draw ( self , context ) :
layout = self . layout
wm = context . window_manager
scene = context . scene
2024-08-14 11:26:19 +02:00
abc3d_data = scene . abc3d_data
2024-08-04 12:52:37 +02:00
2024-08-12 11:22:44 +02:00
layout . row ( ) . operator ( f " { __name__ } .create_font_from_objects " , text = ' Create/Extend Font ' )
2024-08-26 18:48:43 +02:00
box = layout . box ( )
box . row ( ) . label ( text = " Exporting a fontfile " )
box . row ( ) . label ( text = " 1. Select export directory: " )
box . prop ( abc3d_data , ' export_dir ' )
box . row ( ) . label ( text = " 2. More options and export: " )
box . row ( ) . operator ( f " { __name__ } .save_font_to_file " , text = ' Export Font To File ' )
2024-08-14 11:26:19 +02:00
layout . row ( ) . operator ( f " { __name__ } .toggle_abc3d_collection " , text = ' Toggle Collection ' )
2024-08-07 11:56:13 +02:00
box = layout . box ( )
box . label ( text = " 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__ } .align_metrics " , text = ' Align Metrics ' )
box . row ( ) . operator ( f " { __name__ } .align_metrics_to_active_object " , text = ' Align Metrics to Active Object ' )
2024-08-08 11:32:39 +02:00
layout . row ( ) . operator ( f " { __name__ } .temporaryhelper " , text = ' Debug Function Do Not Use ' )
2024-05-21 18:00:49 +02:00
2024-08-14 11:26:19 +02:00
class ABC3D_PT_TextPropertiesPanel ( bpy . types . Panel ) :
2024-06-27 14:56:43 +02:00
bl_label = " Text Properties "
2024-08-14 11:26:19 +02:00
bl_parent_id = " ABC3D_PT_TextManagement "
bl_category = " ABC3D "
2024-06-27 14:56:43 +02:00
bl_space_type = " VIEW_3D "
bl_region_type = " UI "
def get_active_text_properties ( self ) :
2024-08-04 15:12:38 +02:00
if type ( bpy . context . active_object ) != type ( None ) : # and bpy.context.object.select_get():
2024-08-14 11:26:19 +02:00
for t in bpy . context . scene . abc3d_data . available_texts :
2024-08-04 15:12:38 +02:00
if bpy . context . active_object == t . text_object :
2024-06-27 14:56:43 +02:00
return t
2024-08-04 15:12:38 +02:00
if bpy . context . active_object . parent == t . text_object :
2024-06-27 14:56:43 +02:00
return t
return None
2024-08-17 11:02:26 +02:00
# def font_items_callback(self, context):
# items = []
# fonts = Font.get_loaded_fonts_and_faces()
# for f in fonts:
# items.append((f"{f[0]} {f[1]}", f"{f[0]} {f[1]}", ""))
# return items
# def font_default_callback(self, context):
# t = self.get_active_text_properties(self)
# if type(t) != type(None):
# return f"{t.font_name} {t.face_name}"
# else:
# return None
# def font_update_callback(self, context):
# font_name, face_name = self.font.split(" ")
# t = self.get_active_text_properties(self)
# t.font_name = font_name
# t.face_name = face_name
# butils.set_text_on_curve(t)
# font: bpy.props.EnumProperty(
# items=font_items_callback,
# default=font_default_callback,
# update=font_update_callback,
# )
2024-06-27 14:56:43 +02:00
@classmethod
def poll ( self , context ) :
return type ( self . get_active_text_properties ( self ) ) != type ( None )
def draw ( self , context ) :
layout = self . layout
wm = context . window_manager
scene = context . scene
2024-08-14 11:26:19 +02:00
abc3d_data = scene . abc3d_data
2024-06-27 14:56:43 +02:00
props = self . get_active_text_properties ( )
2024-06-28 11:24:29 +02:00
if type ( props ) == type ( None ) or type ( props . text_object ) == type ( None ) :
# this should not happen
2024-08-04 12:52:37 +02:00
# as then polling does not work
# however, we are paranoid
2024-06-27 14:56:43 +02:00
return
layout . label ( text = f " Mom: { props . text_object . name } " )
2024-08-17 11:02:26 +02:00
layout . row ( ) . prop ( props , " font " )
2024-08-04 12:52:37 +02:00
layout . row ( ) . prop ( props , " text " )
2024-06-27 14:56:43 +02:00
layout . row ( ) . prop ( props , " letter_spacing " )
2024-08-05 12:59:07 +02:00
layout . row ( ) . prop ( props , " font_size " )
2024-08-21 17:31:19 +02:00
layout . row ( ) . prop ( props , " offset " )
2024-08-08 11:30:05 +02:00
layout . row ( ) . prop ( props , " compensate_curvature " )
layout . row ( ) . prop ( props , " ignore_orientation " )
2024-08-05 12:59:07 +02:00
layout . column ( ) . prop ( props , " translation " )
2024-08-04 12:52:37 +02:00
layout . column ( ) . prop ( props , " orientation " )
2024-05-08 16:19:47 +02:00
2024-08-21 16:06:00 +02:00
class ABC3D_OT_InstallFont ( bpy . types . Operator ) :
""" Install or load Fontfile from path above.
2024-08-04 12:52:37 +02:00
( Format must be * . glb or * . gltf ) """
2024-08-21 16:06:00 +02:00
bl_idname = f " { __name__ } .install_font "
2024-05-08 16:19:47 +02:00
bl_label = " Load Font "
bl_options = { ' REGISTER ' , ' UNDO ' }
2024-08-23 16:32:40 +02:00
def font_path_update_callback ( self , context ) :
if os . path . exists ( self . font_path ) :
print ( f " { self . font_path } does exist " )
else :
print ( f " { self . font_path } does not exist " )
font_path : bpy . props . StringProperty (
name = " Font path " ,
description = " Install a *.glb or *.gltf fontfile from disk " ,
default = " " ,
maxlen = 1024 ,
# update=font_path_update_callback,
subtype = " FILE_PATH " )
2024-08-21 16:06:00 +02:00
install_in_assets : bpy . props . BoolProperty (
name = " install in assets " ,
description = " install the font in the assets directory of the addon " ,
2024-08-23 12:28:48 +02:00
default = True ,
2024-08-21 16:06:00 +02:00
)
load_into_memory : bpy . props . BoolProperty ( name = " load font data into memory " ,
description = " if false, it will load font data on demand " ,
default = False )
def draw ( self , context ) :
2024-08-23 16:32:40 +02:00
abc3d_data = context . scene . abc3d_data
2024-08-21 16:06:00 +02:00
layout = self . layout
2024-08-23 16:32:40 +02:00
# layout.row().prop(self, "font_path") # crashes on Mac OS?
2024-08-26 18:48:43 +02:00
# layout.row().prop(abc3d_data, "font_path") # closes the stupid panel on Mac OS..
2024-08-21 16:06:00 +02:00
layout . row ( ) . prop ( self , " install_in_assets " )
if not self . install_in_assets and not self . load_into_memory :
layout . label ( text = " If the fontfile is not installed, " )
layout . label ( text = " and the font is not loaded in memory completely, " )
layout . label ( text = " the fontfile should not be moved. " )
layout . row ( ) . prop ( self , " load_into_memory " )
if self . load_into_memory :
layout . label ( text = " Loading font files can take a long time " )
layout . label ( text = " and use a lot of RAM. " )
layout . label ( text = " We recommend not doing this and let us " )
layout . label ( text = " load the font data on demand. " )
def invoke ( self , context , event ) :
2024-08-23 16:32:40 +02:00
abc3d_data = context . scene . abc3d_data
self . font_path = abc3d_data . font_path
if not os . path . exists ( self . font_path ) :
bpy . app . timers . register ( lambda : butils . ShowMessageBox (
title = f " { __name__ } Warning " ,
icon = " ERROR " ,
message = [
f " We believe the font path ( { self . font_path } ) does not exist. " ,
f " Did you select your fontfile in the field above the ' Install new font ' -button? " ,
] ,
) , first_interval = 0.1 )
2024-08-21 16:06:00 +02:00
return context . window_manager . invoke_props_dialog ( self )
2024-05-08 16:19:47 +02:00
def execute ( self , context ) :
scene = bpy . context . scene
2024-08-23 16:32:40 +02:00
abc3d_data = context . scene . abc3d_data
if not os . path . exists ( self . font_path ) :
2024-05-28 14:11:32 +02:00
butils . ShowMessageBox (
title = f " { __name__ } Warning " ,
icon = " ERROR " ,
2024-08-23 16:32:40 +02:00
message = [
f " Could not install font. " ,
f " We believe the font path ( { self . font_path } ) does not exist. " ,
f " If this is an error, please let us know. " ,
] ,
2024-05-28 14:11:32 +02:00
)
return { ' CANCELLED ' }
2024-05-08 16:19:47 +02:00
2024-08-21 16:06:00 +02:00
if self . install_in_assets :
preferences = getPreferences ( context )
2024-08-23 16:32:40 +02:00
filename = os . path . basename ( self . font_path )
2024-08-21 16:06:00 +02:00
target = os . path . join ( preferences . assets_dir , " fonts " , filename )
import shutil
2024-08-23 12:28:48 +02:00
os . makedirs ( os . path . dirname ( target ) , exist_ok = True )
2024-08-23 16:32:40 +02:00
shutil . copyfile ( self . font_path , target )
2024-08-21 16:06:00 +02:00
# def register_load(target, load=False):
# print(f"registering installed fonts")
# bpy.app.timers.register(lambda: register_load(target, self.load_into_memory), first_interval=5)
butils . register_font_from_filepath ( target )
if self . load_into_memory :
butils . load_font_from_filepath ( target )
butils . update_available_fonts ( )
else :
2024-08-23 16:32:40 +02:00
butils . register_font_from_filepath ( self . font_path )
2024-08-21 16:06:00 +02:00
if self . load_into_memory :
2024-08-23 16:32:40 +02:00
butils . load_font_from_filepath ( self . font_path )
2024-05-08 16:19:47 +02:00
return { ' FINISHED ' }
2024-10-31 19:33:22 +01:00
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 ' }
2024-08-14 13:39:47 +02:00
class ABC3D_OT_LoadInstalledFonts ( bpy . types . Operator ) :
""" Load installed fontfiles from datapath. """
bl_idname = f " { __name__ } .load_installed_fonts "
bl_label = " Loading installed Fonts. "
bl_options = { ' REGISTER ' , ' UNDO ' }
2024-08-21 14:42:53 +02:00
load_into_memory : bpy . props . BoolProperty ( name = " load font data into memory " ,
description = " if false, it will load font data on demand " ,
default = False )
2024-08-14 13:39:47 +02:00
def draw ( self , context ) :
layout = self . layout
2024-08-21 14:42:53 +02:00
layout . row ( ) . prop ( self , " load_into_memory " )
if self . load_into_memory :
layout . label ( text = " Loading font files can take a long time " )
layout . label ( text = " and use a lot of RAM. " )
layout . label ( text = " We recommend not doing this and let us " )
layout . label ( text = " load the font data on demand. " )
2024-08-14 13:39:47 +02:00
def invoke ( self , context , event ) :
return context . window_manager . invoke_props_dialog ( self )
def execute ( self , context ) :
2024-08-23 16:32:40 +02:00
print ( " EXECUTE LOAD INSTALLED FONTS " )
2024-08-14 13:39:47 +02:00
scene = bpy . context . scene
2024-08-21 14:42:53 +02:00
if self . load_into_memory :
butils . load_installed_fonts ( )
else :
butils . register_installed_fonts ( )
2024-08-14 14:57:34 +02:00
butils . ShowMessageBox ( " Loading Fonts " ,
' INFO ' ,
" Updating Data Structures. " )
2024-08-14 13:39:47 +02:00
butils . update_available_fonts ( )
2024-08-14 14:57:34 +02:00
butils . ShowMessageBox ( " Loading Fonts " ,
' INFO ' ,
" Done loading installed fonts. " )
2024-08-14 13:39:47 +02:00
return { ' FINISHED ' }
2024-08-28 17:25:06 +02:00
class ABC3D_OT_LoadFont ( bpy . types . Operator ) :
""" Load all glyphs from a specific font in memory. \n This can take a while and slow down Blender. """
bl_idname = f " { __name__ } .load_font "
bl_label = " Loading Font. "
bl_options = { ' REGISTER ' , ' UNDO ' }
font_name : bpy . props . StringProperty ( )
face_name : bpy . props . StringProperty ( )
def execute ( self , context ) :
filepaths = Font . fonts [ self . font_name ] . faces [ self . face_name ] . filepaths
for f in filepaths :
butils . load_font_from_filepath ( f )
return { ' FINISHED ' }
2024-08-14 11:26:19 +02:00
class ABC3D_OT_AddDefaultMetrics ( bpy . types . Operator ) :
2024-08-07 11:56:13 +02:00
""" Add default metrics to selected objects """
bl_idname = f " { __name__ } .add_default_metrics "
bl_label = " Add default metrics "
bl_options = { ' REGISTER ' , ' UNDO ' }
def execute ( self , context ) :
objects = bpy . context . selected_objects
butils . add_default_metrics_to_objects ( objects )
return { ' FINISHED ' }
2024-08-14 11:26:19 +02:00
class ABC3D_OT_RemoveMetrics ( bpy . types . Operator ) :
2024-08-07 11:56:13 +02:00
""" Remove metrics from selected objects """
bl_idname = f " { __name__ } .remove_metrics "
bl_label = " Remove metrics "
bl_options = { ' REGISTER ' , ' UNDO ' }
def execute ( self , context ) :
objects = bpy . context . selected_objects
butils . remove_metrics_from_objects ( objects )
return { ' FINISHED ' }
2024-08-14 11:26:19 +02:00
class ABC3D_OT_AlignMetricsToActiveObject ( bpy . types . Operator ) :
2024-08-07 11:56:13 +02:00
""" Align metrics of selected objects to metrics of active object """
bl_idname = f " { __name__ } .align_metrics_to_active_object "
bl_label = " Align metrics to active object "
bl_options = { ' REGISTER ' , ' UNDO ' }
def execute ( self , context ) :
objects = bpy . context . selected_objects
butils . align_metrics_of_objects_to_active_object ( objects )
return { ' FINISHED ' }
2024-08-14 11:26:19 +02:00
class ABC3D_OT_AlignMetrics ( bpy . types . Operator ) :
2024-08-07 11:56:13 +02:00
""" Align metrics of selected objects to each other """
bl_idname = f " { __name__ } .align_metrics "
bl_label = " Align metrics "
bl_options = { ' REGISTER ' , ' UNDO ' }
def execute ( self , context ) :
objects = bpy . context . selected_objects
butils . align_metrics_of_objects ( objects )
return { ' FINISHED ' }
2024-08-14 11:26:19 +02:00
class ABC3D_OT_TemporaryHelper ( bpy . types . Operator ) :
2024-08-26 18:48:43 +02:00
""" Temporary Helper ABC3D \n This could do anything. \n It ' s just there to make random functions available for testing. """
2024-08-04 12:52:37 +02:00
bl_idname = f " { __name__ } .temporaryhelper "
2024-06-27 14:56:43 +02:00
bl_label = " Temp Font "
bl_options = { ' REGISTER ' , ' UNDO ' }
def execute ( self , context ) :
global shared
scene = bpy . context . scene
2024-08-14 11:26:19 +02:00
abc3d_data = scene . abc3d_data
2024-06-27 14:56:43 +02:00
2024-08-14 11:26:19 +02:00
# butils.load_font_from_filepath("/home/jrkb/.config/blender/4.1/datafiles/abc3d/fonts/NM_Origin.glb")
2024-08-14 10:50:57 +02:00
butils . update_available_fonts ( )
# objects = bpy.context.selected_objects
# butils.add_default_metrics_to_objects(objects)
2024-08-07 11:56:13 +02:00
# reference_bound_box = None
# for o in objects:
# bb = o.bound_box
# reference_bound_box = butils.get_max_bound_box(bb, reference_bound_box)
# for o in objects:
# metrics = butils.get_metrics_bound_box(o.bound_box, reference_bound_box)
# butils.add_metrics_obj_from_bound_box(o, metrics)
# bpy.app.timers.register(lambda: butils.remove_metrics_from_objects(objects), first_interval=5)
2024-06-27 14:56:43 +02:00
return { ' FINISHED ' }
2024-05-28 14:11:32 +02:00
2024-08-21 16:51:00 +02:00
class ABC3D_OT_RemoveText ( bpy . types . Operator ) :
""" Remove Text 3D """
bl_idname = f " { __name__ } .remove_text "
bl_label = " Remove Text "
bl_options = { ' REGISTER ' , ' UNDO ' }
remove_objects : bpy . props . BoolProperty (
name = " Remove Objects " ,
description = " Remove both ABC3D text functionality and the objects/meshes " ,
default = True )
def invoke ( self , context , event ) :
wm = context . window_manager
return wm . invoke_props_dialog ( self )
def execute ( self , context ) :
abc3d_data = context . scene . abc3d_data
if abc3d_data . active_text_index < 0 :
butils . ShowMessageBox (
title = " No text selected " ,
message = ( " Please select a text. " ) ,
icon = ' GHOST_ENABLED ' )
return { ' CANCELLED ' }
i = abc3d_data . active_text_index
if type ( abc3d_data . available_texts [ i ] . text_object ) != type ( None ) :
mom = abc3d_data . available_texts [ i ] . text_object
def delif ( o , p ) :
if p in o :
del o [ p ]
delif ( mom , f " { utils . prefix ( ) } _linked_textobject " )
delif ( mom , f " { utils . prefix ( ) } _font_name " )
delif ( mom , f " { utils . prefix ( ) } _face_name " )
delif ( mom , f " { utils . prefix ( ) } _font_size " )
delif ( mom , f " { utils . prefix ( ) } _letter_spacing " )
delif ( mom , f " { utils . prefix ( ) } _orientation " )
delif ( mom , f " { utils . prefix ( ) } _translation " )
2024-08-21 17:31:19 +02:00
delif ( mom , f " { utils . prefix ( ) } _offset " )
2024-08-21 16:51:00 +02:00
if self . remove_objects :
remove_list = [ ]
for g in abc3d_data . available_texts [ i ] . glyphs :
if type ( g ) != type ( None ) :
remove_list . append ( g . glyph_object )
butils . simply_delete_objects ( remove_list )
abc3d_data . available_texts . remove ( i )
return { ' FINISHED ' }
2024-08-14 11:26:19 +02:00
class ABC3D_OT_PlaceText ( bpy . types . Operator ) :
2024-08-04 13:32:01 +02:00
""" Place Text 3D on active object """
2024-08-04 12:52:37 +02:00
bl_idname = f " { __name__ } .placetext "
bl_label = " Place Text "
2024-05-08 16:19:47 +02:00
bl_options = { ' REGISTER ' , ' UNDO ' }
2024-08-14 16:15:25 +02:00
def font_items_callback ( self , context ) :
items = [ ]
fonts = Font . get_loaded_fonts_and_faces ( )
for f in fonts :
items . append ( ( f " { f [ 0 ] } { f [ 1 ] } " , f " { f [ 0 ] } { f [ 1 ] } " , " " ) )
return items
2024-08-14 14:57:34 +02:00
2024-08-14 16:15:25 +02:00
def font_update_callback ( self , context ) :
font_name , face_name = self . font . split ( " " )
self . font_name = font_name
self . face_name = face_name
font_name : bpy . props . StringProperty (
options = { ' HIDDEN ' }
)
face_name : bpy . props . StringProperty (
options = { ' HIDDEN ' }
)
font : bpy . props . EnumProperty ( items = font_items_callback ,
update = font_update_callback
)
text : bpy . props . StringProperty (
name = " Text " ,
description = " The text. " ,
2024-08-26 18:48:43 +02:00
default = " ABC3D " ,
2024-08-14 16:15:25 +02:00
maxlen = 1024 ,
)
# target_object: bpy.props.PointerProperty(
# name="The Target Object",
# description="The target, which will be populated by character children of text.",
# type=bpy.types.Object,
# )
letter_spacing : bpy . props . FloatProperty (
name = " Letter Spacing " ,
description = " Letter Spacing " ,
default = 0.0 ,
)
font_size : bpy . props . FloatProperty (
name = " Font Size " ,
default = 1.0 ,
subtype = ' NONE ' ,
)
2024-08-21 17:31:19 +02:00
offset : bpy . props . FloatProperty (
name = " Offset " ,
default = 0.0 ,
subtype = ' NONE ' ,
)
2024-08-14 16:15:25 +02:00
translation : bpy . props . FloatVectorProperty (
name = " Translation " ,
default = ( 0.0 , 0.0 , 0.0 ) ,
subtype = ' TRANSLATION ' ,
)
orientation : bpy . props . FloatVectorProperty (
name = " Orientation " ,
default = ( 1.5707963267948966 , 0.0 , 0.0 ) , # 90 degrees in radians
subtype = ' EULER ' ,
)
def invoke ( self , context , event ) :
wm = context . window_manager
2024-08-17 11:02:26 +02:00
self . font_update_callback ( context )
2024-08-14 16:15:25 +02:00
return wm . invoke_props_dialog ( self )
2024-08-14 14:57:34 +02:00
2024-05-08 16:19:47 +02:00
def execute ( self , context ) :
global shared
scene = bpy . context . scene
2024-08-14 11:26:19 +02:00
abc3d_data = scene . abc3d_data
2024-06-27 14:56:43 +02:00
2024-08-14 16:15:25 +02:00
selected = bpy . context . view_layer . objects . active
2024-05-28 14:11:32 +02:00
if selected :
2024-08-14 16:15:25 +02:00
# font = abc3d_data.available_fonts[abc3d_data.active_font_index]
# font_name = font.font_name
# face_name = font.face_name
2024-06-27 14:56:43 +02:00
distribution_type = ' DEFAULT '
2024-07-10 16:34:43 +02:00
text_id = 0
2024-08-14 11:26:19 +02:00
for i , tt in enumerate ( abc3d_data . available_texts ) :
2024-07-10 16:34:43 +02:00
while text_id == tt . text_id :
text_id = text_id + 1
2024-08-14 11:26:19 +02:00
t = abc3d_data . available_texts . add ( )
2024-08-21 14:42:53 +02:00
# If you wish to set a value and not fire an update, set the id property.
# A property defined via bpy.props for example ob.prop is stored as ob["prop"] once set to non default.
t [ ' text_id ' ] = text_id
2024-08-23 13:55:40 +02:00
# t['font'] = self.font # enums want to be set as attribute
2024-08-21 14:42:53 +02:00
t [ ' font_name ' ] = self . font_name
t [ ' face_name ' ] = self . face_name
2024-06-27 14:56:43 +02:00
t . text_object = selected
2024-08-21 14:42:53 +02:00
t [ ' text ' ] = self . text
t [ ' letter_spacing ' ] = self . letter_spacing
t [ ' font_size ' ] = self . font_size
2024-08-21 17:31:19 +02:00
t [ ' offset ' ] = self . offset
2024-08-21 14:42:53 +02:00
t [ ' translation ' ] = self . translation
t [ ' orientation ' ] = self . orientation
t [ ' distribution_type ' ] = distribution_type
2024-08-23 13:55:40 +02:00
t . font = self . font # enums want to be set as attribute
# this also calls the update function
# so we don't need to prepare/set again
# no need for these:
# butils.prepare_text(t.font_name,
# t.face_name,
# t.text)
# or this:
# butils.set_text_on_curve(t)
# else:
2024-05-28 14:11:32 +02:00
butils . ShowMessageBox (
title = " No object selected " ,
message = (
" Please select an object. " ,
" It will be used to put the type on. " ,
2024-08-04 12:52:37 +02:00
" Thank you :) " ) ,
2024-05-28 14:11:32 +02:00
icon = ' GHOST_ENABLED ' )
2024-05-21 18:00:49 +02:00
return { ' FINISHED ' }
2024-08-14 11:26:19 +02:00
class ABC3D_OT_ToggleABC3DCollection ( bpy . types . Operator ) :
""" Toggle ABC3D Collection """
bl_idname = f " { __name__ } .toggle_abc3d_collection "
2024-05-21 18:00:49 +02:00
bl_label = " Toggle Collection visibility "
bl_options = { ' REGISTER ' , ' UNDO ' }
def execute ( self , context ) :
scene = context . scene
2024-08-14 11:26:19 +02:00
fontcollection = bpy . data . collections . get ( " ABC3D " )
2024-05-21 18:00:49 +02:00
if fontcollection is None :
2024-08-26 18:48:43 +02:00
self . report ( { ' INFO ' } , f " { bl_info [ ' name ' ] } : There is no collection. Did you use or create any glyphs yet? " )
2024-05-21 18:00:49 +02:00
elif scene . collection . children . find ( fontcollection . name ) < 0 :
scene . collection . children . link ( fontcollection )
self . report ( { ' INFO ' } , f " { bl_info [ ' name ' ] } : show collection " )
else :
scene . collection . children . unlink ( fontcollection )
self . report ( { ' INFO ' } , f " { bl_info [ ' name ' ] } : hide collection " )
return { ' FINISHED ' }
2024-08-14 11:26:19 +02:00
class ABC3D_OT_SaveFontToFile ( bpy . types . Operator ) :
2024-05-28 14:11:32 +02:00
""" Save font to file """
bl_idname = f " { __name__ } .save_font_to_file "
bl_label = " Save Font "
bl_options = { ' REGISTER ' , ' UNDO ' }
2024-08-14 10:50:57 +02:00
def invoke ( self , context , event ) :
wm = context . window_manager
2024-08-26 18:48:43 +02:00
preferences = getPreferences ( context )
abc3d_data = context . scene . abc3d_data
if abc3d_data . export_dir == " " :
abc3d_data . export_dir = os . path . join ( preferences . assets_dir , " fonts " )
2024-08-14 10:50:57 +02:00
return wm . invoke_props_dialog ( self )
2024-08-26 18:48:43 +02:00
def draw ( self , context ) :
abc3d_data = context . scene . abc3d_data
layout = self . layout
layout . label ( text = " Available Fonts " )
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 ]
font_name = available_font . font_name
face_name = available_font . face_name
loaded_glyphs = sorted ( Font . fonts [ font_name ] . faces [ face_name ] . loaded_glyphs )
n = 16
n_rows = int ( len ( loaded_glyphs ) / n )
box = layout . box ( )
box . row ( ) . label ( text = f " Glyphs to be exported: " )
subbox = box . box ( )
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 ] )
scale_y = 0.5
row = subbox . row ( ) ; row . scale_y = scale_y
row . label ( text = text )
layout . prop ( abc3d_data , ' export_dir ' )
2024-05-28 14:11:32 +02:00
def execute ( self , context ) :
global shared
scene = bpy . context . scene
2024-08-14 11:26:19 +02:00
abc3d_data = scene . abc3d_data
2024-05-28 14:11:32 +02:00
2024-08-14 11:26:19 +02:00
fontcollection = bpy . data . collections . get ( " ABC3D " )
2024-05-28 14:11:32 +02:00
2024-06-27 14:56:43 +02:00
# check if all is good to proceed
2024-05-28 16:53:01 +02:00
if fontcollection is None :
self . report ( { ' INFO ' } , f " { bl_info [ ' name ' ] } : There is no collection " )
return { ' CANCELLED ' }
2024-08-14 11:26:19 +02:00
if abc3d_data . active_font_index < 0 :
2024-06-27 14:56:43 +02:00
self . report ( { ' INFO ' } , f " { bl_info [ ' name ' ] } : There is no active font " )
return { ' CANCELLED ' }
2024-05-28 16:53:01 +02:00
2024-08-14 11:26:19 +02:00
if len ( abc3d_data . available_fonts ) < = abc3d_data . active_font_index :
2024-06-27 14:56:43 +02:00
self . report ( { ' INFO ' } , f " { bl_info [ ' name ' ] } : Active font is not available " )
return { ' CANCELLED ' }
# save state to restore later
was_fontcollection_linked = scene . collection . children . find ( fontcollection . name ) > = 0
was_selection = [ ]
for obj in bpy . context . selected_objects :
was_selection . append ( obj )
was_active_object = bpy . context . view_layer . objects . active
bpy . ops . object . select_all ( action = " DESELECT " )
2024-05-28 16:53:01 +02:00
# get save data
2024-08-14 11:26:19 +02:00
selected_font = abc3d_data . available_fonts [ abc3d_data . active_font_index ]
2024-05-28 14:11:32 +02:00
2024-07-01 14:39:07 +02:00
# print(selected_font.font_name)
2024-08-08 11:28:31 +02:00
self . report ( { ' INFO ' } , f " { bl_info [ ' name ' ] } : { selected_font . font_name } { selected_font . face_name } " )
2024-06-27 14:56:43 +02:00
preferences = getPreferences ( context )
print ( f " assets folder: { preferences . assets_dir } " )
bpy . ops . scene . new ( type = ' FULL_COPY ' )
linked_collections = bpy . context . scene . collection . children . values ( )
for c in linked_collections :
bpy . context . scene . collection . children . unlink ( c )
bpy . context . scene . collection . children . link ( fontcollection )
# select what needs to be selected
export_objects = [ ]
for obj in fontcollection . objects :
if obj [ " font_name " ] == selected_font . font_name :
2024-08-08 11:30:39 +02:00
if not butils . is_metrics_object ( obj ) :
obj . select_set ( True )
export_objects . append ( obj )
2024-08-14 13:39:47 +02:00
else :
obj . select_set ( True )
butils . add_faces_to_metrics ( obj )
export_objects . append ( obj )
2024-06-27 14:56:43 +02:00
context_override = bpy . context . copy ( )
context_override [ " selected_objects " ] = list ( export_objects )
# context_override["scene"] = bpy.context.scene.copy()
with bpy . context . temp_override ( * * context_override ) :
2024-08-26 18:48:43 +02:00
filepath = f " { abc3d_data . export_dir } / { selected_font . font_name } .glb "
2024-06-28 10:24:22 +02:00
# get rid of scene extra data before export
2024-08-08 11:31:39 +02:00
scene_keys = [ ]
2024-06-28 10:24:22 +02:00
for k in bpy . context . scene . keys ( ) :
2024-08-08 11:31:39 +02:00
scene_keys . append ( k )
for k in scene_keys :
2024-06-28 10:24:22 +02:00
del bpy . context . scene [ k ]
2024-06-27 14:56:43 +02:00
# save as gltf
2024-06-28 10:24:56 +02:00
bpy . ops . export_scene . gltf (
2024-06-27 14:56:43 +02:00
filepath = filepath ,
check_existing = False ,
2024-08-26 18:48:43 +02:00
export_format = ' GLB ' , # GLB or GLTF_SEPARATE (also change filepath)
2024-06-27 14:56:43 +02:00
export_extras = True ,
use_selection = True ,
use_active_scene = True ,
)
2024-08-26 18:48:43 +02:00
bpy . app . timers . register ( lambda : bpy . ops . scene . delete ( ) , first_interval = 1 )
2024-05-28 16:53:01 +02:00
2024-08-26 18:48:43 +02:00
# bpy.ops.scene.delete()
2024-06-27 14:56:43 +02:00
# restore()
2024-05-28 16:53:01 +02:00
2024-08-26 18:48:43 +02:00
def remove_faces ( ) :
for obj in fontcollection . objects :
if obj [ " font_name " ] == selected_font . font_name :
if butils . is_metrics_object ( obj ) :
butils . remove_faces_from_metrics ( obj )
bpy . app . timers . register ( lambda : remove_faces ( ) , first_interval = 2 )
2024-08-08 11:32:23 +02:00
self . report ( { ' INFO ' } , f " did it " )
2024-05-28 14:11:32 +02:00
return { ' FINISHED ' }
2024-08-14 11:26:19 +02:00
# keep = ['io_anim_bvh', 'io_curve_svg', 'io_mesh_stl', 'io_mesh_uv_layout', 'io_scene_fbx', 'io_scene_gltf2', 'io_scene_x3d', 'cycles', 'pose_library', 'abc3d']
2024-06-27 14:56:43 +02:00
# for addon in keep:
# bpy.ops.preferences.addon_enable(module=addon)
2024-05-28 14:11:32 +02:00
2024-08-14 11:26:19 +02:00
class ABC3D_OT_CreateFontFromObjects ( bpy . types . Operator ) :
2024-08-07 11:56:13 +02:00
""" Create Font from selected objects """
2024-08-04 12:52:37 +02:00
bl_idname = f " { __name__ } .create_font_from_objects "
2024-05-21 18:00:49 +02:00
bl_label = " Create Font "
bl_options = { ' REGISTER ' , ' UNDO ' }
2024-08-12 11:22:44 +02:00
font_name : bpy . props . StringProperty (
default = " NM_Origin " ,
)
face_name : bpy . props . StringProperty (
default = " Tender " ,
)
import_infix : bpy . props . StringProperty (
default = " _NM_Origin_Tender " ,
)
2024-08-14 10:50:57 +02:00
autodetect_names : bpy . props . BoolProperty (
default = True ,
)
2024-08-12 11:22:44 +02:00
fix_common_misspellings : bpy . props . BoolProperty (
default = True ,
)
def invoke ( self , context , event ) :
wm = context . window_manager
return wm . invoke_props_dialog ( self )
2024-08-26 18:48:43 +02:00
def draw ( self , context ) :
2024-08-12 11:22:44 +02:00
layout = self . layout
2024-08-26 18:48:43 +02:00
if len ( context . selected_objects ) == 0 :
layout . row ( ) . label ( text = " No objects selected. " , icon = " ERROR " )
layout . row ( ) . label ( text = " Please select your glyphs first. " , icon = " INFO " )
else :
row = layout . row ( )
row . prop ( self , ' autodetect_names ' )
if self . autodetect_names :
scale_y = 0.5
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 = layout . row ( ) ; row . scale_y = scale_y
row . label ( text = " - glyph id: unicode glyph name or raw glyph " )
row = layout . row ( ) ; row . scale_y = scale_y
row . label ( text = " - font name: font name with underscore " )
row = layout . row ( ) ; row . scale_y = scale_y
row . label ( text = " - face name: face name " )
row = layout . row ( ) ; row . scale_y = scale_y
row . label ( text = " working examples: " )
row = layout . row ( ) ; row . scale_y = scale_y
row . label ( text = " - ' A_NM_Origin_Tender ' " )
row = layout . row ( ) ; row . scale_y = scale_y
row . label ( text = " - ' B_NM_Origin_Tender ' " )
row = layout . row ( ) ; row . scale_y = scale_y
row . label ( text = " - ' arrowright_NM_Origin_Tender ' " )
row = layout . row ( ) ; row . scale_y = scale_y
row . label ( text = " - ' →_NM_Origin_Tender ' (equal to above) " )
row = layout . row ( ) ; row . scale_y = scale_y
row . label ( text = " - ' quotesingle_NM_Origin_Tender.001 ' " )
row = layout . row ( ) ; row . scale_y = scale_y
row . label ( text = " - ' colon_NM_Origin_Tender_2 ' " )
box = layout . box ( )
box . enabled = not self . autodetect_names
box . prop ( self , ' font_name ' )
box . prop ( self , ' face_name ' )
box . prop ( self , ' import_infix ' )
layout . prop ( self , ' fix_common_misspellings ' )
if self . fix_common_misspellings :
for k in Font . known_misspellings :
character = " "
if Font . known_misspellings [ k ] in Font . name_to_glyph_d :
character = f " ( { Font . name_to_glyph_d [ Font . known_misspellings [ k ] ] } ) "
row = layout . row ( ) ; row . scale_y = 0.5
row . label ( text = f " { k } → { Font . known_misspellings [ k ] } { character } " )
2024-08-12 11:22:44 +02:00
2024-05-21 18:00:49 +02:00
def execute ( self , context ) :
2024-08-14 10:50:57 +02:00
print ( f " executing { self . bl_idname } " )
2024-08-26 18:48:43 +02:00
if len ( context . selected_objects ) == 0 :
print ( f " cancelled { self . bl_idname } - no objects selected " )
return { ' CANCELLED ' }
2024-05-21 18:00:49 +02:00
global shared
scene = bpy . context . scene
2024-08-14 11:26:19 +02:00
abc3d_data = scene . abc3d_data
2024-05-21 18:00:49 +02:00
2024-08-14 11:26:19 +02:00
fontcollection = bpy . data . collections . get ( " ABC3D " )
2024-05-21 18:00:49 +02:00
if fontcollection is None :
2024-08-14 11:26:19 +02:00
fontcollection = bpy . data . collections . new ( " ABC3D " )
2024-05-21 18:00:49 +02:00
2024-08-23 16:32:40 +02:00
ifxsplit = self . import_infix . split ( ' _ ' )
2024-08-12 11:22:44 +02:00
# if len(ifxsplit) != 4:
# font_name = f"{ifxsplit[1]}_{ifxsplit[2]}"
# face_name = ifxsplit[3]
font_name = self . font_name
face_name = self . face_name
2024-05-21 18:00:49 +02:00
2024-08-07 11:56:13 +02:00
# TODO: do not clear
2024-08-14 11:26:19 +02:00
# abc3d_data.available_fonts.clear()
2024-08-12 11:22:44 +02:00
# Font.fonts = {}
2024-05-21 18:00:49 +02:00
currentObjects = [ ]
2024-08-07 11:56:13 +02:00
for o in context . selected_objects :
2024-05-21 18:00:49 +02:00
if o . name not in currentObjects :
2024-08-14 10:50:57 +02:00
print ( f " processing { o . name } " )
process_object = True
if self . autodetect_names :
ifxsplit = o . name . split ( ' _ ' )
if len ( ifxsplit ) < 4 :
print ( f " whoops name could not be autodetected { o . name } " )
continue
font_name = f " { ifxsplit [ 1 ] } _ { ifxsplit [ 2 ] } "
face_name = ifxsplit [ 3 ]
if butils . is_mesh ( o ) and not butils . is_metrics_object ( o ) :
2024-05-21 18:00:49 +02:00
uc = o . users_collection
2024-08-12 11:22:44 +02:00
if self . fix_common_misspellings :
o . name = Font . fix_glyph_name_misspellings ( o . name )
2024-08-14 10:50:57 +02:00
# name = re.sub(regex, "", o.name)
# glyph_id = Font.name_to_glyph(name)
name = o . name . split ( ' _ ' ) [ 0 ]
2024-08-12 11:22:44 +02:00
glyph_id = Font . name_to_glyph ( name )
if type ( glyph_id ) != type ( None ) :
2024-06-27 14:56:43 +02:00
o [ " glyph " ] = glyph_id
o [ " font_name " ] = font_name
o [ " face_name " ] = face_name
# butils.apply_all_transforms(o)
2024-05-21 18:00:49 +02:00
butils . move_in_fontcollection (
o ,
2024-06-27 14:56:43 +02:00
fontcollection )
2024-05-21 18:00:49 +02:00
Font . add_glyph (
font_name ,
face_name ,
glyph_id ,
o )
#TODO: is there a better way to iterate over a CollectionProperty?
found = False
2024-08-14 11:26:19 +02:00
for f in abc3d_data . available_fonts . values ( ) :
2024-08-12 11:22:44 +02:00
if ( f . font_name == font_name
and f . face_name == face_name ) :
2024-05-21 18:00:49 +02:00
found = True
break
if not found :
2024-08-14 11:26:19 +02:00
f = abc3d_data . available_fonts . add ( )
2024-05-21 18:00:49 +02:00
f . font_name = font_name
2024-08-12 11:22:44 +02:00
f . face_name = face_name
2024-05-21 18:00:49 +02:00
else :
2024-08-12 11:22:44 +02:00
print ( f " import warning: did not understand glyph { name } " )
2024-05-21 18:00:49 +02:00
self . report ( { ' INFO ' } , f " did not understand glyph { name } " )
2024-05-08 16:19:47 +02:00
return { ' FINISHED ' }
2024-08-14 11:26:19 +02:00
class ABC3D_PT_RightPropertiesPanel ( bpy . types . Panel ) :
2024-07-01 14:39:07 +02:00
""" Creates a Panel in the Object properties window """
2024-08-04 12:52:37 +02:00
bl_label = f " { bl_info [ ' name ' ] } "
2024-08-14 11:26:19 +02:00
bl_idname = " ABC3D_PT_RightPropertiesPanel "
2024-07-01 14:39:07 +02:00
bl_space_type = ' PROPERTIES '
bl_region_type = ' WINDOW '
bl_context = " object "
2024-07-10 16:34:43 +02:00
@classmethod
def poll ( self , context ) :
# only show the panel, if it's a textobject or a glyph
2024-08-14 11:26:19 +02:00
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_glyph = type ( next ( ( t for t in context . scene . abc3d_data . available_texts if t . text_object == context . active_object . parent ) , None ) ) != type ( None )
2024-07-10 16:34:43 +02:00
return is_text or is_glyph
2024-07-01 14:39:07 +02:00
def draw ( self , context ) :
layout = self . layout
scene = context . scene
2024-08-14 11:26:19 +02:00
abc3d_data = scene . abc3d_data
2024-07-01 14:39:07 +02:00
obj = context . active_object
2024-08-05 12:59:07 +02:00
def is_it_text ( ) :
2024-08-14 11:26:19 +02:00
return type ( next ( ( t for t in context . scene . abc3d_data . available_texts if t . text_object == context . active_object ) , None ) ) != type ( None )
2024-08-05 12:59:07 +02:00
def is_it_glyph ( ) :
2024-08-14 11:26:19 +02:00
return type ( next ( ( t for t in context . scene . abc3d_data . available_texts if t . text_object == context . active_object . parent ) , None ) ) != type ( None )
2024-07-10 16:34:43 +02:00
2024-08-05 12:59:07 +02:00
is_text = is_it_text ( )
is_glyph = is_it_glyph ( )
2024-07-10 16:34:43 +02:00
2024-08-05 12:59:07 +02:00
textobject = obj if is_text else obj . parent if is_glyph else obj
2024-08-14 11:26:19 +02:00
available_text = abc3d_data . available_texts [ abc3d_data . active_text_index ]
2024-07-01 14:39:07 +02:00
2024-08-05 12:59:07 +02:00
# row = layout.row()
# row.label(text="Hello world!", icon='WORLD_DATA')
# row = layout.row()
# row.label(text="Active object is: " + obj.name)
# row = layout.row()
# row.label(text="text object is: " + textobject.name)
2024-07-10 16:34:43 +02:00
row = layout . row ( )
2024-08-14 11:26:19 +02:00
row . label ( text = f " active text index is: { abc3d_data . active_text_index } " )
2024-08-05 12:59:07 +02:00
layout . row ( ) . label ( text = " Text Properties: " )
2024-08-04 12:52:37 +02:00
layout . row ( ) . prop ( available_text , " text " )
layout . row ( ) . prop ( available_text , " letter_spacing " )
2024-08-05 12:59:07 +02:00
layout . row ( ) . prop ( available_text , " font_size " )
2024-08-21 17:31:19 +02:00
layout . row ( ) . prop ( available_text , " offset " )
2024-08-08 11:30:05 +02:00
layout . row ( ) . prop ( available_text , " compensate_curvature " )
layout . row ( ) . prop ( available_text , " ignore_orientation " )
2024-08-05 12:59:07 +02:00
layout . column ( ) . prop ( available_text , " translation " )
2024-08-04 12:52:37 +02:00
layout . column ( ) . prop ( available_text , " orientation " )
2024-07-01 14:39:07 +02:00
2024-08-05 12:59:07 +02:00
if is_glyph :
layout . row ( ) . label ( text = " Glyph Properties: " )
2024-08-14 11:26:19 +02:00
class ABC3D_OT_Reporter ( bpy . types . Operator ) :
2024-08-14 10:50:57 +02:00
bl_idname = f " { __name__ } .reporter "
bl_label = " Report "
label = bpy . props . StringProperty (
name = " label " ,
default = " INFO " ,
)
message = bpy . props . StringProperty (
name = " message " ,
default = " I have nothing to say really " ,
)
def execute ( self , context ) :
#this is where I send the message
self . report ( { ' INFO ' } , ' whatever ' )
for i in range ( 0 , 10 ) :
butils . ShowMessageBox ( ' whatever ' , ' INFO ' , ' INFO ' )
return { ' FINISHED ' }
2024-07-01 14:39:07 +02:00
2024-05-08 16:19:47 +02:00
classes = (
2024-08-21 14:42:53 +02:00
bimport . ImportGLTF2 ,
bimport . GetFontFacesInFile ,
2024-08-14 11:26:19 +02:00
ABC3D_addonPreferences ,
ABC3D_available_font ,
ABC3D_glyph_properties ,
ABC3D_text_properties ,
ABC3D_data ,
ABC3D_UL_fonts ,
ABC3D_UL_texts ,
ABC3D_PT_Panel ,
2024-08-26 18:48:43 +02:00
ABC3D_PT_LoadFontPanel ,
2024-08-14 11:26:19 +02:00
ABC3D_PT_FontList ,
ABC3D_PT_TextPlacement ,
ABC3D_PT_TextManagement ,
ABC3D_PT_FontCreation ,
ABC3D_PT_TextPropertiesPanel ,
2024-10-31 19:33:22 +01:00
ABC3D_OT_OpenAssetDirectory ,
2024-08-14 13:39:47 +02:00
ABC3D_OT_LoadInstalledFonts ,
2024-08-28 17:25:06 +02:00
ABC3D_OT_LoadFont ,
2024-08-14 11:26:19 +02:00
ABC3D_OT_AddDefaultMetrics ,
ABC3D_OT_RemoveMetrics ,
ABC3D_OT_AlignMetricsToActiveObject ,
ABC3D_OT_AlignMetrics ,
ABC3D_OT_TemporaryHelper ,
2024-08-21 16:51:00 +02:00
ABC3D_OT_RemoveText ,
2024-08-14 11:26:19 +02:00
ABC3D_OT_PlaceText ,
2024-08-21 16:06:00 +02:00
ABC3D_OT_InstallFont ,
2024-08-14 11:26:19 +02:00
ABC3D_OT_ToggleABC3DCollection ,
ABC3D_OT_SaveFontToFile ,
ABC3D_OT_CreateFontFromObjects ,
ABC3D_PT_RightPropertiesPanel ,
ABC3D_OT_Reporter ,
2024-05-08 16:19:47 +02:00
)
2024-08-26 18:48:43 +02:00
def compare_text_object_with_object ( t , o , strict = False ) :
for k in o . keys ( ) :
if k == f " { utils . prefix ( ) } _type " :
if o [ k ] != " textobject " :
return False
elif k . startswith ( f " { utils . prefix ( ) } _ " ) :
p = k . replace ( f " { utils . prefix ( ) } _ " , " " )
if p in t . keys ( ) :
if t [ p ] != o [ k ] :
return False
else :
print ( f " { __name__ } set_text_object: did not find key ( { p } ) " )
if strict :
return False
# for p in t.keys():
# if
return True
def detect_text ( ) :
scene = bpy . context . scene
abc3d_data = scene . abc3d_data
for o in scene . objects :
if o [ f " { utils . prefix ( ) } _type " ] == " textobject " :
linked_textobject = int ( o [ f " { utils . prefix ( ) } _linked_textobject " ] )
if len ( abc3d_data . available_texts ) > linked_textobject \
and abc3d_data . available_texts [ linked_textobject ] . text_object == o :
t = abc3d_data . available_texts [ linked_textobject ]
a = test_availability ( o [ " font_name " ] , o [ " face_name " ] , o [ " text " ] )
butils . transfer_blender_object_to_text_properties ( o , t )
2024-08-28 17:45:27 +02:00
def load_used_glyphs ( ) :
print ( " LOAD USED GLYPHS " )
scene = bpy . context . scene
abc3d_data = scene . abc3d_data
for t in abc3d_data . available_texts :
a = Font . test_availability ( t . font_name ,
t . face_name ,
t . text )
2024-08-28 18:07:17 +02:00
if type ( a ) == type ( int ( ) ) :
if a == Font . MISSING_FONT :
butils . ShowMessageBox ( " Missing Font " ,
" ERROR " ,
[ f " Font { t . font_name } is missing. " ,
" Do you have it installed? " ] )
if a is Font . MISSING_FACE :
butils . ShowMessageBox ( " Missing FontFace " ,
" ERROR " ,
[ f " Font { t . font_name } is there, " ,
f " but the FontFace { t . face_name } is missing, " ,
" Do you have it installed? " ] )
2024-08-28 17:45:27 +02:00
if len ( a [ " maybe " ] ) > 0 :
for fp in a [ " filepaths " ] :
butils . load_font_from_filepath ( fp , a [ " maybe " ] )
2024-08-26 18:48:43 +02:00
2024-07-10 16:34:43 +02:00
@persistent
def load_handler ( self , dummy ) :
2024-07-10 17:14:02 +02:00
if not bpy . app . timers . is_registered ( butils . execute_queued_functions ) :
bpy . app . timers . register ( butils . execute_queued_functions )
2024-08-14 10:50:57 +02:00
butils . run_in_main_thread ( butils . update_available_fonts )
2024-08-21 14:42:53 +02:00
butils . run_in_main_thread ( bpy . ops . abc3d . load_installed_fonts )
2024-08-28 17:45:27 +02:00
butils . run_in_main_thread ( load_used_glyphs )
2024-07-10 16:34:43 +02:00
def load_handler_unload ( ) :
2024-07-10 17:14:02 +02:00
if bpy . app . timers . is_registered ( butils . execute_queued_functions ) :
bpy . app . timers . unregister ( butils . execute_queued_functions )
2024-07-10 16:34:43 +02:00
2024-08-04 12:52:37 +02:00
@persistent
def on_frame_changed ( self , dummy ) :
2024-08-14 11:26:19 +02:00
for t in bpy . context . scene . abc3d_data . available_texts :
2024-08-04 12:52:37 +02:00
# TODO PERFORMANCE: only on demand
butils . set_text_on_curve ( t )
2024-10-31 19:34:31 +01:00
@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 " ]
2024-05-08 16:19:47 +02:00
def register ( ) :
2024-11-05 15:45:50 +01:00
addon_updater_ops . register ( bl_info )
2024-05-08 16:19:47 +02:00
for cls in classes :
2024-11-05 15:45:50 +01:00
addon_updater_ops . make_annotations ( cls ) # Avoid blender 2.8 warnings.
2024-05-08 16:19:47 +02:00
bpy . utils . register_class ( cls )
2024-08-14 11:26:19 +02:00
bpy . types . Scene . abc3d_data = bpy . props . PointerProperty ( type = ABC3D_data )
2024-07-10 16:34:43 +02:00
# bpy.types.Object.__del__ = lambda self: print(f"Bye {self.name}")
2024-05-21 18:00:49 +02:00
print ( f " REGISTER { bl_info [ ' name ' ] } " )
2024-07-10 17:14:02 +02:00
# auto start if we load a blend file
2024-07-10 16:34:43 +02:00
if load_handler not in bpy . app . handlers . load_post :
bpy . app . handlers . load_post . append ( load_handler )
2024-07-10 17:14:02 +02:00
# and autostart if we reload script
load_handler ( None , None )
2024-05-08 16:19:47 +02:00
2024-08-04 12:52:37 +02:00
if on_frame_changed not in bpy . app . handlers . frame_change_post :
bpy . app . handlers . frame_change_post . append ( on_frame_changed )
2024-06-27 14:56:43 +02:00
2024-10-31 19:34:31 +01:00
if on_depsgraph_update not in bpy . app . handlers . depsgraph_update_post :
bpy . app . handlers . depsgraph_update_post . append ( on_depsgraph_update )
2024-08-04 12:52:37 +02:00
butils . run_in_main_thread ( butils . clear_available_fonts )
2024-08-14 13:39:47 +02:00
# butils.run_in_main_thread(butils.load_installed_fonts)
2024-08-14 10:50:57 +02:00
butils . run_in_main_thread ( butils . update_available_fonts )
2024-10-31 19:33:49 +01:00
butils . run_in_main_thread ( butils . update_types )
2024-06-27 14:56:43 +02:00
2024-08-14 13:39:47 +02:00
# bpy.ops.abc3d.load_installed_fonts()
2024-08-12 11:22:44 +02:00
Font . name_to_glyph_d = Font . generate_name_to_glyph_d ( )
2024-05-08 16:19:47 +02:00
def unregister ( ) :
2024-11-05 15:45:50 +01:00
addon_updater_ops . unregister ( )
for cls in reversed ( classes ) :
2024-05-08 16:19:47 +02:00
bpy . utils . unregister_class ( cls )
2024-07-10 16:34:43 +02:00
2024-07-10 17:14:02 +02:00
# remove autostart when loading blend file
2024-07-10 16:34:43 +02:00
if load_handler in bpy . app . handlers . load_post :
bpy . app . handlers . load_post . remove ( load_handler )
2024-07-10 17:14:02 +02:00
# and when reload script
2024-07-10 16:34:43 +02:00
load_handler_unload ( )
2024-05-08 16:19:47 +02:00
2024-08-04 12:52:37 +02:00
if on_frame_changed in bpy . app . handlers . frame_change_post :
bpy . app . handlers . frame_change_post . remove ( on_frame_changed )
2024-08-14 11:26:19 +02:00
del bpy . types . Scene . abc3d_data
2024-05-21 18:00:49 +02:00
print ( f " UNREGISTER { bl_info [ ' name ' ] } " )
2024-05-08 16:19:47 +02:00
if __name__ == ' __main__ ' :
register ( )