56.8 Writing and transforming text and formulas
Use text- and formula-aware operations instead of guessing generic shape matches.
procedure
(typewrite visual [ #:unit unit #:cursor? cursor? #:cursor-style cursor-style]) → typewrite-request? visual : text-visual? unit : (or/c 'grapheme 'run 'line 'span) = 'grapheme cursor? : boolean? = #f cursor-style : (or/c #f color-spec?) = #f
#:unit controls whether the front advances by Unicode grapheme, whitespace-preserving run, line, or rich-text span intervals. 'run means maximal whitespace/non-whitespace source runs; it is not Unicode word segmentation, and 'word is reserved for a future real word-boundary mode. A renderer must be able to associate those intervals with stable shaped fragments. The current Pict renderer prepares one frozen whole layout for every text Visual, but it exposes exact fragment masks only for a conservative unwrapped, single-run LTR subset. It reports a contract error for interior partial samples of rich or wrapped text, transformed text, bidirectional or complex-script text, and common ligature/kerning-sensitive sequences. This deliberate refusal prevents incorrect prefix re-layout or rectangular masks from being represented as exact shaping. Exact start and final samples remain valid for every text-visual?. A future shaped-layout backend may advertise broader fragment-mask capabilities.
With #:cursor? #t, the renderer adds a clip-local cursor at the prepared-layout reveal frontier. #:cursor-style selects its colour; #f uses the source text colour. The cursor is presentation only: it does not add a scene Visual or cache entry, and it is absent at the exact final sample that restores visual.
procedure
(typewrite-request? value) → boolean?
value : any/c
procedure
(erase-text target [ #:unit unit #:cursor? cursor? #:cursor-style cursor-style]) → erase-text-request? target : (or/c text-visual? symbol? visual-path?) unit : (or/c 'grapheme 'run 'line 'span) = 'grapheme cursor? : boolean? = #f cursor-style : (or/c #f color-spec?) = #f
#:cursor? and #:cursor-style have the same presentation meaning as for typewrite. The exact initial source and exact removal endpoint do not contain a cursor; it appears only while an erasure has an interior partial text state.
procedure
(erase-text-request? value) → boolean?
value : any/c
procedure
(transform-formula-parts correspondence)
→ transform-formula-parts-request? correspondence : formula-correspondence?
The identity of (formula-correspondence-source correspondence) is the top-level scene target. That identity must already name a formula-assembly-visual when scene-play compiles the request. The current assembly must have exactly the same local part names, in the same order, as the correspondence source. The actual current formulas, local transforms, and local opacities are used as the source values. This allows an earlier clip to change those values before this request is compiled.
The correspondence destination is a part-layout template. Its exact ordered part list becomes the structural endpoint. Its top-level identity, reference position, rotation, scale, and opacity are not copied. The current source assembly keeps those outer values unless simultaneous requests change them.
For every explicit match, the local translation, rotation, and x/y scale are interpolated from the current source formula to the destination formula. The part opacity is also interpolated.
A matched pair uses one moving layer when these typesetting values are equal:
LaTeX source;
formula mode and semantic font size;
preamble;
ordered document-class options and Preview-package options;
horizontal and vertical anchor choices.
Identity, local transform, and local opacity are not part of that equality test. When one of the listed typesetting values differs, two layers move along the same transform interpolation: the current source layer fades to zero, and the destination layer fades in from zero. This is a moving cross-fade. The library does not morph glyph outlines or TeX boxes.
An unmatched source part remains at its current local transform and fades to zero. An unmatched destination part remains at its destination local transform and fades in from zero.
Interior drawing order is deterministic:
unmatched source parts in source part order;
matched layers in explicit correspondence-match order;
unmatched destination parts in destination part order.
A changed matched pair contributes its source layer immediately before its destination layer. Interior layers receive deterministic temporary local names beginning with __formula-transition-. The allocator avoids the top-level assembly identity and every source and destination part name. Exact endpoint samples use the original endpoint names, not the temporary names.
At eased progress zero, the exact current source part list is used. At interior progress, the temporary layers are used. At structural completion, the exact destination part list is installed even when the easing procedure does not map one to one. Such an easing procedure can therefore cause a discontinuity at the clip boundary.
The request may run simultaneously with move-to, rotation, scale, and fade-to requests for the same assembly because those operations change separate components. It conflicts with another formula-part transformation. It also reserves the presence component, so it conflicts with same-target fade-in, fade-out, create, and uncreate. Those operations add or remove the top-level identity, and combining their structural endpoints would otherwise make completion order significant.
An empty source assembly, empty destination assembly, and empty match list form a valid transformation.
procedure
(transform-formula-parts-request? value) → boolean?
value : any/c
procedure
(write-in visual [ #:order order #:lag-ratio lag-ratio #:outline-stroke-width outline-stroke-width #:reveal reveal #:reverse? reverse? #:rate-func rate-func]) → write-in-request? visual : visual? order : (or/c 'document 'left-to-right) = 'document lag-ratio : (or/c #f nonnegative-real?) = #f outline-stroke-width : nonnegative-real? = 2 reveal : (or/c 'bezier 'arc-length) = 'bezier reverse? : boolean? = #f rate-func : (-> finite-real? finite-real?) = linear
The default 'bezier reveal gives every ordered line or Bézier segment equal writing time, matching Manim’s partial-VMobject behavior. 'arc-length retains constant-speed geometric progress as an explicit alternative. #:reverse? #t writes both leaves and their path traversal in reverse. The scene easing and #:rate-func are applied to each leaf after its stagger offset, so nonlinear easing does not delay the start of later leaves.
'document follows group/SVG path order. 'left-to-right sorts the path leaves by their resolved horizontal position before calculating the same stagger. The supplied Visual must be absent from the scene at the clip start. The endpoint is installed exactly as supplied, so semantic SVG circles and rectangles, and tagged formula fragments rendered through their normal SVG renderer, are restored without a proxy representation at completion.
Built-in path Visuals, groups whose leaves are writable, circles, rectangles, and tagged-formula assemblies are supported. Tagged formulas expand dvisvgm’s local glyph-path <defs> and <use> references only while the request is constructed; sampling and rendering the clip never invoke TeX. Arbitrary renderer Visuals, gradients, masks, filters, and SVG text are not writable. The request reserves all target Visual components, so it cannot run simultaneously with another same-target Visual animation.
The name is write-in, rather than write, so requiring the library does not shadow Racket’s ordinary output procedure.
procedure
(unwrite target [ #:order order #:lag-ratio lag-ratio #:outline-stroke-width outline-stroke-width #:rate-func rate-func]) → unwrite-request? target : (or/c visual? symbol? visual-path?) order : (or/c 'document 'left-to-right) = 'document lag-ratio : (or/c #f nonnegative-real?) = #f outline-stroke-width : nonnegative-real? = 2 rate-func : (-> finite-real? finite-real?) = linear
procedure
(write-in-request? value) → boolean?
value : any/c
procedure
(unwrite-request? value) → boolean?
value : any/c
56.8.1 Serializable Rate Functions
Built-in easings are represented as transparent callable values. A rate-function? can therefore be supplied anywhere a one-argument easing procedure is accepted, while its kind and parameters remain part of the scene’s serializable representation. Arbitrary one-argument procedures remain supported; they are intentionally opaque to automatic authoring caches.
procedure
(formula-part-targets formula selections) → target-sequence?
formula : formula-assembly-visual? selections : (or/c list? vector?)
procedure
(reveal-formula-parts formula selections make-request [ #:order order #:lag-ratio lag-ratio]) → any/c formula : formula-assembly-visual? selections : (or/c list? vector?) make-request : procedure? order : (or/c 'forward 'reverse animation-order?) = 'forward lag-ratio : (and/c finite-real? (>=/c 0)) = 1/5
This helper deliberately chooses no effect by name. For example, an author can make an ordinary semantic formula-part cascade with:
(reveal-formula-parts equation '(x equals two) (lambda (reference) (indicate (target-ref-path reference) #:color "gold")) #:lag-ratio 1/5)
All normal target validation, timing, conflict checking, and random-access sampling rules are inherited from the concrete requests returned by the template.