simvx.graphics.draw2d_text¶
MSDF text rendering for Draw2D – the ONE 2D text builder.
This is the single glyph layout + measure + emit path for the whole engine. It
feeds both the legacy flag-OFF Draw2D op stream (draw_text appends a
TEXT :class:Op) and the flag-ON item pipeline (which re-runs the same
layout natively via :func:layout_glyph_run). The desktop overlay
TextRenderer.draw_text path it replaced is deleted; only the MSDF atlas
generation in :mod:text_renderer survives (both paths share it).
Three text-layout reconciliations:
Font-size unit =
font_scale * 16is canonical (_LOGICAL_BASE = 16); the old divergent14is replaced so a label renders the same pixel height whichever node draws it and whichever backend. The* sycontent-scale factor multiplies on top (applied by the caller / the_xf_scfactor).Kerning is ported from the deleted overlay path – the cursor advances by
font.get_kerning(prev, ch) * scalebetween glyph pairs, so the kerned advance is pixel-identical to the old overlay (0.00 px diff).Quad rounding =
ceil(fully contains the glyph + padding) replaces the oldmax(1.0, ...); align spelling ='centre'(UK, repo rule) – the banned US'center'is accepted transitionally and mapped to'centre'.
Text never silently goes blank. A character the primary font lacks is drawn from the font fallback chain; one no available font can draw at all is drawn as a hollow box in the text’s own colour, and reported once per codepoint, rather than advancing the cursor over an invisible gap. Text drawn large enough to read them writes the codepoint out, one box per byte, so the character can be identified from the screen rather than guessed at.
Module Contents¶
Classes¶
Mixin providing MSDF text rendering and measurement for Draw2D. |
Functions¶
Lay out |
Data¶
API¶
- simvx.graphics.draw2d_text.log¶
‘getLogger(…)’
- class simvx.graphics.draw2d_text.Draw2DTextMixin¶
Mixin providing MSDF text rendering and measurement for Draw2D.
- classmethod set_font(path: str | None = None, size: int = 48) None¶
Load an MSDF font atlas via the shared TextRenderer.
- classmethod draw_text(text, pos=None, *, colour=None, scale=1.0, rect=None, alignment='left', vertical_alignment='top', fit_to_width=False, min_scale=None, outline=0.0, outline_colour=None, screen_space=False, cell_width=0.0)¶
Draw text at
posor insiderectwith optional alignment.screen_space=Truebypasses the active Camera2D transform (the screen-pinned HUD-label case – the behaviour Text2D’s deleted overlay pass had: text stays fixed while the world camera pans).Two positioning modes:
pos=(x, y): text anchored at the given coordinate.alignment('left'/'centre'/'right') anchors each line’s left edge / centre / right edge onx(the overlay-parity point anchor).rect=(x, y, w, h): text positioned byalignmentandvertical_alignment(top/centre/bottom) inside the rect.fit_to_width=Trueshrinksscaleso the text fitsrect.w, clamped tomin_scale.
outline(in glyph-em units, e.g.0.08) draws a 4-direction offset copy of the run inoutline_colour(default opaque black) UNDER the main run – the common port “readable text on any background” pattern.min_scale=Noneapplies the ~10px readable safety net; an explicit value honours the caller’s floor exactly.cell_widthis the pitch of the character grid this text sits on, in the pixels it is laid out in (before any Draw2D transform), and is how a caller that has a grid says so: a terminal, a code view, a tabular readout. It changes one thing: a character no font can draw occupies exactly one cell, drawn as the largest form that fits it, instead of taking the room a box asks for in proportional text. The columns after it stay on the grid, and nothing it draws reaches the column beside it, which is what a caller that places every character itself needs. Text that is not on a grid leaves it at zero and is drawn exactly as before.Both the US
'center'and the UK'centre'are accepted;'centre'is canonical (repo spelling).
- classmethod text_height(text, scale=1.0)¶
Height of
textin pixels atscale.Multi-line strings (containing
\n) accumulate line heights using the font’s line-height metric. Returns 0 for empty input.
- classmethod text_size(text, scale=1.0)¶
Return
(width, height)in pixels atscale.
- classmethod fit_scale(text, max_width, *, base_scale=1.0, min_scale=None, cell_width=0.0)¶
Largest scale ≤
base_scalethat fitstextwithinmax_width.Returns
base_scalewhen the text already fits or when inputs are degenerate. The returned value matches what :meth:draw_textwould actually use, so callers can compute width metrics that align with what’s drawn.min_scalesemantics mirror :meth:draw_text:None(default): clamps from below by the readable-pixel floor (~10px) so the returned scale never produces illegible MSDF output.explicit value: the caller’s floor is honoured exactly, bypassing the readable safety net.
- classmethod text_width(text, scale=1, *, cell_width=0.0)¶
Width of
textin pixels atscale.Multi-line strings (containing
\n) return the widest line; newlines themselves contribute no horizontal advance. The walk is- Meth:
_layout_run’s, without the geometry: the same advances, and the same rule for which neighbours a kerning pair reaches across.
cell_widthis the caller’s character-grid pitch, in the pixels this returns, and means what it does on :meth:draw_text: a boxed character is reserved one cell, so the width comes back as the run is drawn.A character the primary face lacks but another draws is measured at the borrowing face’s advance without consulting the fallback chain here: the walk below is preceded by
_ensure_with_fallback, which resolves the character through that chain and packs its glyph into the atlas, so it is an ordinary atlas cell by the time the walk reaches it.- Func:
~simvx.core.text.measure_text_widthasks the same lookup directly and gets the same face at the same size, which is why the two agree. The chain refuses to lend a glyph for a character defined to occupy no space, so neither walk can be handed one to measure.
- simvx.graphics.draw2d_text.layout_glyph_run(text, pos=None, *, colour=None, scale=1.0, rect=None, alignment='left', vertical_alignment='top', fit_to_width=False, min_scale=None, outline=0.0, outline_colour=None, cell_width=0.0)¶
Lay out
textinto MSDF glyph quads in LOCAL space (the item path).The native-emission counterpart of :meth:
Draw2DTextMixin.draw_text: it runs the SAME single layout (kerning,*16unit,ceil,centre) but without baking anyDraw2Dtransform and without appending anOp– it returns(verts, indices)so the item builder can stow them as aGLYPHitem’s geometry (geometry stays camera-/parent-free, the node’s transform rides the item’s transform row). Returns([], [])when there is no font or no visible glyphs.Because both paths funnel through :meth:
Draw2DTextMixin._layout_run, the item-path glyph geometry is byte-identical to the op-path geometry at the same position/scale – which is what makes flag-ON text match flag-OFF. That covers the missing-glyph box’s choice of form as well: noform_scaleis passed here because there is no transform to exclude, and the op path passes its own untransformed scale so it reaches the same choice.cell_widthrides through unchanged, so a monospace caller reaches the same grid whichever path draws it.