16 Quick Start
The following program builds Cartesian axes, samples a coordinate-valued parametric procedure, plots one ordered data series, and animates the camera at the same time. Both curves use smooth cubic interpolation and the ordinary path create animation.
#lang racket/base (require animate) (require animate/render) (define coordinate-axes (axes #:id 'coordinate-axes #:x-range (axis-range -4 4 1) #:y-range (axis-range -3 3 1) #:x-length 8 #:y-length 6 #:stroke "navy")) (define loop-curve (parametric-curve coordinate-axes (lambda (parameter) (define x (- (* parameter parameter) 2)) (vec2 x (/ (* parameter x) 2))) #:id 'loop-curve #:parameter-range (parameter-range -2 2) #:sample-count 181 #:interpolation 'smooth #:stroke "crimson")) (define observations (data-plot coordinate-axes (list (vec2 -3 -3/2) (vec2 -2 1/2) (vec2 -1 1) (vec2 0 1/4) (vec2 1 -1) (vec2 2 -1/2) (vec2 3 3/2)) #:id 'observations #:interpolation 'smooth #:stroke "seagreen")) (define initial-camera (make-camera #:world-width 14 #:center origin)) (define animation (scene-wait (scene-play (make-scene #:camera initial-camera) (fade-in coordinate-axes) (create loop-curve) (create observations) (camera-pan-to (vec2 1 0)) (camera-zoom-by 3/2) #:duration 2) 1/2)) (render-frames! animation "frames" #:fps 30)
parametric-curve calls the procedure while constructing the Visual. It stores only immutable path geometry and a snapshot of the axes transform. Rendering later frames does not call the procedure or read the original data list again. The camera center and visible width are sampled from the same scene clip as the Visual animations.
The call to render-frames! creates numbered PNG files. To assemble them as an MP4 file, install FFmpeg and call:
(encode-mp4! "frames" "animation.mp4" #:fps 30)
16.1 The Model
This section explains the terms used throughout the reference.
16.1.1 World Coordinates
A scene uses mathematical coordinates. Positive x points right. Positive y points up. A camera converts these world coordinates to pixels, where positive y points down.
Lengths in ordinary Visual values are normally measured in world units. Camera width and height are measured in pixels. Rotation is measured in counter-clockwise radians.
SCENE-X also has frame space. A frame-space Visual uses an origin-centered mathematical coordinate system attached to the output frame. Positive x still points right and positive y still points up. Its visible width is captured when the frame-space wrapper is constructed, so later world-camera pan and zoom do not move or resize the overlay. The current output pixel width and height are still used when rendering, allowing the same semantic frame coordinates to scale with output resolution.
16.1.2 Visuals and Identity
A Visual is semantic model data. It is not a Pict. Every Visual has a stable symbol identity and a reference position. A Visual may also implement the affine-Visual protocol for rotation and scaling and the opacity-Visual protocol for global opacity. A group is a Visual whose ordered children are other affine Visuals.
Identity is explicit:
(circle #:id 'moving-circle)
Two top-level Visuals with the same identity cannot be present in one scene state. Immutable updates must preserve identity. A group’s direct children must have distinct identities and no descendant may reuse that group’s identity. The same local child identity may occur in separate branches, since complete nested paths remain distinct. A custom affine Visual is treated as one leaf.
Built-in group and formula children are addressable with a nonempty nested Visual path such as '(equation numerator) or '(A row-1 col-2). Formula-part names form a local namespace inside one formula assembly; a formula-part transformation still targets the containing top-level assembly and updates its parts collectively.
16.1.3 Text and Formulas
A plain-text Visual stores one line of Unicode text together with font, color, alignment, transform, and opacity data. The string is copied into immutable model storage. Plain-text model values do not contain a Pict, font% object, drawing context, or platform font handle.
A formula Visual stores a LaTeX mathematical snippet together with its display mode, semantic font size, preamble, ordered typesetting options, alignment, transform, and opacity. Formula source and option strings are also copied into immutable model storage. A formula model value does not contain a Pict, PDF page, Poppler value, cached TeX result, or running process.
Font and formula sizes are measured in local world units. Horizontal and vertical alignment select the point of the untransformed text or formula box that lies at the Visual’s reference position. Alignment is resolved first; scale and rotation are then applied around that anchor. Both kinds of Visual can be top-level scene values or ordinary affine children of a group.
Plain text supports one line only. Carriage returns and newline characters are rejected. Formula source may contain line breaks because they can be meaningful to LaTeX. An empty plain-text or formula string is valid and produces stable transparent local geometry.
Formula rendering is a separate adapter effect. A nonempty formula requires latex-pict, LaTeX, and Poppler when it is converted to a Pict.
A formula assembly stores explicitly named formula parts in back-to-front order. An ordinary assembly uses independently typeset formula Visuals at caller-selected local positions. A tagged formula instead typesets all author-declared fragments in one TeX document and records each fragment as an SVG group at its TeX-determined local position. A formula correspondence records an explicit one-to-one list of source and destination-part names. The transform-formula-parts request compiles it against the current source assembly and produces deterministic moving and fading layers.
16.1.4 Paths
A path is local semantic geometry. It contains ordered subpaths, and each subpath contains a start point followed by ordered line or cubic Bézier segments. Subpaths may be open or closed. A line segment stores its endpoint. A cubic segment stores two control points and its endpoint; its start is the previous point in the subpath.
Traversal order is also reveal order. Path length is measured in local world units. A closed subpath includes its implicit straight edge back to its start. Line length is computed directly from its endpoints. Cubic length is a deterministic approximation. Partial geometry selects an interval of the total ordered arc length and preserves cubic segments as cubic segments.
Path geometry does not contain world translation, Pict values, drawing contexts, or pixels. A path Visual combines local path geometry with an affine transform and style. morph-to interpolates paths that already have corresponding structure. morph-to-normalized first applies the limited cubic normalization described by path-geometry-normalize-for-morph. morph-to-aligned additionally selects closed-loop phase/direction before that normalization. morph-to-open-aligned selects the endpoint direction for one open source/destination pair before normalization. morph-to-open-compound-aligned globally pairs equal-count open subpaths and selects endpoint direction independently within each pair. morph-to-mixed-compound-aligned partitions compound correspondence by open/closed topology, globally pairs within each class, and then restores source subpath order before normalization. morph-to-topology-changing extends that correspondence with deterministic births/deaths when topology-class subpath counts differ. SCENE-AI keeps bounds-center seeds as the default and also permits explicit shared local birth/death anchor points. SCENE-AJ optionally assigns finite birth/death costs so a poor real correspondence may be replaced by local collapse and regrowth even when topology counts match. SCENE-AK adds sparse per-subpath birth/death anchor overrides keyed by original endpoint subpath indexes. SCENE-AL adds sparse per-subpath numeric birth/death cost overrides using those same original endpoint indexes. SCENE-AM adds sparse additive real-match penalties keyed by original source/destination index pairs. morph-to-compound-aligned first globally pairs equal-count closed subpaths and applies the same loop alignment within every pair. create and uncreate animate semantic partial paths. None of these operations animate a finished Pict.
transform-shape is the higher-level replacement operation for ordinary diagram shapes. It changes a present top-level Visual into a fresh destination Visual. Atomic built-in paths, circles, and rectangles use automatic outline correspondence; groups and other endpoint types use an intentional cross-fade fallback rather than claiming a contour correspondence they do not have.
transform-matching-visuals extends that operation over the leaves of ordinary group trees. It first honors explicit relative paths, then searches stable leaf paths and conservative built-in shape correspondence. Formula and glyph transitions retain their more specialized tagged-TeX matching API.
16.1.5 Arrows and Cartesian Axes
An arrow Visual stores one ordered shaft from a start point to an end point. The untransformed midpoint is its reference position. Optional triangular tips can be attached independently to the start and end. Tip dimensions are local world-unit geometry. Stroke width is cosmetic output style.
An axis range stores a minimum, maximum, and regular positive tick step. The range must contain zero because the built-in Cartesian axes cross at numeric coordinate (0, 0). Tick values are ordered nonzero multiples of the step that lie in the closed range.
An axes Visual maps the complete numeric x and y ranges to explicit local x and y lengths. It contains two shafts, regular ticks, and optional tips at the maximum x and y endpoints. Numeric coordinates can be converted to points in the axes’ containing coordinate system and converted back again. The conversion includes the axes’ translation, rotation, and positive scale.
Arrows and axes are pure semantic values. Their model modules do not contain Picts, drawing paths, drawing contexts, pixels, or text labels. The built-in adapter derives ordinary path geometry when rendering them.
16.1.6 Sampled Function Graphs
A sampled function graph is ordinary semantic path geometry produced from a one-variable numeric procedure and an axes Visual. Sampling happens once when sample-function-path or function-graph is called. The returned value does not retain the procedure.
Finite real results become ordered samples. #f, positive infinity, negative infinity, and NaN create explicit breaks. An optional maximum y jump can reject a connection between two finite samples. Accepted segments are clipped to the displayed axes rectangle by default.
Function graphs use explicit 'linear or 'smooth interpolation. Linear remains the default. Smooth interpolation converts every accepted run to semantic cubic Bézier segments derived from uniform Catmull-Rom tangents. Clipping happens before interpolation, and smooth controls are clamped to the axes rectangle when clipping is enabled.
A graph Visual copies the axes translation, rotation, and scale at construction time. It is an ordinary path Visual and therefore uses the existing path renderer, opacity protocol, group composition, create, uncreate, and affine animation requests.
16.1.7 Parametric Curves and Data Plots
A parameter range stores an ordered start and end value. The values may increase or decrease. Parametric sampling includes both endpoints and calls a one-argument procedure in that order. Each call returns one vec2 or #f. A coordinate becomes one sample; #f creates a gap.
An ordered data series is a proper list of vec2 values and #f gaps. List order is traversal order. The library does not sort the points by x, infer time order, or remove repeated coordinates.
Parametric and data plots can reject adjacent samples farther apart than an explicit Euclidean distance in numeric-coordinate units. They share the same segment clipping, run construction, and linear or smooth interpolation rules as function graphs. Empty input, one isolated point, or an isolated finite sample creates no drawn segment.
parametric-curve and data-plot return ordinary path Visuals whose transforms are construction-time snapshots of their axes. The sampling procedure and input point list are not stored.
16.1.8 Point Markers, Scatter Plots, and Filled Areas
A point-marker Visual stores one closed marker shape, local size, fill, stroke, affine transform, and opacity. A scatter plot is an ordered semantic group of such markers placed from numeric coordinates in an axes snapshot. Marker identities are deterministic and include the original input index. Gaps and clipped points are omitted without renumbering later markers.
A filled coordinate area is ordinary closed path geometry derived from accepted function-graph or ordered-data runs. Each visible run is closed to one horizontal numeric baseline. Discontinuous runs remain separate. Smooth interpolation keeps cubic graph segments rather than flattening them. Area Visuals store only geometry, style, transform, and opacity; they do not retain the sampling procedure or source list.
16.1.9 Scene States and Scenes
A scene state is one complete snapshot of the top-level Visuals in a scene. It stores both an identity lookup table and a significant drawing order. Drawing order is back to front: later Visuals are painted over earlier Visuals. A group occupies one top-level entry; its children keep a separate back-to-front order inside the group.
A scene is an immutable timeline. A scene contains chronological play and wait clips. Each play clip stores its complete starting state and compiled animation endpoints. Sampling one frame does not depend on sampling any earlier frame.
16.1.10 Transforms
The established affine-transform value stores translation, rotation, and scale. Components are applied in this fixed order:
scale, then rotate, then translate
Scale is stored as positive x and y factors. SCENE-CY-A adds separate linear2 and affine2 values for a full matrix and translation. apply-affine and apply-matrix map a world Visual through those values. SCENE-DK extends the map layer to ordinary nested paths: a named child can be mapped inside an already-mapped group without flattening the group. The existing decomposed affine-Visual protocol remains available for Visual implementations; the general-map wrapper supplies the bridge through a nested group tree.
A group may be translated and rotated normally, but its own legacy scale must be uniform. A uniform parent scale and rotation compose exactly with each child’s existing decomposed transform. Allowing a non-uniform parent scale followed by a rotated child can create shear, which the legacy transform model cannot represent. Use apply-affine on the complete top-level group when that is the intended mathematical operation.
16.1.11 Groups
A group is a semantic composite Visual. Its child list is stored in significant back-to-front order. Child positions are local to the group anchor. A child can itself be a group, so transforms can be nested.
During rendering, the group rotation and uniform scale are inherited by each child. The group translation places the complete composite in its parent coordinate system. Child opacity is applied to each child, and group opacity is applied to the complete composed result. Thus, opacity values multiply through nested groups.
Groups contain model values only. They do not contain Picts or renderer callbacks. The Pict adapter composes their children recursively and passes the same explicit renderer list to every descendant.
16.1.12 Relative Layout
Relative layout is an adapter-level calculation. It renders a Visual with an explicit camera and renderer list, converts the resulting Pict dimensions back to world units, and returns immutable position updates. It is not stored in a scene, group, formula assembly, or Visual model value.
A layout box is the complete symmetric Pict box around a Visual’s reference position. It includes transparent padding and anchor padding; it is not a tight outline of visible ink. Layout must use the same camera and renderer list as the final rendering when exact spacing matters.
16.1.13 Opacity
Global opacity is semantic model data in the closed interval from zero through one. Zero means completely transparent. One means fully opaque. Intermediate values multiply the complete rendered Visual, including fill and stroke.
Opacity is applied after Pict renderer selection. It does not change a Visual’s geometry, identity, drawing order, Pict bounds, or reference position. A zero-opacity Visual remains in the scene state until an operation removes it.
16.1.14 Time and Frames
Scene time is measured in seconds. Exact rational times work and are useful in tests. For a scene duration D and frame rate fps, the frame count is:
ceiling(D * fps)
Frame n samples the scene at exact time n/fps. The exact scene endpoint is not normally a frame sample. Add a wait clip when the final state must remain visible.
16.2 Scene States
Scene-state values are immutable. Their raw constructor and fields are not part of the public API.
procedure
(scene-state? value) → boolean?
value : any/c
value
procedure
(scene-state-count state) → exact-nonnegative-integer?
state : scene-state?
procedure
(scene-state-has? state target) → boolean?
state : scene-state? target : (or/c visual? symbol? visual-path?)
procedure
(scene-state-ref state target) → visual?
state : scene-state? target : (or/c visual? symbol? visual-path?)
procedure
(scene-state-visuals-in-drawing-order state) → (listof visual?)
state : scene-state?
procedure
(scene-state-resolved-ref state target) → visual?
state : scene-state? target : (or/c visual? symbol? visual-path?)
procedure
→ (listof visual?) state : scene-state?
procedure
(scene-state-value-has? state id) → boolean?
state : scene-state? id : (or/c symbol? scene-parameter?)
procedure
(scene-state-value-ref state id) → any/c
state : scene-state? id : (or/c symbol? scene-parameter?)
16.3 Animation Requests
Animation constructor procedures return immutable request values. A request stores a target identity and a requested endpoint or relative change. The request is compiled against the scene’s current state when scene-play is called.
A target can be a Visual value, its top-level symbol identity, or a nonempty visual-path?. A path follows built-in group children and is a direct animation target; child transforms remain local to their containing group.
procedure
(value-to id destination) → value-to-request?
id : (or/c symbol? scene-parameter?) destination : any/c
An immutable scene-parameter? may be used in place of id.
procedure
(value-to-request? value) → boolean?
value : any/c
procedure
(change-number-to id destination) → change-number-to-request?
id : (or/c symbol? scene-parameter?) destination : (or/c finite-real? finite-complex?)
procedure
(change-number-to-request? value) → boolean?
value : any/c
procedure
(count-to id destination) → count-to-request?
id : (or/c symbol? scene-parameter?) destination : finite-real?
procedure
(count-to-request? value) → boolean?
value : any/c
procedure
(count-from id from to) → count-from-request?
id : (or/c symbol? scene-parameter?) from : finite-real? to : finite-real?
procedure
(count-from-request? value) → boolean?
value : any/c
procedure
(move-to target destination) → move-to-request?
target : (or/c visual? symbol?) destination : vec2?
procedure
(move-to-request? value) → boolean?
value : any/c
procedure
(move-along-path target path [ #:start start #:end end #:normal-offset normal-offset]) → move-along-path-request? target : (or/c visual? symbol?) path : (or/c path-geometry? path-visual? derived-visual? symbol?) start : (real-in 0 1) = 0 end : (real-in 0 1) = 1 normal-offset : finite-real? = 0
normal-offset adds a signed displacement perpendicular to the selected route point. A positive value lies to the left of the actual traversal direction; reverse traversal therefore reverses the normal. Zero preserves the SCENE-Y route point exactly and does not require tangent sampling. On a sharp polyline corner the offset is segment-local, so the offset trajectory can jump between the adjacent offset edge lines. Smooth cubic routes give smooth normal motion wherever their tangent is continuous.
When path is a path-geometry? value, its points are interpreted directly in target’s containing coordinate system. When it is a path Visual, derived Visual definition, or symbol identity, scene-play resolves the current top-level Visual from the prepared clip-start state by stable identity. A derived definition is evaluated against that exact state first; the concrete result must be a path Visual. The path Visual’s current affine transform is then applied to its local path points before the resulting world-space route is measured. Passing an earlier Visual value therefore selects the current scene value rather than capturing stale coordinates.
The compiled route is a snapshot for that play clip. Simultaneously moving, scaling, rotating, or morphing the path Visual does not dynamically deform the motion route. A path Visual route is world-space after resolution and cannot be used to drive a frame-space target. Raw path geometry may drive a frame-space target because it is already interpreted in that target’s containing coordinate system.
Motion requires a positive finite route with exactly one positive-length subpath. Compound drawings with multiple positive-length subpaths are valid path geometry but are rejected here so the target cannot silently teleport across a gap. Closed single-subpath routes are allowed, including their implicit closing edge.
The request changes the ordinary translation animation component. It conflicts with same-target move-to or another move-along-path, but may run with disjoint rotation, scale, opacity, path-geometry, formula-part, and camera components. Sampling places the target at the selected route point plus any requested normal offset even when its prior reference position differs; put the target at that complete start position before the clip when a continuous clip boundary is required.
procedure
(move-along-path-request? value) → boolean?
value : any/c
procedure
(orient-along-path target path [ #:start start #:end end #:rotation-offset rotation-offset]) → orient-along-path-request? target : (or/c visual? symbol?) path : (or/c path-geometry? path-visual? derived-visual? symbol?) start : (real-in 0 1) = 0 end : (real-in 0 1) = 1 rotation-offset : finite-real? = 0
Path source resolution, clip-start snapshot semantics, transformed path Visual handling, frame/world coordinate rules, positive finite length requirements, and single-positive-subpath continuity requirements are the same as for move-along-path. Tangents are provided by path-geometry-tangent-at.
The request changes only the ordinary rotation animation component. It may run simultaneously with same-target move-along-path, scale, opacity, and disjoint components, but conflicts with same-target rotate-to, rotate-by, or another orient-along-path. The rotation at a sample is derived directly from the sampled tangent rather than interpolated between endpoint angles, so bends and curved routes are followed geometrically.
procedure
(orient-along-path-request? value) → boolean?
value : any/c
procedure
(rotate-to target angle) → rotate-to-request?
target : (or/c visual? symbol?) angle : finite-real?
procedure
(rotate-to-request? value) → boolean?
value : any/c
procedure
(rotate-by target delta) → rotate-by-request?
target : (or/c visual? symbol?) delta : finite-real?
procedure
(rotate-by-request? value) → boolean?
value : any/c
procedure
(scale-to target scale) → scale-to-request?
target : (or/c visual? symbol?) scale : scale-factor?
A built-in group accepts only a uniform endpoint. scene-play rejects a request whose normalized x and y factors differ. For every affine target, compilation also checks that visual-with-scale returns an affine Visual, preserves identity, and installs the requested endpoint exactly. These checks occur before timeline sampling.
procedure
(scale-to-request? value) → boolean?
value : any/c
procedure
(scale-by target factor) → scale-by-request?
target : (or/c visual? symbol?) factor : scale-factor?
For a built-in group, the computed endpoint must remain uniform. scene-play rejects a relative factor that produces unequal x and y components. For every affine target, compilation checks the resulting endpoint through the same visual-with-scale protocol rules as scale-to.
procedure
(scale-by-request? value) → boolean?
value : any/c
procedure
(apply-affine target map) → apply-affine-request?
target : (or/c visual? symbol? visual-path?) map : affine2?
The target may be a top-level world Visual or an ordinary nested visual-path?. A nested request is rebased through enclosing semantic affine maps so its requested map still has world-coordinate meaning. The enclosing map must be invertible; derived Visuals and frame-space overlays are still rejected during scene compilation. The resulting endpoint is an affine-map-visual?, so a later apply-affine composes maps without rasterizing the prior result. Existing movement, rotation, and scale requests conflict with apply-affine for the same target during an overlapping interval.
procedure
(apply-affine-request? value) → boolean?
value : any/c
procedure
(apply-matrix target matrix) → apply-affine-request?
target : (or/c visual? symbol? visual-path?) matrix : linear2?
16.3.1 Robust Pointwise Maps
SCENE-DQ extends SCENE-CY-C’s nonlinear companion to the affine-map layer. It acts on world-space points and samples a path before mapping it, so a line under a nonlinear map becomes a visible curve rather than a chord joining two transformed endpoints. The exact caller Visual is retained at clip time zero. Adaptive refinement checks the deviation of a mapped midpoint from its mapped chord, while a failed map sample can split the result into separate subpaths.
procedure
(apply-pointwise target map-point [ #:samples samples #:adaptive? adaptive? #:tolerance tolerance #:max-depth max-depth #:discontinuities discontinuities]) → apply-pointwise-request? target : (or/c visual? symbol? visual-path?) map-point : (procedure-arity-includes/c 1) samples : exact-positive-integer? = 24 adaptive? : boolean? = #t tolerance : (and/c finite-real? positive?) = 1/32 max-depth : exact-nonnegative-integer? = 8 discontinuities : (or/c 'split 'error) = 'split
Path Visuals, circles, rectangles, axes, and arrows are converted to sampled path geometry. Groups retain their ordinary hierarchy and names. A nested target is resolved into world coordinates and rebased through its invertible enclosing affine map, preserving its siblings and their paths. Text, images, and other affine leaves without an exposed path remain at their original resolved world placement so they stay legible; they are not secretly raster-warped. The request rejects derived Visuals and frame-space overlays. It conflicts with simultaneous same-target spatial, style, opacity, formula, or path changes, because it replaces the complete sampled Visual tree.
#:samples gives the initial positive number of pieces per original line or cubic segment. With the default #:adaptive? #t, intervals whose mapped midpoint differs from their mapped chord by more than #:tolerance are bisected, up to #:max-depth. At 'split discontinuity policy, a raised error or an invalid map result omits that interval and leaves adjacent valid fragments disconnected; 'error propagates it. Point maps should be pure because refinement may call them more than once at a source point.
procedure
(apply-pointwise-request? value) → boolean?
value : any/c
procedure
(apply-homotopy target homotopy [ #:samples samples #:adaptive? adaptive? #:tolerance tolerance #:max-depth max-depth #:discontinuities discontinuities]) → apply-homotopy-request? target : (or/c visual? symbol? visual-path?) homotopy : (procedure-arity-includes/c 2) samples : exact-positive-integer? = 24 adaptive? : boolean? = #t tolerance : (and/c finite-real? positive?) = 1/32 max-depth : exact-nonnegative-integer? = 8 discontinuities : (or/c 'split 'error) = 'split
The exact source Visual is retained at clip time zero; authors normally supply (homotopy p 0) equal to p. The map must be pure, may be called repeatedly at a source point by adaptive refinement, and must return a finite vec2? for every retained sample. Nested targets, supported geometric leaves, adaptive sampling, and 'split versus 'error discontinuity semantics are the same as for apply-pointwise. As a sampled approximation, phase-dependent adaptive refinement can choose different path subdivisions at different phases; this API does not infer continuous topology changes over the full time interval.
procedure
(apply-homotopy-request? value) → boolean?
value : any/c
procedure
(pointwise-jacobian map-point point [ #:step step]) → linear2? map-point : (procedure-arity-includes/c 1) point : vec2? step : (and/c finite-real? positive?) = 1/1000
procedure
(pointwise-jacobian-determinant map-point point [ #:step step]) → finite-real? map-point : (procedure-arity-includes/c 1) point : vec2? step : (and/c finite-real? positive?) = 1/1000
procedure
(pointwise-orientation map-point point [ #:step step #:tolerance tolerance]) → (or/c 'preserving 'reversing 'singular) map-point : (procedure-arity-includes/c 1) point : vec2? step : (and/c finite-real? positive?) = 1/1000 tolerance : (and/c finite-real? positive?) = 1e-8
procedure
(inverse-map-mesh inverse-map #:id id [ #:x-min x-min #:x-max x-max #:y-min y-min #:y-max y-max #:x-count x-count #:y-count y-count #:samples samples #:tolerance tolerance #:max-depth max-depth #:stroke stroke #:stroke-width stroke-width]) → group-visual? inverse-map : (procedure-arity-includes/c 1) id : symbol? x-min : finite-real? = -3 x-max : finite-real? = 3 y-min : finite-real? = -2 y-max : finite-real? = 2 x-count : exact-integer? = 7 y-count : exact-integer? = 5 samples : exact-positive-integer? = 12 tolerance : (and/c finite-real? positive?) = 1/32 max-depth : exact-nonnegative-integer? = 8 stroke : any/c = "mediumpurple" stroke-width : (and/c finite-real? (>=/c 0)) = 2
procedure
(stroke-width-to target stroke-width) → stroke-width-to-request?
target : (or/c symbol? (and/c visual? stroke-width-visual?)) stroke-width : (and/c finite-real? (>=/c 0))
When target is a Visual value it must implement both gen:visual and gen:stroke-width-visual. When it is a symbol, scene-play checks the resolved Visual while compiling the request. Compilation validates the getter value, calls visual-with-stroke-width with the requested endpoint, requires the result to remain a stroke-width Visual with the same identity, and checks that the endpoint was installed exactly.
Stroke width is its own animation component. It may run simultaneously with translation, rotation, scale, opacity, or path-geometry changes for the same identity. Two overlapping same-target stroke-width requests conflict after AN–AR schedule expansion; touching requests may chain through a succession or other nonoverlapping schedule.
The numeric interpolation follows the leaf easing like movement and ordinary opacity animation. With an easing whose endpoint is one, the exact requested numeric width is installed at the leaf endpoint even when the starting width or schedule arithmetic is inexact. For custom Visuals, compilation also rejects a setter that changes the exact/inexact representation of that requested endpoint.
The semantic width domain is renderer-independent. The default Pict/racket/draw backend accepts cosmetic pen widths from 0 through 255 pixels and reports a renderer error for larger values. Within that backend, width zero is a device-dependent hairline rather than an instruction to remove the stroke.
procedure
(stroke-width-to-request? value) → boolean?
value : any/c
procedure
(fill-color-to target color) → fill-color-to-request?
target : (or/c symbol? (and/c visual? fill-color-visual?)) color : paint?
Compatible solid colours, linear gradients, radial gradients, and checker patterns interpolate through paint-lerp. At exact progress zero and one, the exact source and destination paint objects are installed. Different paint kinds (or gradients with unequal stop counts) are rejected during scene compilation; use an explicit cross-fade of two Visuals for that change.
Fill color owns the 'fill-color animation component. It may overlap same-target movement, rotation, scaling, opacity, stroke width, stroke color, and path geometry. Overlapping same-target fill-color leaves conflict after schedule expansion; touching leaves are legal.
procedure
(fill-color-to-request? value) → boolean?
value : any/c
procedure
(stroke-color-to target color) → stroke-color-to-request?
target : (or/c symbol? (and/c visual? stroke-color-visual?)) color : color-spec?
procedure
(stroke-color-to-request? value) → boolean?
value : any/c
procedure
(fade-to target opacity) → fade-to-request?
target : (or/c symbol? (and/c visual? opacity-visual?)) opacity : opacity?
When target is a Visual value, it must implement both gen:visual and gen:opacity-visual. When it is a symbol, scene-play checks the current Visual with that identity while compiling the request. A missing target or a Visual without valid semantic opacity raises an exception.
The request changes only the opacity component. It preserves identity, position, affine transform, geometry, style, drawing order, and group child order. It may run at the same time as movement, rotation, scaling, or path morphing for the same identity. It conflicts with another same-target request that changes opacity, including fade-in and fade-out.
fade-to does not add or remove the target. Its endpoint follows the easing result, like movement and morphing. An unusual easing procedure that returns zero at the end therefore leaves the starting opacity in place.
procedure
(fade-to-request? value) → boolean?
value : any/c
procedure
(fade-in visual) → fade-in-request?
visual : (and/c visual? opacity-visual?)
The Visual’s identity must be absent from the scene before the play clip. scene-play adds a placeholder with the same identity, position, geometry, affine transform, style, and drawing position, but with opacity zero. Several fade-in requests add their placeholders in request order, in front of Visuals already present. When the supplied Visual is a group, the complete child tree is introduced as one top-level Visual.
All requests in the play clip compile against the prepared shared start state. Movement, rotation, scaling, and path morphing can therefore target a Visual introduced by fade-in, even when those requests appear before the fade-in request. These operations change different components.
At interior samples, opacity is interpolated from zero to the opacity stored in visual. At the structural endpoint, the supplied opacity is installed regardless of the easing result. This guarantees that the complete Visual is present after the clip. An unusual easing procedure can therefore cause a jump at the exact clip boundary. A Visual whose supplied opacity is zero is introduced structurally but remains invisible.
A fade-in changes both opacity and scene presence. It conflicts with another same-target opacity request and with another same-target introduction or removal request. In particular, fade-in cannot be combined with same-target create, uncreate, or fade-out.
procedure
(fade-in-request? value) → boolean?
value : any/c
procedure
(fade-out target) → fade-out-request?
target : (or/c symbol? (and/c visual? opacity-visual?))
When target is a symbol, scene-play checks that the current Visual implements the opacity protocol and returns a valid opacity. A missing target or a Visual without semantic opacity raises an exception.
Movement, rotation, scaling, and path morphing may run at the same time for the same identity. The Visual remains present at interior samples, so those components continue to update while it fades. At the structural endpoint, the Visual is removed regardless of the easing result. A group and its complete child tree are removed as one top-level Visual. An unusual easing procedure can therefore leave it visibly opaque just before the boundary and absent at the boundary.
A fade-out changes both opacity and scene presence. It conflicts with another same-target opacity request and with another same-target introduction or removal request. In particular, it cannot be combined with same-target fade-in, create, uncreate, or fade-to.
procedure
(fade-out-request? value) → boolean?
value : any/c
procedure
(morph-to target destination) → morph-to-request?
target : (or/c path-visual? symbol?) destination : path-geometry?
The target must be present when scene-play compiles the request. When target is a symbol, the built-in path-Visual requirement is checked at that time. The target’s current path at the beginning of the clip must be compatible with destination according to path-geometry-morph-compatible?. Incompatible structure raises an exception before the clip is added.
At each sample, morph-to uses path-geometry-lerp with the clip’s eased progress. It interpolates subpath starts, line endpoints, cubic control points, and cubic endpoints. It preserves the target’s identity, affine transform, opacity, fill, stroke, stroke width, and drawing position. It does not copy style or placement from another Visual.
A morph can run at the same time as movement, rotation, scaling, or opacity fading for the same identity because those requests change different components. This includes morphing a Visual introduced by fade-in or removed by fade-out. A second strict, normalized, or aligned morph, create, or uncreate for the same identity conflicts because all of them change the path-geometry component.
Like translation, rotation, and scale, morphing follows the easing result at the clip endpoint. It has no special structural completion rule. An unusual easing function that does not map one to one can therefore leave the final path between the source and destination, or at the source.
procedure
(morph-to-request? value) → boolean?
value : any/c
procedure
(morph-to-normalized target destination)
→ morph-to-normalized-request? target : (or/c path-visual? symbol?) destination : path-geometry?
When scene-play compiles the request, it reads the target’s current path and normalizes that path together with destination. Compilation requires the pair to satisfy path-geometry-morph-normalizable?. A subpath-count, closure, or point-only/nonempty mismatch raises an exception before the clip is added.
At eased progress zero, the exact original source path is used. At eased progress one, the exact requested destination is used. At interior progress values, the normalized cubic source and destination are interpolated with path-geometry-lerp. This preserves the visible source and destination figures while allowing stored line-versus-cubic and segment-count differences supported by the normalizer.
The operation preserves Visual identity, reference position, rotation, scale, opacity, fill, stroke, stroke width, and drawing order. Movement, rotation, scaling, and opacity fading may run simultaneously for the same identity. A strict morph, another normalized or aligned morph, create, or uncreate conflicts because all of them change path geometry.
Like morph-to, this request follows the easing result and has no structural endpoint override. An easing function that returns zero at the clip endpoint therefore leaves the exact source path. An easing function that returns one produces the exact requested destination.
This request does not reverse a path, rotate a closed path’s starting point, reorder subpaths, add or remove subpaths, change closure, or invent drawn segments for a point-only subpath.
procedure
(morph-to-normalized-request? value) → boolean?
value : any/c
procedure
(morph-to-aligned target destination [ #:allow-reverse? allow-reverse? #:sample-count sample-count]) → morph-to-aligned-request? target : (or/c path-visual? symbol?) destination : path-geometry? allow-reverse? : boolean? = #t sample-count : (and/c exact-integer? (>=/c 8)) = 64
The target must be a present built-in path Visual when scene-play compiles the request. Both source and destination must each contain exactly one positive finite closed subpath. allow-reverse? and sample-count have the same meaning as for path-geometry-align-for-morph.
At interior eased progress values, interpolation uses the automatically aligned and normalized destination. At eased progress zero, the exact original source path is used. At eased progress one, the exact requested destination object is used, not the phase-shifted or reversed internal working representation. The endpoint therefore preserves caller-requested semantic storage while the visible closed loop has the correspondence selected for the interior morph.
The request changes the path-geometry component. It may run simultaneously with movement, rotation, scaling, or opacity changes, and conflicts with strict, normalized, or another aligned morph plus create and uncreate on the same target. Like the other morph requests, it follows the easing result and has no structural endpoint override.
procedure
(morph-to-aligned-request? value) → boolean?
value : any/c
procedure
(morph-to-open-aligned target destination [ #:allow-reverse? allow-reverse? #:sample-count sample-count]) → morph-to-open-aligned-request? target : (or/c path-visual? symbol?) destination : path-geometry? allow-reverse? : boolean? = #t sample-count : (and/c exact-integer? (>=/c 8)) = 64
The target must be a present built-in path Visual when scene-play compiles the request. Source and destination must each contain exactly one positive finite open subpath. allow-reverse? and sample-count have the same meanings as for path-geometry-align-open-for-morph.
Interior eased progress uses the selected endpoint direction and normalized geometry. Eased progress zero uses the exact clip-start source path. Eased progress one installs the exact caller-requested destination object, including its original stored direction. Direction selection is therefore an interior correspondence choice rather than an endpoint rewrite.
The request changes the ordinary path-geometry component. It may compose with movement, rotation, scaling, or opacity animation, and conflicts with strict, normalized, closed-loop aligned, compound-aligned, another open-aligned morph, create, or uncreate on the same target.
procedure
(morph-to-open-aligned-request? value) → boolean?
value : any/c
procedure
(morph-to-open-compound-aligned target destination [ #:allow-reverse? allow-reverse? #:sample-count sample-count]) → morph-to-open-compound-aligned-request? target : (or/c path-visual? symbol?) destination : path-geometry? allow-reverse? : boolean? = #t sample-count : (and/c exact-integer? (>=/c 8)) = 64
The target must be a present built-in path Visual when scene-play compiles the request. Source and destination must contain the same nonzero number of positive finite open subpaths. allow-reverse? and sample-count have the same meanings as for path-geometry-align-open-compound-for-morph.
Interior eased progress uses global subpath pairing, per-pair endpoint direction, and normalized geometry. Eased progress zero uses the exact clip-start source path. Eased progress one installs the exact caller-requested destination object, including its original subpath order and stored traversal directions. Pairing and reversal are therefore interior correspondence choices rather than endpoint rewrites.
The request changes the ordinary path-geometry component. It may compose with movement, rotation, scaling, or opacity animation, and conflicts with strict, normalized, one-loop aligned, closed-compound aligned, another open-compound aligned morph, create, or uncreate on the same target.
procedure
value : any/c
procedure
(morph-to-mixed-compound-aligned target destination [ #:allow-reverse? allow-reverse? #:sample-count sample-count]) → morph-to-mixed-compound-aligned-request? target : (or/c path-visual? symbol?) destination : path-geometry? allow-reverse? : boolean? = #t sample-count : (and/c exact-integer? (>=/c 8)) = 64
The target must be a present built-in path Visual when scene-play compiles the request. Source and destination must be nonempty positive-finite compound paths with matching counts of open subpaths and matching counts of closed subpaths. allow-reverse? and sample-count have the same meaning as for path-geometry-align-mixed-compound-for-morph.
Interior eased progress uses topology-class global pairing, per-open endpoint direction, per-closed phase/direction, and normalized geometry. Eased progress zero uses the exact clip-start source. Eased progress one installs the exact caller-requested destination object, including its original interleaving, subpath order, and stored traversal representations.
The request changes the ordinary path-geometry component. It may compose with movement, rotation, scaling, or opacity animation, and conflicts with strict, normalized, one-loop aligned, open-compound aligned, closed-compound aligned, another mixed-compound aligned morph, create, or uncreate on the same target.
procedure
value : any/c
procedure
(morph-to-topology-changing target destination [ #:allow-reverse? allow-reverse? #:sample-count sample-count #:birth-anchor birth-anchor #:death-anchor death-anchor #:birth-anchor-map birth-anchor-map #:death-anchor-map death-anchor-map #:birth-penalty birth-penalty #:death-penalty death-penalty #:birth-penalty-map birth-penalty-map #:death-penalty-map death-penalty-map #:match-penalty-map match-penalty-map]) → morph-to-topology-changing-request? target : (or/c path-visual? symbol?) destination : path-geometry? allow-reverse? : boolean? = #t sample-count : (and/c exact-integer? (>=/c 8)) = 64 birth-anchor : (or/c symbol? vec2?) = 'bounds-center death-anchor : (or/c symbol? vec2?) = 'bounds-center birth-anchor-map : hash? = #hash() death-anchor-map : hash? = #hash()
birth-penalty : (or/c symbol? (and/c finite-real? (>=/c 0))) = 'forced
death-penalty : (or/c symbol? (and/c finite-real? (>=/c 0))) = 'forced birth-penalty-map : hash? = #hash() death-penalty-map : hash? = #hash() match-penalty-map : hash? = #hash()
Matched real open/closed subpaths use the same correspondence rules as SCENE-AG. Unmatched destination subpaths grow from deterministic degenerate bounds-center seeds by default, while unmatched source subpaths collapse to their own bounds-center seeds. birth-anchor and death-anchor may instead be explicit finite local vec2 values shared by all unmatched subpaths on the corresponding side. SCENE-AK’s birth-anchor-map and death-anchor-map may sparsely override those shared values by original destination/source subpath index. Missing keys inherit the shared anchor and an explicit 'bounds-center entry opts that subpath back into its own center. The request snapshots both hashes immutably; index range is checked against the actual clip-start source and caller destination when the request is compiled. By default birth-penalty and death-penalty are both 'forced, preserving SCENE-AH/AI matching. Supplying both as finite nonnegative real costs enables SCENE-AJ voluntary death+birth replacement when that lowers the global correspondence cost; exact cost ties prefer fewer topology changes. SCENE-AL’s birth-penalty-map and death-penalty-map may sparsely override those shared numeric costs by original destination/source subpath index. Missing keys inherit the shared cost. The request snapshots both endpoint penalty maps immutably, and nonempty endpoint maps require numeric shared penalty mode. SCENE-AM’s match-penalty-map may be used in either forced or numeric mode. Its keys are (cons source-index destination-index) pairs in original caller storage order and its finite nonnegative values add to real-edge geometric scores only. The request snapshots this map immutably as well. Pair-key range and topology validation occurs when the request is compiled against the clip-start source and stored destination. Empty source or destination geometry is legal; any real subpath that is present must have positive finite arc length.
Interior eased progress uses the prepared/normalized geometry. Eased progress zero uses the exact clip-start source path, with no synthetic birth slots present. Eased progress one installs the exact caller-requested destination, with no synthetic death slots present. Birth/death seeds, reordering, reversal, and closed-loop phase are therefore interior correspondence only.
The request changes the ordinary path-geometry component. It may compose with movement, rotation, scaling, or opacity animation and conflicts with strict, normalized, aligned, compound-aligned, another topology-changing morph, create, or uncreate on the same target.
procedure
(morph-to-topology-changing-request? value) → boolean?
value : any/c
procedure
(morph-to-compound-aligned target destination [ #:allow-reverse? allow-reverse? #:sample-count sample-count]) → morph-to-compound-aligned-request? target : (or/c path-visual? symbol?) destination : path-geometry? allow-reverse? : boolean? = #t sample-count : (and/c exact-integer? (>=/c 8)) = 64
The target must be a present built-in path Visual when scene-play compiles the request. Source and destination must have the same nonzero number of positive finite closed subpaths. allow-reverse? and sample-count have the same meaning as for path-geometry-align-compound-for-morph.
Interior eased progress uses globally paired, phase/direction-aligned, normalized geometry. Eased progress zero uses the exact clip-start source path. Eased progress one uses the exact caller-requested destination object, including its original subpath order and storage representation. Pairing is therefore an interior correspondence choice rather than an endpoint rewrite.
The request changes the ordinary path-geometry component. It may compose with movement, rotation, scaling, or opacity animation, and conflicts with strict, normalized, one-loop aligned, another compound-aligned morph, create, or uncreate on the same target.
procedure
(morph-to-compound-aligned-request? value) → boolean?
value : any/c
procedure
(transform-shape source destination [ #:mode mode #:correspondence correspondence #:allow-reverse? allow-reverse? #:sample-count sample-count]) → transform-shape-request? source : (or/c visual? symbol?) destination : (and/c visual? affine-visual? opacity-visual?) mode : (or/c 'auto 'morph 'cross-fade) = 'auto correspondence : (or/c 'auto 'perimeter 'path) = 'auto allow-reverse? : boolean? = #t sample-count : (and/c exact-integer? (>=/c 8)) = 64
The default 'auto first tries a geometric transition when each endpoint is one built-in path-visual?, circle-visual?, or rectangle-visual?. Circle/rectangle pairs use a canonical eight-segment perimeter: both start at their right midpoint and correspond at the cardinal and diagonal positions. This produces an evenly rounded square-to-circle interior. Pass 'perimeter to require that primitive correspondence, or 'path to use only the general stored-path policy. Other geometric pairs use automatic topology-class pairing, including closed-loop phase/direction and open-path direction; when counts differ, they try deterministic birth/death preparation. The source and destination styles are alpha layers over the same intermediate outline, so a fill/stroke change fades naturally while the geometry moves. Their transforms are interpolated too.
If either endpoint is a group, image, text, formula, SVG tree, custom Visual, or atomic geometry that cannot be prepared safely, 'auto keeps the exact endpoint trees and cross-fades them at their own positions. This is a deliberate graceful fallback: it does not flatten a group or manufacture a semantic mapping between its children. 'morph requires the geometric case and raises an exception otherwise. 'cross-fade always selects the fallback and ignores the correspondence controls.
At exact start, the source is unchanged. At interior samples it is hidden and a temporary frontmost layer is drawn. At structural completion, regardless of the easing result, the source is removed and the exact caller-supplied destination Visual is installed. Temporary path conversions and normalized outlines are therefore never retained in later clips.
The operation reserves all ordinary Visual components and presence for both source and destination identities. It cannot be combined in one play clip with another animation of either endpoint.
procedure
(transform-shape-request? value) → boolean?
value : any/c
struct
(struct visual-match (source-path destination-path) #:transparent) source-path : (listof symbol?) destination-path : (listof symbol?)
procedure
(transform-matching-visuals source destination [ #:matches matches #:mode mode #:mismatch-mode mismatch-mode #:allow-reverse? allow-reverse? #:sample-count sample-count]) → transform-matching-visuals-request? source : visual? destination : (and/c visual? affine-visual? opacity-visual?) matches : (listof visual-match?) = '() mode : (or/c 'auto 'morph 'cross-fade) = 'auto mismatch-mode : (or/c 'fade 'fade-transform) = 'fade allow-reverse? : boolean? = #t sample-count : (and/c exact-integer? (>=/c 8)) = 64
For a matched path, circle, or rectangle that can use the existing topology-aware outline preparation, 'auto shares an intermediate geometric morph. Other matched affine/opacity leaves move through an interpolated affine transform while their source/destination content cross-fades. Unmatched leaves fade in place; 'fade-transform pairs the remaining leaves by nearest position to give them the same moving cross-fade. Pass 'morph to require every matched pair to have geometric correspondence, or 'cross-fade to disable all geometric morphs.
This first general matcher is intentionally conservative. It does not infer semantic matches for arbitrary SVG/text/custom leaves after renaming, preserve one leaf through a split or merge, resolve occlusion/collisions, or replace the formula APIs’ TeX/glyph correspondence. At exact start the source is unchanged; interior samples use a temporary frontmost overlay; at the clip boundary the exact destination root is installed.
procedure
(transform-matching-visuals-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
(create visual) → create-request?
visual : path-visual?
The Visual’s identity must be absent from the scene before the play clip. Its computed local path length must be finite. Line portions use Euclidean length, and cubic portions use the deterministic approximation documented by path-subpath-length. scene-play prepares an empty-path placeholder with the same identity, style, affine transform, and opacity at clip start. The complete supplied path is stored at the structural endpoint.
A create request can run with movement, rotation, scaling, and fade-to requests for the same identity because those requests change different animation components. It conflicts with morph-to, morph-to-normalized, morph-to-aligned, morph-to-open-aligned, morph-to-open-compound-aligned, morph-to-compound-aligned, and uncreate because they change path geometry. It also conflicts with fade-in and fade-out because all three operations change scene presence. The complete Visual carried by create supplies the shared start position, rotation, scale, opacity, style, and path.
Several creation requests in one play clip introduce their Visuals in request order, in front of Visuals already in the scene.
A zero-length path has no positive interior prefix. It therefore remains empty at interior samples and is restored to its complete semantic structure at the structural endpoint. This matters for point-only and explicitly empty paths.
procedure
(create-request? value) → boolean?
value : any/c
procedure
(uncreate target) → uncreate-request?
target : (or/c path-visual? symbol?)
When target is a symbol, its path-Visual requirement is checked when the request is compiled by scene-play. A missing target or a target that is not a built-in path Visual raises an exception. The current path must also have a finite computed local length. Cubic portions use the same deterministic approximate length and partial extraction as create.
Movement, rotation, scaling, and fade-to may run at the same time for the same target. A second uncreate, morph-to, morph-to-normalized, morph-to-aligned, morph-to-open-aligned, morph-to-open-compound-aligned, morph-to-compound-aligned, or same-target create conflicts because all of them change the path-geometry component. Same-target fade-in or fade-out also conflicts because those requests change scene presence.
A zero-length path has no positive interior prefix, so it is empty at interior samples and is removed at the structural endpoint.
procedure
(uncreate-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
16.3.2 Serializable Rate Functions
SCENE-DL represents built-in easings as transparent callable values. A rate-function? can therefore be supplied anywhere the historical API accepts a one-argument procedure, 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
(rate-function? value) → boolean?
value : any/c
procedure
(rate-function-name value) → symbol?
value : rate-function?
procedure
(rate-function-parameters value) → list?
value : rate-function?
procedure
(rate-function->datum value) → pair?
value : rate-function?
procedure
(rate-function-apply value progress) → finite-real?
value : rate-function? progress : finite-real?
value
procedure
(smooth [#:inflection inflection]) → rate-function?
inflection : positive-real? = 10
procedure
procedure
procedure
procedure
procedure
(there-and-back-with-pause [#:pause-ratio pause-ratio])
→ rate-function? pause-ratio : (and/c finite-real? (>=/c 0) (</c 1)) = 1/3
procedure
(cubic-bezier [ #:x1 x1 #:y1 y1 #:x2 x2 #:y2 y2]) → rate-function? x1 : (and/c finite-real? (>=/c 0) (<=/c 1)) = 1/4 y1 : finite-real? = 1/10 x2 : (and/c finite-real? (>=/c 0) (<=/c 1)) = 1/4 y2 : finite-real? = 1
procedure
(spring [ #:frequency frequency #:damping damping]) → rate-function? frequency : positive-real? = 3 damping : nonnegative-real? = 6
procedure
(reverse-rate function) → rate-function?
function : rate-function?
procedure
(compose-rate first rest ...) → rate-function?
first : rate-function? rest : rate-function?
procedure
(squish-rate function [#:from from #:to to]) → rate-function?
function : rate-function? from : (and/c finite-real? (>=/c 0) (<=/c 1)) = 0 to : (and/c finite-real? (>=/c 0) (<=/c 1)) = 1
procedure
(change-speed keyframes) → rate-function?
keyframes : (listof (list/c finite-real? positive-real?))
Inside succession, animation-group, or lagged-start, the same values are intrinsic timing units. The direct child contributes (+ start duration) units to its parent schedule; the parent then scales that span proportionally into its assigned interval. The scaled start portion is a delay during which the wrapped content has no effect.
When easing is #f, the timed request inherits its enclosing timing context. A supplied procedure overrides that inherited easing. For a leaf it applies only to that leaf; for a timed composition it becomes the inherited easing of all descendant leaves unless a nested timed child supplies another easing. Easing changes interpolation, not schedule allocation.
Before the concrete local start, the wrapped content has no effect. During its active interval it is sampled using local normalized progress; after its active endpoint, its exact semantic endpoint is held. SCENE-CV permits timed wrappers inside Visual/scalar and camera compositions and permits a composition itself to be wrapped. Another timed wrapper is not a valid request value. A timed camera-follow samples its target only while its own interval is active, then holds the resulting endpoint view.
procedure
(timed-animation-request? value) → boolean?
value : any/c
procedure
(succession request ...) → succession-animation-request?
request :
(or/c timed-animation-request? succession-animation-request? animation-group-animation-request? lagged-start-animation-request? style-to-animation-request? value-to-request? move-to-request? move-along-path-request? orient-along-path-request? rotate-to-request? rotate-by-request? scale-to-request? scale-by-request? stroke-width-to-request? fill-color-to-request? stroke-color-to-request? fade-to-request? fade-in-request? fade-out-request? morph-to-request? morph-to-normalized-request? morph-to-aligned-request? morph-to-open-aligned-request? morph-to-open-compound-aligned-request? morph-to-mixed-compound-aligned-request? morph-to-topology-changing-request? morph-to-compound-aligned-request? transform-formula-parts-request? create-request? uncreate-request? camera-pan-to-request? camera-pan-by-request? camera-zoom-to-request? camera-zoom-by-request? camera-follow-request? camera-fit-request?)
An unwrapped direct child contributes one intrinsic timing unit. A direct timed child contributes (+ start duration) units. The direct child spans are placed consecutively in argument order and their total is scaled to the succession’s concrete assigned duration. Thus unwrapped children still receive equal shares exactly as in SCENE-AO, while explicit timed durations act as proportional sequence weights. A timed child’s start portion is a scaled hold delay before its active content.
A bare nested composition still counts as one direct child, preserving AO–AQ parent allocation. Wrap that nested composition with timed when it should reserve a non-unit or delayed parent-level span. Once assigned an interval, the nested composition recursively applies its own timing rule.
Every leaf is compiled against the exact semantic state at its own start boundary. Relative requests therefore chain from prior endpoints, and SCENE-AN’s structural introduction, removal, same-ID reintroduction, overlap checks, and direct arbitrary-time sampling are reused. Easing is inherited independently by each leaf; a nested timed child may override it.
At least one child is required. A single list of valid children is accepted in place of separate arguments. Ordinary Visual and camera requests, unified style transitions, timed Visual/camera composition wrappers, and nested successions, animation groups, or lagged starts are valid children. Camera center and world-width overlap rules apply after expansion.
procedure
(succession-animation-request? value) → boolean?
value : any/c
procedure
(animation-group request ...)
→ animation-group-animation-request?
request :
(or/c timed-animation-request? succession-animation-request? animation-group-animation-request? lagged-start-animation-request? style-to-animation-request? value-to-request? move-to-request? move-along-path-request? orient-along-path-request? rotate-to-request? rotate-by-request? scale-to-request? scale-by-request? stroke-width-to-request? fill-color-to-request? stroke-color-to-request? fade-to-request? fade-in-request? fade-out-request? morph-to-request? morph-to-normalized-request? morph-to-aligned-request? morph-to-open-aligned-request? morph-to-open-compound-aligned-request? morph-to-mixed-compound-aligned-request? morph-to-topology-changing-request? morph-to-compound-aligned-request? transform-formula-parts-request? create-request? uncreate-request? camera-pan-to-request? camera-pan-by-request? camera-zoom-to-request? camera-zoom-by-request? camera-follow-request? camera-fit-request?)
Every unwrapped direct child has one intrinsic timing unit. A direct timed child has span (+ start duration). All children share the group start; their spans are scaled against the longest direct span so the longest child reaches the group endpoint. Shorter children finish earlier and hold their exact endpoints. If no direct child is timed, every span is one and SCENE-AP’s original full-interval timing is unchanged.
A timed child’s scaled start portion is a delay. A bare nested succession, group, or lagged start still contributes one parent-level unit and recursively expands inside the concrete interval it receives; wrap that nested composition with timed to give it an explicit non-unit span.
Existing component rules still apply after complete expansion: compatible components such as translation and rotation may share one target, while two positive-overlap updates to the same target/component are rejected. The final leaves use the SCENE-AN scheduler, preserving exact-boundary compilation, structural semantics, easing inheritance, camera-follow behavior, and direct arbitrary-time sampling.
At least one child is required. A single list of valid children is accepted in place of separate arguments. Unified style transitions, camera requests, timed Visual/camera composition wrappers, and nested compositions are valid group children. Camera center and world-width overlap rules apply after expansion.
procedure
(animation-group-animation-request? value) → boolean?
value : any/c
procedure
(lagged-start request ... [ #:lag-ratio lag-ratio]) → lagged-start-animation-request?
request :
(or/c timed-animation-request? succession-animation-request? animation-group-animation-request? lagged-start-animation-request? style-to-animation-request? value-to-request? move-to-request? move-along-path-request? orient-along-path-request? rotate-to-request? rotate-by-request? scale-to-request? scale-by-request? stroke-width-to-request? fill-color-to-request? stroke-color-to-request? fade-to-request? fade-in-request? fade-out-request? morph-to-request? morph-to-normalized-request? morph-to-aligned-request? morph-to-open-aligned-request? morph-to-open-compound-aligned-request? morph-to-mixed-compound-aligned-request? morph-to-topology-changing-request? morph-to-compound-aligned-request? transform-formula-parts-request? create-request? uncreate-request? camera-pan-to-request? camera-pan-by-request? camera-zoom-to-request? camera-zoom-by-request? camera-follow-request? camera-fit-request?) lag-ratio : (and/c finite-real? (>=/c 0)) = 1/4
When every direct span is one, this reduces exactly to the SCENE-AQ formula (/ D (+ 1 (* (sub1 n) r))). More generally, #:lag-ratio 0 has duration-scaled animation-group timing and #:lag-ratio 1 has duration-scaled succession timing even when child spans differ. Intermediate ratios overlap according to the previous child’s span; ratios greater than one may leave hold gaps.
A timed child’s start portion becomes scaled delay before its active content. A bare nested composition contributes one parent-level unit and recursively applies its own rule in the interval it receives; wrap it with timed for an explicit parent-level duration or delay. All expanded leaves use the SCENE-AN scheduled-leaf engine, so exact boundary compilation, conflict validation, structural ordering, easing inheritance, and arbitrary-time sampling remain unchanged.
At least one child is required. A single list of valid children is accepted in place of separate arguments. Unified style transitions, camera requests, timed Visual/camera composition wrappers, and nested compositions are valid lagged children. Camera center and world-width overlap rules apply after expansion.
procedure
(lagged-start-animation-request? value) → boolean?
value : any/c
procedure
(style-to target [ #:fill fill #:stroke stroke #:stroke-width stroke-width #:opacity opacity]) → style-to-animation-request? target : (or/c symbol? visual?) fill : (or/c false/c paint?) = #f stroke : (or/c false/c color-spec?) = #f stroke-width : (or/c false/c stroke-width?) = #f opacity : (or/c false/c opacity?) = #f
This operation is composition syntax rather than a new interpolation primitive. It expands to fill-color-to, stroke-color-to, stroke-width-to, and fade-to leaves for exactly the properties that were supplied. Those leaves share one assigned interval and preserve their existing semantic protocol validation, exact endpoints, easing, and renderer behavior.
Because expansion occurs before scheduler conflict checking, the properties do not collapse into one coarse 'style component. For example, a fill-only style-to may overlap a same-target stroke-width-to or fade-to, while an overlapping fill-color-to conflicts normally on 'fill-color.
When target is a direct Visual, each supplied property’s primitive constructor validates the required optional Visual protocol immediately. A symbolic target defers those same checks until scene-play resolves the target. A #f keyword value means omitted; it is not a request to remove paint. The SCENE-AT rule that a current #f fill or stroke cannot be color-interpolated therefore remains unchanged.
style-to counts as one direct child for parent composition timing and then expands its primitive leaves in parallel inside the interval it receives. It may be used directly by scene-play, wrapped with timed, or nested inside succession, animation-group, and lagged-start.
procedure
(style-to-animation-request? value) → boolean?
value : any/c
16.4 Scene Timelines
procedure
(make-scene [initial-state #:camera camera]) → scene?
initial-state : scene-state? = empty-scene-state camera : camera? = default-camera
Visuals are added in argument order. Each later argument is placed in front of earlier Visuals. Adding a top-level identity already present as a Visual or named scalar in the current state raises an exception. A group is added as one top-level Visual; its child order remains internal to the group. A derived-visual? is likewise stored as one top-level identity and is resolved only when concrete geometry is requested.
An instantaneous addition at the exact end of a scene is not normally included in frame sampling. Follow it with scene-wait or scene-play when it must be visible in rendered output. Supplying no Visuals returns an equivalent scene.
procedure
(scene-remove scene target ...) → scene?
scene : scene? target : (or/c visual? symbol? visual-path?)
procedure
scene : scene? target : (or/c visual? symbol? visual-path?)
procedure
(scene-visual-at scene target time) → visual?
scene : scene? target : (or/c visual? symbol? visual-path?) time : (and/c finite-real? (>=/c 0))
procedure
(scene-set-value scene id value) → scene?
scene : scene? id : (or/c symbol? scene-parameter?) value : any/c
The two-argument shorthand (scene-set-value scene parameter) accepts a scene-parameter? and installs its declared initial value.
procedure
(scene-remove-value scene id) → scene?
scene : scene? id : (or/c symbol? scene-parameter?)
procedure
(scene-current-value scene id) → any/c
scene : scene? id : (or/c symbol? scene-parameter?)
procedure
(scene-value-at scene id time) → any/c
scene : scene? id : (or/c symbol? scene-parameter?) time : (and/c finite-real? (>=/c 0))
procedure
(scene-set-camera scene camera) → scene?
scene : scene? camera : camera?
An instantaneous replacement at the exact end of a scene is not normally included in frame sampling. Follow it with scene-wait or scene-play when the replacement must appear in rendered output.
procedure
(scene-play scene request ... [ #:duration duration #:easing easing]) → scene? scene : scene?
request :
(or/c timed-animation-request? succession-animation-request? animation-group-animation-request? lagged-start-animation-request? style-to-animation-request? value-to-request? move-to-request? move-along-path-request? orient-along-path-request? rotate-to-request? rotate-by-request? scale-to-request? scale-by-request? stroke-width-to-request? fill-color-to-request? stroke-color-to-request? fade-to-request? fade-in-request? fade-out-request? morph-to-request? morph-to-normalized-request? morph-to-aligned-request? morph-to-open-aligned-request? morph-to-open-compound-aligned-request? morph-to-mixed-compound-aligned-request? morph-to-topology-changing-request? morph-to-compound-aligned-request? transform-formula-parts-request? create-request? uncreate-request? write-in-request? unwrite-request? camera-pan-to-request? camera-pan-by-request? camera-zoom-to-request? camera-zoom-by-request? camera-follow-request? camera-fit-request?) duration : (or/c false/c (and/c finite-real? positive?)) = #f easing : (procedure-arity-includes/c 1) = linear
When #:duration is omitted, ordinary requests retain the historical one-second default. A direct write-in uses Manim’s default instead: one second for fewer than fifteen writable leaves and two seconds for fifteen or more. An explicit positive duration always takes precedence.
When at least one timing/composition value is present, every Visual, scalar, or camera request resolves to one or more concrete local intervals. A top-level timed value uses literal second-based start and duration and may wrap either one leaf or one composition. An ordinary unwrapped top-level Visual or camera request spans the complete enclosing clip.
Inside compositions, unwrapped direct children contribute one timing unit and a timed direct child contributes (+ start duration) units. A succession places those spans consecutively, an animation group starts them together and scales against the longest span, and a lagged start offsets raw starts by its lag ratio before scaling the complete envelope. Bare nested compositions remain one-unit direct children unless explicitly wrapped by timed. All three composition forms may nest arbitrarily with timed Visual/camera composition children.
Equal-start Visual leaves are compiled together against one prepared local start state. A later start batch is compiled against the exact semantic state sampled at that local boundary, so a touching relative request starts from the previous request’s exact endpoint rather than the enclosing clip start. Sampling remains direct and does not depend on rendering prior frames.
Structural introduction requests are installed only at their local start. A create request adds an empty-path placeholder and fade-in adds the complete Visual at opacity zero. Requests beginning at that same local time share the prepared state, so movement, rotation, scaling, opacity, and compatible geometry changes can still compose with an introduction exactly as in the historical simultaneous model. A fade-out or uncreate removal may not end while another animation of that target remains active; reintroduction at the exact removal boundary is allowed.
Positive-measure overlap on the same Visual component is rejected. Touching intervals are not overlap and are legal. Requests for disjoint components may overlap freely.
SCENE-CV gives camera requests those same local intervals. A later pan, zoom, fit, or follow compiles from the exact camera view at its local start. camera-pan-by therefore adds to that local center, and camera-zoom-by divides that local visible width by its magnification factor. A camera-follow captures its target’s frame offset at the start of its own interval, samples the target’s actual local Visual state while active, and holds the resulting endpoint view after it ends. A camera-fit request remains a concrete center/width snapshot measured when it was constructed. Camera and Visual requests may appear in any order.
A single list of requests can be supplied in place of separate request arguments:
(scene-play scene (list (move-to 'a (vec2 2 0)) (rotate-by 'a 1) (camera-pan-to (vec2 1 0)) (camera-zoom-by 2)) #:duration 2)
At least one request is required. Each composition value itself also requires at least one child. In the historical full-clip case, two simultaneous requests may target the same Visual when every component they change is disjoint. With local timing, the same rule applies only where the request intervals overlap. The components in this version are translation, rotation, scale, opacity, path geometry, formula parts, and scene presence. Both move-to and move-along-path reserve translation. orient-along-path, rotate-to, and rotate-by reserve rotation. A request can change more than one component: fade-in and fade-out change opacity and presence, create and uncreate change path geometry and presence, and transform-formula-parts changes formula parts and reserves presence.
Two overlapping requests may not change the same component for the same identity. Exact endpoint touching is allowed. Thus overlapping opacity requests conflict. Strict morphing, normalized morphing, creation, and uncreation conflict on path geometry. Two formula-part transformations conflict on formula parts. A formula-part transformation also conflicts with a same-target structural introduction or removal because both reserve presence. Requests for different identities do not conflict.
The camera center and visible world width are separate camera components. Pan and follow requests change the center. Zoom requests change visible width. A fit request changes both. One follow request and one zoom request may run together. Overlapping requests that reserve the same camera component raise an exception, so fit conflicts with pan, follow, zoom, or another fit for the same local interval. Touching camera intervals are legal and hand off exactly. Camera components do not conflict with Visual components.
Before easing is called, progress is clamped to the closed unit interval. The easing result must be a finite real and is also clamped to that interval. A normal easing procedure should map 0 to 0 and 1 to 1. Transform component endpoints follow the easing result, as in earlier versions.
Introduction, removal, and formula-part transformation requests have structural endpoint rules. For an untimed request the structural endpoint is the play-clip boundary; for a timed request it is the local interval endpoint. At that endpoint, a completed create contains the complete original path, a completed fade-in contains the supplied final opacity, and a completed transform-formula-parts contains the exact destination part list. A completed uncreate or fade-out removes its target. These rules apply even when easing does not map one to one. Such an easing procedure can therefore cause a discontinuity at the request endpoint.
A fade-to, morph-to, morph-to-normalized, morph-to-aligned, morph-to-open-aligned, morph-to-open-compound-aligned, or morph-to-compound-aligned request has no structural endpoint override. Like movement, rotation, scale, camera pan, camera zoom, camera fit, and camera follow, its final sampled value follows the easing result. A normalized morph preserves the exact source at eased progress zero and the exact requested destination at eased progress one. Camera animation preserves pixel dimensions and background throughout the clip.
procedure
(scene-wait scene duration) → scene?
scene : scene? duration : (and/c finite-real? positive?)
procedure
(scene-sample scene time) → scene-state?
scene : scene? time : (and/c finite-real? (>=/c 0))
Clip intervals are half-open. Sampling the exact total duration returns scene-current-state. This procedure samples only the Visual state; it does not apply camera animations. Camera values are sampled separately with scene-camera-at.
procedure
(scene-camera-at scene time) → camera?
scene : scene? time : (and/c finite-real? (>=/c 0))
procedure
(scene-duration scene) → (and/c finite-real? (>=/c 0))
scene : scene?
procedure
(scene-current-state scene) → scene-state?
scene : scene?
procedure
(scene-current-camera scene) → camera?
scene : scene?
procedure
(scene-clip-count scene) → exact-nonnegative-integer?
scene : scene?