A tour of the features. Every symbol here is a placeholder (, , , ) and the scene is deliberately empty of meaning: a point, a segment, a circle. Each bullet states one thing pygeomatic can do and demonstrates it with the smallest live example that shows the mechanism.
pygeomatic fencegm. call both evaluates numerically and appends one DSL line, so the
reader receives deterministic command text, never Python.gm.mul(k, 2) is exactly x = \mul k 2. Repetition is
ordinary Python loops, not a batch API.k + 1 records
\add), but the explicit form is always available and is the only option
elsewhere: gm.add(a, b), gm.pow_(a, 2), and nothing infix on a Point or a
Circle. In-place += raises, so assign a new name instead.sum_ab above is the node
sum-ab; out="my-id" overrides, and engine shapes like num0 are refused.gm.p0 is the origin used above as the
segment's first endpoint, unit is the zoom, gm.T / gm.F are the literals.gm.load_colors() once, then c.BLUE, c.AMBER, or
c["COLOR-TEAL-LIGHT"] for the hyphenated ramp names.A with group(...) block is one beat; a prose span whose target is ref:name
reveals it, so the figure builds click by click and each click lands on a fully
set-up scene.
Objects built as scaffolding are hidden at build time with gm.hide and brought
back with gm.show inside the beat that earns them.
An inline span carrying a Python statement is a one-off command the reader triggers. Article mode is last-write-wins, so re-binding a name is a reassignment.
${k} in place.Give a $$…$$ formula an id with a %id: first line, take a handle with
gm.tex(...), and bind a store node into a slot. Binding replaces content, so
every slot needs a placeholder symbol already written in the LaTeX.
SCHEMA in src/pygeomatic/tex.py:
int / sum / prod take lower, upper, body; frac takes num and
denom; sqrt takes body; underbrace / overbrace are reveal-only.fmt is ".2f" or "d", or omit it to trim to four decimals, and
show="symbol" links a slot without substituting its glyph.A highlight is a predicate over a cell's grid position, never its content.
gm.rows, gm.cols, gm.dim(i); compare them with ==, <, >
against an int or a node, and combine regions with & and |.matrix=N picks the -th matrix in a multi-matrix formula, counting real
matrices in source order and skipping aligned-style layout blocks.The same selector machine, painting opacity instead of color. Use strict < so
the gate counts how many parts show and shows nothing.
mode="collapse" is available for lines and slots
but would break a grid.Bindings never become DSL: they ride a separate channel and land in a trailing
<!-- texatlas:v1 … --> comment. The compiler replays the whole document
through parse_dsl in order, so a broken article fails the build, not the
reader:
uv run python scripts/compile_article.py examples/agent-output.md