Merge pull request #960 from jwagenet/text-update

Add OCCT text alignment to `Text`/`make_text`
This commit is contained in:
Roger Maitland 2025-05-01 14:29:56 -04:00 committed by GitHub
commit 6a4a3b54c4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 115 additions and 19 deletions

View file

@ -228,7 +228,7 @@ Cheat Sheet
+----------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------+
| :class:`~build_enums.Extrinsic` | XYZ, XZY, YZX, YXZ, ZXY, ZYX, XYX, XZX, YZY, YXY, ZXZ, ZYZ |
+----------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------+
| :class:`~build_enums.FontStyle` | REGULAR, BOLD, ITALIC |
| :class:`~build_enums.FontStyle` | REGULAR, BOLD, BOLDITALIC, ITALIC |
+----------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------+
| :class:`~build_enums.FrameMethod` | CORRECTED, FRENET |
+----------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------+
@ -262,6 +262,8 @@ Cheat Sheet
+----------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------+
| :class:`~build_enums.SortBy` | LENGTH, RADIUS, AREA, VOLUME, DISTANCE |
+----------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------+
| :class:`~build_enums.TextAlign` | BOTTOM, CENTER, LEFT, RIGHT, TOP, TOPFIRSTLINE |
+----------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------+
| :class:`~build_enums.Transition` | RIGHT, ROUND, TRANSFORMED |
+----------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------+
| :class:`~build_enums.Unit` | MC, MM, CM, M, IN, FT |

View file

@ -63,6 +63,7 @@ __all__ = [
"Select",
"Side",
"SortBy",
"TextAlign",
"Transition",
"Unit",
"Until",

View file

@ -220,6 +220,7 @@ class FontStyle(Enum):
REGULAR = auto()
BOLD = auto()
ITALIC = auto()
BOLDITALIC = auto()
def __repr__(self):
return f"<{self.__class__.__name__}.{self.name}>"
@ -344,6 +345,20 @@ class SortBy(Enum):
return f"<{self.__class__.__name__}.{self.name}>"
class TextAlign(Enum):
"""Text Alignment"""
BOTTOM = auto()
CENTER = auto()
LEFT = auto()
RIGHT = auto()
TOP = auto()
TOPFIRSTLINE = auto()
def __repr__(self):
return f"<{self.__class__.__name__}.{self.name}>"
class Transition(Enum):
"""Sweep discontinuity handling option"""

View file

@ -36,7 +36,7 @@ from typing import cast
from collections.abc import Iterable
from build123d.build_common import LocationList, flatten_sequence, validate_inputs
from build123d.build_enums import Align, FontStyle, Mode
from build123d.build_enums import Align, FontStyle, Mode, TextAlign
from build123d.build_sketch import BuildSketch
from build123d.geometry import (
Axis,
@ -538,21 +538,37 @@ class Text(BaseSketchObject):
"""Sketch Object: Text
Create text defined by text string and font size.
May have difficulty finding non-system fonts depending on platform and render default.
font_path defines an exact path to a font file and overrides font.
Fonts installed to the system can be specified by name and FontStyle. Fonts with
subfamilies not in FontStyle should be specified with the subfamily name, e.g.
"Arial Black". Alternatively, a specific font file can be specified with font_path.
Note: Windows 10+ users must "Install for all users" for fonts to be found by name.
Not all fonts have every FontStyle available, however ITALIC and BOLDITALIC will
still italicize the font if the respective font file is not available.
text_align specifies alignment of text inside the bounding box, while align the
aligns the bounding box itself.
Optionally, the Text can be positioned on a non-linear edge or wire with a path and
position_on_path.
Args:
txt (str): text to render
font_size (float): size of the font in model units
font (str, optional): font name. Defaults to "Arial"
font_path (str, optional): system path to font file. Defaults to None
font_style (Font_Style, optional): font style, REGULAR, BOLD, or ITALIC.
Defaults to Font_Style.REGULAR
align (Align | tuple[Align, Align], optional): align MIN, CENTER, or MAX of object.
Defaults to (Align.CENTER, Align.CENTER)
font_style (Font_Style, optional): font style, REGULAR, BOLD, BOLDITALIC, or
ITALIC. Defaults to Font_Style.REGULAR
text_align (tuple[TextAlign, TextAlign], optional): horizontal text align
LEFT, CENTER, or RIGHT. Vertical text align BOTTOM, CENTER, TOP, or
TOPFIRSTLINE. Defaults to (TextAlign.CENTER, TextAlign.CENTER)
align (Align | tuple[Align, Align], optional): align MIN, CENTER, or MAX of
object. Defaults to None
path (Edge | Wire, optional): path for text to follow. Defaults to None
position_on_path (float, optional): the relative location on path to position the
text, values must be between 0.0 and 1.0. Defaults to 0.0
position_on_path (float, optional): the relative location on path to position
the text, values must be between 0.0 and 1.0. Defaults to 0.0
rotation (float, optional): angle to rotate object. Defaults to 0
mode (Mode, optional): combination mode. Defaults to Mode.ADD
"""
@ -567,7 +583,8 @@ class Text(BaseSketchObject):
font: str = "Arial",
font_path: str | None = None,
font_style: FontStyle = FontStyle.REGULAR,
align: Align | tuple[Align, Align] | None = (Align.CENTER, Align.CENTER),
text_align: tuple[TextAlign, TextAlign] = (TextAlign.CENTER, TextAlign.CENTER),
align: Align | tuple[Align, Align] | None = None,
path: Edge | Wire | None = None,
position_on_path: float = 0.0,
rotation: float = 0.0,
@ -581,6 +598,7 @@ class Text(BaseSketchObject):
self.font = font
self.font_path = font_path
self.font_style = font_style
self.text_align = text_align
self.align = align
self.text_path = path
self.position_on_path = position_on_path
@ -593,6 +611,7 @@ class Text(BaseSketchObject):
font=font,
font_path=font_path,
font_style=font_style,
text_align=text_align,
align=align,
position_on_path=position_on_path,
text_path=path,

View file

@ -67,11 +67,22 @@ import OCP.TopAbs as ta
from OCP.BRepAlgoAPI import BRepAlgoAPI_Fuse
from OCP.Font import (
Font_FA_Bold,
Font_FA_BoldItalic,
Font_FA_Italic,
Font_FA_Regular,
Font_FontMgr,
Font_SystemFont,
)
from OCP.gp import gp_Ax3
from OCP.Graphic3d import (
Graphic3d_HTA_LEFT,
Graphic3d_HTA_CENTER,
Graphic3d_HTA_RIGHT,
Graphic3d_VTA_BOTTOM,
Graphic3d_VTA_CENTER,
Graphic3d_VTA_TOP,
Graphic3d_VTA_TOPFIRSTLINE,
)
from OCP.GProp import GProp_GProps
from OCP.NCollection import NCollection_Utf8String
from OCP.StdPrs import StdPrs_BRepTextBuilder as Font_BRepTextBuilder, StdPrs_BRepFont
@ -85,7 +96,7 @@ from OCP.TopoDS import (
TopoDS_Shape,
)
from anytree import PreOrderIter
from build123d.build_enums import Align, CenterOf, FontStyle
from build123d.build_enums import Align, CenterOf, FontStyle, TextAlign
from build123d.geometry import (
TOLERANCE,
Axis,
@ -238,7 +249,8 @@ class Compound(Mixin3D, Shape[TopoDS_Compound]):
font: str = "Arial",
font_path: str | None = None,
font_style: FontStyle = FontStyle.REGULAR,
align: Align | tuple[Align, Align] | None = (Align.CENTER, Align.CENTER),
text_align: tuple[TextAlign, TextAlign] = (TextAlign.CENTER, TextAlign.CENTER),
align: Align | tuple[Align, Align] | None = None,
position_on_path: float = 0.0,
text_path: Edge | Wire | None = None,
) -> Compound:
@ -254,12 +266,15 @@ class Compound(Mixin3D, Shape[TopoDS_Compound]):
font_size: size of the font in model units
font: font name
font_path: path to font file
font_style: text style. Defaults to FontStyle.REGULAR.
font_style: text style. Defaults to FontStyle.REGULAR
text_align (tuple[TextAlign, TextAlign], optional): horizontal text align
LEFT, CENTER, or RIGHT. Vertical text align BOTTOM, CENTER, TOP, or
TOPFIRSTLINE. Defaults to (TextAlign.CENTER, TextAlign.CENTER)
align (Union[Align, tuple[Align, Align]], optional): align min, center, or max
of object. Defaults to (Align.CENTER, Align.CENTER).
of object. Defaults to None
position_on_path: the relative location on path to position the text,
between 0.0 and 1.0. Defaults to 0.0.
text_path: a path for the text to follows. Defaults to None - linear text.
between 0.0 and 1.0. Defaults to 0.0
text_path: a path for the text to follows. Defaults to None (linear text)
Returns:
a Compound object containing multiple Faces representing the text
@ -306,8 +321,39 @@ class Compound(Mixin3D, Shape[TopoDS_Compound]):
FontStyle.REGULAR: Font_FA_Regular,
FontStyle.BOLD: Font_FA_Bold,
FontStyle.ITALIC: Font_FA_Italic,
FontStyle.BOLDITALIC: Font_FA_BoldItalic,
}[font_style]
if text_align[0] not in [TextAlign.LEFT, TextAlign.CENTER, TextAlign.RIGHT]:
raise ValueError(
"Horizontal TextAlign must be LEFT, CENTER, or RIGHT. "
f"Got {text_align[0]}"
)
if text_align[1] not in [
TextAlign.BOTTOM,
TextAlign.CENTER,
TextAlign.TOP,
TextAlign.TOPFIRSTLINE,
]:
raise ValueError(
"Vertical TextAlign must be BOTTOM, CENTER, TOP, or TOPFIRSTLINE. "
f"Got {text_align[1]}"
)
horiz_align = {
TextAlign.LEFT: Graphic3d_HTA_LEFT,
TextAlign.CENTER: Graphic3d_HTA_CENTER,
TextAlign.RIGHT: Graphic3d_HTA_RIGHT,
}[text_align[0]]
vert_align = {
TextAlign.BOTTOM: Graphic3d_VTA_BOTTOM,
TextAlign.CENTER: Graphic3d_VTA_CENTER,
TextAlign.TOP: Graphic3d_VTA_TOP,
TextAlign.TOPFIRSTLINE: Graphic3d_VTA_TOPFIRSTLINE,
}[text_align[1]]
mgr = Font_FontMgr.GetInstance_s()
if font_path and mgr.CheckFont(TCollection_AsciiString(font_path).ToCString()):
@ -330,7 +376,12 @@ class Compound(Mixin3D, Shape[TopoDS_Compound]):
font_kind,
float(font_size),
)
text_flat = Compound(builder.Perform(font_i, NCollection_Utf8String(txt)))
text_flat = Compound(
builder.Perform(
font_i, NCollection_Utf8String(txt), gp_Ax3(), horiz_align, vert_align
)
)
# Align the text from the bounding box
align_text = tuplify(align, 2)

View file

@ -55,6 +55,7 @@ class TestEnumRepr(unittest.TestCase):
Side,
SortBy,
Transition,
TextAlign,
Unit,
Until,
]

View file

@ -361,7 +361,8 @@ class TestBuildSketchObjects(unittest.TestCase):
self.assertEqual(t.font, "Arial")
self.assertIsNone(t.font_path)
self.assertEqual(t.font_style, FontStyle.REGULAR)
self.assertEqual(t.align, (Align.CENTER, Align.CENTER))
self.assertEqual(t.text_align, (TextAlign.CENTER, TextAlign.CENTER))
self.assertIsNone(t.align)
self.assertIsNone(t.text_path)
self.assertEqual(t.position_on_path, 0)
self.assertEqual(t.rotation, 0)
@ -369,6 +370,12 @@ class TestBuildSketchObjects(unittest.TestCase):
self.assertEqual(len(test.sketch.faces()), 4)
self.assertEqual(t.faces()[0].normal_at(), Vector(0, 0, 1))
with self.assertRaises(ValueError):
Text("test", 2, text_align=(TextAlign.BOTTOM, TextAlign.BOTTOM))
with self.assertRaises(ValueError):
Text("test", 2, text_align=(TextAlign.LEFT, TextAlign.LEFT))
def test_trapezoid(self):
with BuildSketch() as test:
t = Trapezoid(6, 2, 63.434948823)