Compare commits
49 commits
Author | SHA1 | Date | |
---|---|---|---|
a5602a6095 | |||
cd99362bb1 | |||
f02f8fc2f0 | |||
e95266afc9 | |||
58e0df3427 | |||
7de8fcc5d1 | |||
14d1b7a160 | |||
59edb2e786 | |||
bb0a5a4a2c | |||
6160b99c93 | |||
d61607c75d | |||
8470425d20 | |||
01fcb60e31 | |||
7a43cfaf2f | |||
2dcd4e7a2c | |||
9423659153 | |||
19f4bf586f | |||
04229fbc31 | |||
963d89daf9 | |||
3ef2ae934d | |||
8965ab11eb | |||
513497d492 | |||
335ab1face | |||
777644e509 | |||
e14251523b | |||
8f3d58aad0 | |||
2ace31a246 | |||
88cfaf3be7 | |||
10e57dd46a | |||
c27cf41368 | |||
7a034efd1c | |||
3ea2f0e304 | |||
840fdf1ca4 | |||
7b4e65cbb7 | |||
4113343e79 | |||
e21ecaef0a | |||
9c77139dcd | |||
13b5a4dd88 | |||
49699db309 | |||
7ebe913e49 | |||
2422d0cf09 | |||
d6dfbfa5a1 | |||
5bd78a3fc1 | |||
1e54a10341 | |||
23624ea1eb | |||
42c4a33801 | |||
35d864b9b8 | |||
a2c4ba60f2 | |||
7e2eeeeec1 |
11 changed files with 2130 additions and 811 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,6 +1,7 @@
|
|||
# python
|
||||
__pycache__
|
||||
venv
|
||||
venv*
|
||||
|
||||
# vim
|
||||
*.swo
|
||||
|
|
|
@ -5,10 +5,8 @@
|
|||
/ ___ \| |_) | |___ ___) | |_| |
|
||||
/_/ \_\____/ \____|____/|____/
|
||||
```
|
||||
v0.0.6
|
||||
v0.0.12
|
||||
|
||||
Convenience tool to work with 3D typography in Blender and Cinema4D.
|
||||
|
||||
Install as you would normally install an addon.
|
||||
Convenience addon to work with 3D typography in Blender and Cinema4D.
|
||||
|
||||
Instructions for development in [CONTRIBUTING,md](./CONTRIBUTING.md).
|
||||
|
|
925
__init__.py
925
__init__.py
File diff suppressed because it is too large
Load diff
32
bimport.py
32
bimport.py
|
@ -4,11 +4,10 @@ from bpy.props import (
|
|||
BoolProperty,
|
||||
EnumProperty,
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
from bpy.types import Operator
|
||||
from bpy_extras.io_utils import ImportHelper, ExportHelper
|
||||
from bpy_extras.io_utils import ImportHelper
|
||||
from io_scene_gltf2 import ConvertGLTF2_Base
|
||||
import importlib
|
||||
|
||||
|
@ -16,20 +15,31 @@ import importlib
|
|||
if "Font" in locals():
|
||||
importlib.reload(Font)
|
||||
else:
|
||||
from .common import Font
|
||||
pass
|
||||
|
||||
if "utils" in locals():
|
||||
importlib.reload(utils)
|
||||
else:
|
||||
from .common import utils
|
||||
|
||||
try:
|
||||
from io_scene_gltf2.io.imp.gltf2_io_gltf import glTFImporter, ImportError
|
||||
from io_scene_gltf2.blender.imp.gltf2_blender_gltf import BlenderGlTF
|
||||
from io_scene_gltf2.blender.imp.gltf2_blender_vnode import VNode, compute_vnodes
|
||||
from io_scene_gltf2.blender.com.gltf2_blender_extras import set_extras
|
||||
from io_scene_gltf2.blender.imp.gltf2_blender_node import BlenderNode
|
||||
except (ModuleNotFoundError, ImportError):
|
||||
from io_scene_gltf2.io.imp.gltf2_io_gltf import glTFImporter, ImportError
|
||||
from io_scene_gltf2.blender.imp.blender_gltf import BlenderGlTF
|
||||
from io_scene_gltf2.blender.imp.vnode import VNode, compute_vnodes
|
||||
from io_scene_gltf2.blender.com.extras import set_extras
|
||||
from io_scene_gltf2.blender.imp.node import BlenderNode
|
||||
|
||||
|
||||
# taken from blender_git/blender/scripts/addons/io_scene_gltf2/__init__.py
|
||||
|
||||
|
||||
def get_font_faces_in_file(filepath):
|
||||
from io_scene_gltf2.io.imp.gltf2_io_gltf import glTFImporter, ImportError
|
||||
|
||||
try:
|
||||
import_settings = {"import_user_extensions": []}
|
||||
gltf_importer = glTFImporter(filepath, import_settings)
|
||||
|
@ -50,7 +60,7 @@ def get_font_faces_in_file(filepath):
|
|||
out.append(node.extras)
|
||||
return out
|
||||
|
||||
except ImportError as e:
|
||||
except ImportError:
|
||||
return None
|
||||
|
||||
|
||||
|
@ -60,7 +70,7 @@ def get_font_faces_in_file(filepath):
|
|||
class GetFontFacesInFile(Operator, ImportHelper):
|
||||
"""Load a glTF 2.0 font and check which faces are in there"""
|
||||
|
||||
bl_idname = f"abc3d.check_font_gltf"
|
||||
bl_idname = "abc3d.check_font_gltf"
|
||||
bl_label = "Check glTF 2.0 Font"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
|
@ -77,7 +87,6 @@ class GetFontFacesInFile(Operator, ImportHelper):
|
|||
|
||||
def check_gltf2(self, context):
|
||||
import os
|
||||
import sys
|
||||
|
||||
if self.files:
|
||||
# Multiple file check
|
||||
|
@ -100,7 +109,7 @@ class GetFontFacesInFile(Operator, ImportHelper):
|
|||
class ImportGLTF2(Operator, ConvertGLTF2_Base, ImportHelper):
|
||||
"""Load a glTF 2.0 font"""
|
||||
|
||||
bl_idname = f"abc3d.import_font_gltf"
|
||||
bl_idname = "abc3d.import_font_gltf"
|
||||
bl_label = "Import glTF 2.0 Font"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
|
@ -285,11 +294,6 @@ class ImportGLTF2(Operator, ConvertGLTF2_Base, ImportHelper):
|
|||
|
||||
def unit_import(self, filename, import_settings):
|
||||
import time
|
||||
from io_scene_gltf2.io.imp.gltf2_io_gltf import glTFImporter, ImportError
|
||||
from io_scene_gltf2.blender.imp.gltf2_blender_gltf import BlenderGlTF
|
||||
from io_scene_gltf2.blender.imp.gltf2_blender_vnode import VNode, compute_vnodes
|
||||
from io_scene_gltf2.blender.com.gltf2_blender_extras import set_extras
|
||||
from io_scene_gltf2.blender.imp.gltf2_blender_node import BlenderNode
|
||||
|
||||
try:
|
||||
gltf = glTFImporter(filename, import_settings)
|
||||
|
|
155
common/Font.py
155
common/Font.py
|
@ -1,5 +1,5 @@
|
|||
from typing import Dict
|
||||
from pathlib import Path
|
||||
from typing import Dict, NamedTuple
|
||||
|
||||
# convenience dictionary for translating names to glyph ids
|
||||
# note: overwritten/extended by the content of "glypNamesToUnicode.txt"
|
||||
|
@ -163,7 +163,7 @@ class Font:
|
|||
def register_font(font_name, face_name, glyphs_in_fontfile, filepath):
|
||||
if not fonts.keys().__contains__(font_name):
|
||||
fonts[font_name] = Font({})
|
||||
if fonts[font_name].faces.get(face_name) == None:
|
||||
if fonts[font_name].faces.get(face_name) is None:
|
||||
fonts[font_name].faces[face_name] = FontFace({})
|
||||
fonts[font_name].faces[face_name].glyphs_in_fontfile = glyphs_in_fontfile
|
||||
else:
|
||||
|
@ -177,6 +177,34 @@ def register_font(font_name, face_name, glyphs_in_fontfile, filepath):
|
|||
fonts[font_name].faces[face_name].filepaths.append(filepath)
|
||||
|
||||
|
||||
def get_font(font_name):
|
||||
if not fonts.keys().__contains__(font_name):
|
||||
print(f"ABC3D::get_font: font name({font_name}) not found")
|
||||
print(fonts.keys())
|
||||
return None
|
||||
return fonts[font_name]
|
||||
|
||||
|
||||
def get_font_face(font_name, face_name):
|
||||
font = get_font(font_name)
|
||||
if font is None:
|
||||
return None
|
||||
if not font.faces.keys().__contains__(face_name):
|
||||
print(
|
||||
f"ABC3D::get_font_face (font: {font_name}): face name({face_name}) not found"
|
||||
)
|
||||
print(font.faces.keys())
|
||||
return None
|
||||
return font.faces[face_name]
|
||||
|
||||
|
||||
def get_font_face_filepaths(font_name, face_name):
|
||||
face = get_font_face(font_name, face_name)
|
||||
if not face:
|
||||
return None
|
||||
return face.filepaths
|
||||
|
||||
|
||||
def add_glyph(font_name, face_name, glyph_id, glyph_object):
|
||||
"""add_glyph adds a glyph to a FontFace
|
||||
it creates the :class:`Font` and :class:`FontFace` if it does not exist yet
|
||||
|
@ -193,9 +221,9 @@ def add_glyph(font_name, face_name, glyph_id, glyph_object):
|
|||
|
||||
if not fonts.keys().__contains__(font_name):
|
||||
fonts[font_name] = Font({})
|
||||
if fonts[font_name].faces.get(face_name) == None:
|
||||
if fonts[font_name].faces.get(face_name) is None:
|
||||
fonts[font_name].faces[face_name] = FontFace({})
|
||||
if fonts[font_name].faces[face_name].glyphs.get(glyph_id) == None:
|
||||
if fonts[font_name].faces[face_name].glyphs.get(glyph_id) is None:
|
||||
fonts[font_name].faces[face_name].glyphs[glyph_id] = []
|
||||
fonts[font_name].faces[face_name].glyphs.get(glyph_id).append(glyph_object)
|
||||
|
||||
|
@ -203,7 +231,48 @@ def add_glyph(font_name, face_name, glyph_id, glyph_object):
|
|||
fonts[font_name].faces[face_name].loaded_glyphs.append(glyph_id)
|
||||
|
||||
|
||||
def get_glyph(font_name, face_name, glyph_id, alternate=0):
|
||||
def get_glyphs(font_name, face_name, glyph_id):
|
||||
"""get_glyphs returns an array of glyphs of a FontFace
|
||||
|
||||
:param font_name: The :class:`Font` you want to get the glyph from
|
||||
:type font_name: str
|
||||
:param face_name: The :class:`FontFace` you want to get the glyph from
|
||||
:type face_name: str
|
||||
:param glyph_id: The ``glyph_id`` from the glyph you want
|
||||
:type glyph_id: str
|
||||
...
|
||||
:return: returns a list of the glyph objects, or an empty list if none exists
|
||||
:rtype: `List`
|
||||
"""
|
||||
|
||||
face = get_font_face(font_name, face_name)
|
||||
if face is None:
|
||||
print(f"ABC3D::get_glyph: font({font_name}) face({face_name}) not found")
|
||||
try:
|
||||
print(fonts[font_name].faces.keys())
|
||||
except:
|
||||
print(fonts.keys())
|
||||
return []
|
||||
|
||||
glyphs_for_id = face.glyphs.get(glyph_id)
|
||||
if glyphs_for_id is None:
|
||||
print(
|
||||
f"ABC3D::get_glyph: font({font_name}) face({face_name}) glyph({glyph_id}) not found"
|
||||
)
|
||||
if glyph_id not in fonts[font_name].faces[face_name].missing_glyphs:
|
||||
fonts[font_name].faces[face_name].missing_glyphs.append(glyph_id)
|
||||
return []
|
||||
|
||||
return glyphs_for_id
|
||||
|
||||
|
||||
def get_glyph(
|
||||
font_name: str,
|
||||
face_name: str,
|
||||
glyph_id: str,
|
||||
alternate: int = 0,
|
||||
alternate_tolerant: bool = True,
|
||||
):
|
||||
"""add_glyph adds a glyph to a FontFace
|
||||
it creates the :class:`Font` and :class:`FontFace` if it does not exist yet
|
||||
|
||||
|
@ -213,56 +282,79 @@ def get_glyph(font_name, face_name, glyph_id, alternate=0):
|
|||
:type face_name: str
|
||||
:param glyph_id: The ``glyph_id`` from the glyph you want
|
||||
:type glyph_id: str
|
||||
:param alternate: The ``alternate`` from the glyph you want
|
||||
:type alternate: int
|
||||
:param alternate_tolerant: Fetch an existing alternate if requested is out of bounds
|
||||
:type glyph_id: bool
|
||||
...
|
||||
:return: returns the glyph object, or ``None`` if it does not exist
|
||||
:rtype: `Object`
|
||||
"""
|
||||
|
||||
if not fonts.keys().__contains__(font_name):
|
||||
# print(f"ABC3D::get_glyph: font name({font_name}) not found")
|
||||
# print(fonts.keys())
|
||||
glyphs = get_glyphs(font_name, face_name, glyph_id)
|
||||
|
||||
if len(glyphs) == 0:
|
||||
print(
|
||||
f"ABC3D::get_glyph: font({font_name}) face({face_name}) glyph({glyph_id})[{alternate}] not found"
|
||||
)
|
||||
return None
|
||||
|
||||
face = fonts[font_name].faces.get(face_name)
|
||||
if face == None:
|
||||
# print(f"ABC3D::get_glyph: font({font_name}) face({face_name}) not found")
|
||||
# print(fonts[font_name].faces.keys())
|
||||
return None
|
||||
if len(glyphs) <= alternate:
|
||||
if alternate_tolerant:
|
||||
alternate = 0
|
||||
else:
|
||||
print(
|
||||
f"ABC3D::get_glyph: font({font_name}) face({face_name}) glyph({glyph_id})[{alternate}] not found"
|
||||
)
|
||||
return None
|
||||
|
||||
glyphs_for_id = face.glyphs.get(glyph_id)
|
||||
if glyphs_for_id == None or len(glyphs_for_id) <= alternate:
|
||||
# print(f"ABC3D::get_glyph: font({font_name}) face({face_name}) glyph({glyph_id})[{alternate}] not found")
|
||||
if glyph_id not in fonts[font_name].faces[face_name].missing_glyphs:
|
||||
fonts[font_name].faces[face_name].missing_glyphs.append(glyph_id)
|
||||
return None
|
||||
return glyphs[alternate]
|
||||
|
||||
return fonts[font_name].faces[face_name].glyphs.get(glyph_id)[alternate]
|
||||
|
||||
def unloaded_glyph(font_name, face_name, glyph_id):
|
||||
face = get_font_face(font_name, face_name)
|
||||
if face is None:
|
||||
print(f"ABC3D::get_glyph: font({font_name}) face({face_name}) not found")
|
||||
return
|
||||
while True:
|
||||
try:
|
||||
fonts[font_name].faces[face_name].loaded_glyphs.remove(glyph_id)
|
||||
del fonts[font_name].faces[face_name].glyphs[glyph_id]
|
||||
except ValueError:
|
||||
break
|
||||
|
||||
|
||||
class GlyphsAvailability(NamedTuple):
|
||||
loaded: str
|
||||
missing: str
|
||||
unloaded: str
|
||||
filepaths: list[str]
|
||||
|
||||
|
||||
def test_glyphs_availability(font_name, face_name, text):
|
||||
# maybe there is NOTHING yet
|
||||
if (
|
||||
not fonts.keys().__contains__(font_name)
|
||||
or fonts[font_name].faces.get(face_name) == None
|
||||
or fonts[font_name].faces.get(face_name) is None
|
||||
):
|
||||
return "", "", text # <loaded>, <missing>, <maybe>
|
||||
return GlyphsAvailability("", "", "", [])
|
||||
|
||||
loaded = []
|
||||
missing = []
|
||||
maybe = []
|
||||
unloaded = []
|
||||
for c in text:
|
||||
if c in fonts[font_name].faces[face_name].loaded_glyphs:
|
||||
loaded.append(c)
|
||||
elif c in fonts[font_name].faces[face_name].glyphs_in_fontfile:
|
||||
maybe.append(c)
|
||||
unloaded.append(c)
|
||||
else:
|
||||
if c not in fonts[font_name].faces[face_name].missing_glyphs:
|
||||
fonts[font_name].faces[face_name].missing_glyphs.append(c)
|
||||
missing.append(c)
|
||||
return (
|
||||
return GlyphsAvailability(
|
||||
"".join(loaded),
|
||||
"".join(missing),
|
||||
"".join(maybe),
|
||||
"".join(unloaded),
|
||||
fonts[font_name].faces[face_name].filepaths,
|
||||
)
|
||||
|
||||
|
@ -286,17 +378,12 @@ MISSING_FACE = 1
|
|||
def test_availability(font_name, face_name, text):
|
||||
if not fonts.keys().__contains__(font_name):
|
||||
return MISSING_FONT
|
||||
if fonts[font_name].faces.get(face_name) == None:
|
||||
if fonts[font_name].faces.get(face_name) is None:
|
||||
return MISSING_FACE
|
||||
loaded, missing, maybe, filepaths = test_glyphs_availability(
|
||||
availability: GlyphsAvailability = test_glyphs_availability(
|
||||
font_name, face_name, text
|
||||
)
|
||||
return {
|
||||
"loaded": loaded,
|
||||
"missing": missing,
|
||||
"maybe": maybe,
|
||||
"filepaths": filepaths,
|
||||
}
|
||||
return availability
|
||||
|
||||
|
||||
# holds all fonts
|
||||
|
|
|
@ -4,20 +4,20 @@ space 0020 0.25
|
|||
nbspace 00A0 0.25
|
||||
# ethi:wordspace 1361 # NOTE: has shape
|
||||
enquad 2000 0.5
|
||||
emquad 2001 1
|
||||
emquad 2001 1.0
|
||||
enspace 2002 0.5
|
||||
emspace 2003 1
|
||||
threeperemspace 2004 3
|
||||
fourperemspace 2005 4
|
||||
sixperemspace 2006 6
|
||||
figurespace 2007 1
|
||||
punctuationspace 2008 1
|
||||
emspace 2003 1.0
|
||||
threeperemspace 2004 3.0
|
||||
fourperemspace 2005 4.0
|
||||
sixperemspace 2006 6.0
|
||||
figurespace 2007 1.0
|
||||
punctuationspace 2008 1.0
|
||||
thinspace 2009 0.1
|
||||
hairspace 200A 0.05
|
||||
zerowidthspace 200B 0
|
||||
zerowidthspace 200B 0.0
|
||||
narrownobreakspace 202F 0.1
|
||||
mediummathematicalspace 205F 1
|
||||
mediummathematicalspace 205F 1.0
|
||||
cntr:space 2420 0.25
|
||||
ideographicspace 3000 1
|
||||
ideographicspace 3000 1.0
|
||||
# ideographichalffillspace 303F # NOTE: has shape
|
||||
zerowidthnobreakspace FEFF 0
|
||||
zerowidthnobreakspace FEFF 0.0
|
||||
|
|
|
@ -8,11 +8,11 @@ def get_version_minor():
|
|||
|
||||
|
||||
def get_version_patch():
|
||||
return 6
|
||||
return 12
|
||||
|
||||
|
||||
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():
|
||||
|
@ -23,7 +23,6 @@ import datetime
|
|||
import time
|
||||
|
||||
|
||||
|
||||
def get_timestamp():
|
||||
return datetime.datetime.fromtimestamp(time.time()).strftime("%Y.%m.%d-%H:%M:%S")
|
||||
|
||||
|
@ -82,6 +81,10 @@ def open_file_browser(directory):
|
|||
# xdg-open *should* be supported by recent Gnome, KDE, Xfce
|
||||
|
||||
|
||||
def LINE():
|
||||
return sys._getframe(1).f_lineno
|
||||
|
||||
|
||||
def printerr(*args, **kwargs):
|
||||
print(*args, file=sys.stderr, **kwargs)
|
||||
|
||||
|
@ -90,6 +93,34 @@ def removeNonAlphabetic(s):
|
|||
return "".join([i for i in s if i.isalpha()])
|
||||
|
||||
|
||||
import os
|
||||
import pathlib
|
||||
|
||||
|
||||
def can_create_path(path_str: str):
|
||||
path = pathlib.Path(path_str).absolute().resolve()
|
||||
|
||||
tries = 0
|
||||
maximum_tries = 1000
|
||||
while True:
|
||||
if path.exists():
|
||||
if os.access(path, os.W_OK):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
elif path == path.parent:
|
||||
# should never be reached, because root exists
|
||||
# but if it doesn't.. well then we can't
|
||||
return False
|
||||
|
||||
path = path.parent
|
||||
tries += 1
|
||||
if tries > maximum_tries:
|
||||
# always, always break out of while loops eventually
|
||||
# IF you don't want to be here forever
|
||||
break
|
||||
|
||||
|
||||
# # Evaluate a bezier curve for the parameter 0<=t<=1 along its length
|
||||
# def evaluateBezierPoint(p1, h1, h2, p2, t):
|
||||
# return ((1 - t)**3) * p1 + (3 * t * (1 - t)**2) * h1 + (3 * (t**2) * (1 - t)) * h2 + (t**3) * p2
|
||||
|
|
|
@ -1,33 +1,39 @@
|
|||
astroid==3.3.5
|
||||
attrs==24.2.0
|
||||
black==24.10.0
|
||||
bpy==4.2.0
|
||||
cattrs==24.1.2
|
||||
certifi==2024.8.30
|
||||
charset-normalizer==3.4.0
|
||||
click==8.1.7
|
||||
Cython==3.0.11
|
||||
dill==0.3.9
|
||||
docstring-to-markdown==0.15
|
||||
flake8==7.1.1
|
||||
asttokens==3.0.0
|
||||
attrs==25.3.0
|
||||
bpy==4.4.0
|
||||
cattrs==24.1.3
|
||||
certifi==2025.4.26
|
||||
charset-normalizer==3.4.2
|
||||
Cython==3.1.1
|
||||
decorator==5.2.1
|
||||
docstring-to-markdown==0.17
|
||||
executing==2.2.0
|
||||
idna==3.10
|
||||
isort==5.13.2
|
||||
jedi==0.19.1
|
||||
jedi-language-server==0.41.4
|
||||
importlib_metadata==8.7.0
|
||||
ipython==9.2.0
|
||||
ipython_pygments_lexers==1.1.1
|
||||
jedi==0.19.2
|
||||
jedi-language-server==0.45.1
|
||||
lsprotocol==2023.0.1
|
||||
mathutils==3.3.0
|
||||
mccabe==0.7.0
|
||||
mypy-extensions==1.0.0
|
||||
numpy==2.1.3
|
||||
packaging==24.1
|
||||
matplotlib-inline==0.1.7
|
||||
numpy==1.26.4
|
||||
parso==0.8.4
|
||||
pathspec==0.12.1
|
||||
platformdirs==4.3.6
|
||||
pycodestyle==2.12.1
|
||||
pyflakes==3.2.0
|
||||
pexpect==4.9.0
|
||||
pluggy==1.6.0
|
||||
prompt_toolkit==3.0.51
|
||||
ptyprocess==0.7.0
|
||||
pure_eval==0.2.3
|
||||
pygls==1.3.1
|
||||
pylint==3.3.1
|
||||
Pygments==2.19.1
|
||||
python-jsonrpc-server==0.4.0
|
||||
python-lsp-jsonrpc==1.1.2
|
||||
requests==2.32.3
|
||||
tomlkit==0.13.2
|
||||
urllib3==2.2.3
|
||||
stack-data==0.6.3
|
||||
traitlets==5.14.3
|
||||
typing_extensions==4.13.2
|
||||
ujson==5.10.0
|
||||
urllib3==2.4.0
|
||||
wcwidth==0.2.13
|
||||
zipp==3.22.0
|
||||
zstandard==0.23.0
|
||||
|
|
25
testing_scripts/bezier_distance.py
Normal file
25
testing_scripts/bezier_distance.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
import bpy
|
||||
from mathutils import *
|
||||
from math import *
|
||||
import abc3d.butils
|
||||
|
||||
v = 0
|
||||
goal = 5.0
|
||||
step = 0.1
|
||||
speed = 1.0
|
||||
|
||||
C = bpy.context
|
||||
obj = C.scene.objects['Cube']
|
||||
curve = C.scene.objects['BézierCurve']
|
||||
|
||||
m = curve.matrix
|
||||
|
||||
def fun(distance):
|
||||
obj.location = m @ abc3d.butils.calc_point_on_bezier_curve(curve,
|
||||
distance,
|
||||
output_tangent=True)
|
||||
print(f"executed {distance}")
|
||||
|
||||
while v < goal:
|
||||
bpy.app.timers.register(lambda: fun(v), first_interval=(v * speed))
|
||||
v += step
|
115
testing_scripts/unload_glyphs.py
Normal file
115
testing_scripts/unload_glyphs.py
Normal file
|
@ -0,0 +1,115 @@
|
|||
import bpy
|
||||
|
||||
import abc3d
|
||||
from abc3d import butils
|
||||
from abc3d.common import Font
|
||||
|
||||
|
||||
def get_text_properties_by_mom(mom):
|
||||
scene = bpy.context.scene
|
||||
abc3d_data = scene.abc3d_data
|
||||
|
||||
for text_properties in abc3d_data.available_texts:
|
||||
if mom == text_properties.text_object:
|
||||
return text_properties
|
||||
return None
|
||||
|
||||
|
||||
def isolate_objects(objects):
|
||||
for area in bpy.context.window.screen.areas:
|
||||
if area.type == "VIEW_3D":
|
||||
with bpy.context.temp_override(
|
||||
selected_objects=list(objects),
|
||||
area=area,
|
||||
refgion=[region for region in area.regions if region.type == "WINDOW"][
|
||||
0
|
||||
],
|
||||
screen=bpy.context.window.screen,
|
||||
):
|
||||
# bpy.ops.view3d.view_selected()
|
||||
bpy.ops.view3d.localview(frame_selected=True)
|
||||
break
|
||||
|
||||
|
||||
def main():
|
||||
# create a curve
|
||||
bpy.ops.curve.primitive_bezier_curve_add(
|
||||
radius=1,
|
||||
enter_editmode=False,
|
||||
align="WORLD",
|
||||
location=(0, 0, 0),
|
||||
scale=(1, 1, 1),
|
||||
)
|
||||
# new curve is active object
|
||||
mom = bpy.context.active_object
|
||||
|
||||
# make sure
|
||||
print(f"MOM: {mom.name}")
|
||||
|
||||
fonts = Font.get_loaded_fonts_and_faces()
|
||||
if len(fonts) == 0:
|
||||
print("no fonts! what?")
|
||||
return
|
||||
|
||||
font_name = fonts[0][0]
|
||||
face_name = fonts[0][1]
|
||||
font = f"{font_name} {face_name}"
|
||||
|
||||
isolate_objects([mom])
|
||||
|
||||
bpy.ops.abc3d.placetext(
|
||||
font_name=font_name,
|
||||
face_name=face_name,
|
||||
font=font,
|
||||
text="SOMETHING SOMETHING BROKEN ARMS",
|
||||
letter_spacing=0,
|
||||
font_size=1,
|
||||
offset=0,
|
||||
translation=(0, 0, 0),
|
||||
orientation=(1.5708, 0, 0),
|
||||
)
|
||||
|
||||
def change_text(font_name="", face_name="", text=""):
|
||||
print(f"change_text to '{text}'")
|
||||
text_properties = get_text_properties_by_mom(mom)
|
||||
if font_name != "":
|
||||
text_properties["font_name"] = font_name
|
||||
if face_name != "":
|
||||
text_properties["face_name"] = face_name
|
||||
if text != "":
|
||||
text_properties.text = text
|
||||
else:
|
||||
text_properties.text = text_properties.text
|
||||
return None
|
||||
|
||||
def unload(glyph_id):
|
||||
print(f"unload glyph '{glyph_id}'")
|
||||
butils.unload_unused_glyph(font_name, face_name, glyph_id)
|
||||
return None
|
||||
|
||||
def unload_all():
|
||||
print(f"unload glyph all unused glyphs")
|
||||
butils.unload_unused_glyphs()
|
||||
return None
|
||||
|
||||
bpy.app.timers.register(lambda: change_text(text="SOMETHING"), first_interval=0)
|
||||
bpy.app.timers.register(lambda: change_text(text="LOLSS"), first_interval=2)
|
||||
bpy.app.timers.register(lambda: change_text(text="LOLAA"), first_interval=3)
|
||||
bpy.app.timers.register(lambda: change_text(text="WHAT"), first_interval=4)
|
||||
bpy.app.timers.register(lambda: change_text(text="LOL"), first_interval=5)
|
||||
|
||||
bpy.app.timers.register(lambda: unload("A"), first_interval=10)
|
||||
bpy.app.timers.register(lambda: unload_all(), first_interval=12)
|
||||
|
||||
bpy.app.timers.register(lambda: change_text(text="LOLM"), first_interval=16)
|
||||
bpy.app.timers.register(lambda: change_text(text="ZHE END"), first_interval=20)
|
||||
|
||||
bpy.app.timers.register(
|
||||
lambda: change_text(font_name="NM_Origin", face_name="Tender"),
|
||||
first_interval=30,
|
||||
)
|
||||
|
||||
bpy.app.timers.register(lambda: unload_all(), first_interval=42)
|
||||
|
||||
|
||||
main()
|
Loading…
Add table
Add a link
Reference in a new issue