On this page:
vec2
finite-real?
finite-complex?
origin
real-lerp
interpolable?
interpolate-value
17.1 Scene Parameters
parameter
scene-parameter?
parameter-id
parameter-initial-value
vec2+
vec2-
vec2-scale
vec2*
vec2-lerp
17.2 Path Geometry
line-path-segment
cubic-bezier-path-segment
path-segment?
path-subpath
path-geometry
empty-path-geometry
path-geometry-empty?
path-subpath-points
path-geometry-subpath-points
path-geometry-map-points
path-geometry-translate
path-geometry-reverse
path-geometry->cubic
path-geometry-morph-normalizable?
path-geometry-align-for-morph
path-geometry-align-open-for-morph
path-geometry-align-open-compound-for-morph
path-geometry-align-mixed-compound-for-morph
path-geometry-prepare-topology-changing-morph
path-geometry-align-compound-for-morph
path-geometry-normalize-for-morph
path-geometry-morph-compatible?
path-geometry-lerp
path-geometry-bounds
path-geometry-center
path-subpath-length
path-geometry-length
path-geometry-point-at
path-geometry-tangent-at
path-geometry-normal-at
path-geometry-offset
path-geometry-partial
path-geometry-cycle-start
polyline-path
polygon-path
cubic-bezier-path
17.2.1 General Boolean Path Geometry and Clipping
path-union
path-intersection
path-difference
path-xor
cutout
clip-to
mask-with
clip-visual
clipped-visual?
17.3 Affine Transforms
affine-transform?
affine-transform-translation
affine-transform-rotation
affine-transform-scale
scale-factor?
scale-factor->vec2
make-affine-transform
identity-affine-transform
affine-transform-with-translation
affine-transform-with-rotation
affine-transform-with-scale
affine-transform-lerp
affine-transform-apply-vector
affine-transform-apply-point
17.3.1 General Affine Maps
linear2?
linear2
make-linear2
identity-linear2
linear2-a
linear2-b
linear2-c
linear2-d
linear2-determinant
linear2-invert
linear2-compose
linear2-apply-vector
affine2?
affine2
make-affine2
identity-affine2
affine2-linear
affine2-translation
affine2-a
affine2-b
affine2-h
affine2-c
affine2-d
affine2-k
affine2-with-linear
affine2-with-translation
affine2-invert
affine2-compose
affine2-lerp
affine2-apply-vector
affine2-apply-point
affine-transform->affine2
17.4 Cameras
camera?
camera-width
camera-height
camera-world-width
camera-center
camera-background
make-camera
default-camera
camera-scale
camera-world-height
camera-length->pixels
camera-world->pixel
camera-pan-to
camera-pan-to-request?
camera-pan-by
camera-pan-by-request?
camera-zoom-to
camera-zoom-to-request?
camera-zoom-by
camera-zoom-by-request?
camera-follow
camera-follow-request?
camera-fit-request?
camera-fit-request-center
camera-fit-request-world-width
camera-fit-layout-box
camera-fit-visuals
camera-fit-scene
camera-focus
17.5 Frame-Space Overlays and Callouts
frame-space-visual?
frame-space-visual-frame-width
frame-space-camera
fixed-in-frame
fixed-in-frame-visual?
fixed-in-frame-visual-content
camera-view
camera-view-visual?
camera-view-visual-target
camera-view-visual-targets
camera-view-visual-camera
camera-view-visual-with-camera
camera-view-visual-width
camera-view-visual-clip
camera-view-pan-to
camera-view-pan-to-request?
camera-view-pan-by
camera-view-pan-by-request?
camera-view-zoom-to
camera-view-zoom-to-request?
camera-view-zoom-by
camera-view-zoom-by-request?
camera-view-follow
camera-view-follow-request?
camera-view-fit
camera-view-fit-request?
callout
callout-visual?
callout-visual-content
callout-visual-target
callout-visual-target-anchor
callout-visual-connector-stroke
callout-visual-connector-width
17.6 Semantic Colors and Paints
rgba-color
rgb-color
color-spec?
color-spec->rgba-color
rgba-color-lerp
17.6.1 Semantic fill paints
paint-stop
linear-gradient-paint
linear-gradient
radial-gradient-paint
radial-gradient
checker-pattern-paint
checker-pattern
paint?
paint-lerp
9.3

17 Geometry🔗

struct

(struct vec2 (x y)
    #:transparent)
  x : finite-real?
  y : finite-real?
Represents either a point or a displacement in two-dimensional world coordinates.

The constructor checks both fields. Infinite values and NaN values are rejected. The structure is immutable and transparent. The public bindings created for this structure include vec2, vec2?, vec2-x, and vec2-y.

procedure

(finite-real? value)  boolean?

  value : any/c
Returns #t when value is a finite real number. Exact and inexact finite real numbers are accepted. Complex numbers, infinities, and NaN values are rejected.

procedure

(finite-complex? value)  boolean?

  value : any/c
Returns #t for a non-real complex number whose real and imaginary components are both finite. It is an interpolable scene-value scalar.

value

origin : vec2? = (vec2 0 0)

The origin of world coordinates.

procedure

(real-lerp from to progress)  finite-real?

  from : finite-real?
  to : finite-real?
  progress : finite-real?
Returns the linear interpolation (+ from (* progress (- to from))). A progress value of 0 returns from, and a progress value of 1 returns to. Values outside the unit interval perform linear extrapolation.

procedure

(interpolable? value)  boolean?

  value : any/c
Returns #t for a semantic scene value that supports interpolate-value. In this version, finite real numbers, vec2 coordinates, and rgba-color values are interpolable.

procedure

(interpolate-value from to progress)  any/c

  from : any/c
  to : any/c
  progress : (and/c finite-real? (>=/c 0) (<=/c 1))
Interpolates two values of the same interpolable semantic kind over the closed unit interval. A progress of 0 returns the original from value and a progress of 1 returns the original to value. Mixed kinds and noninterpolable values raise an exception.

17.1 Scene Parameters🔗

procedure

(parameter id initial-value)  scene-parameter?

  id : symbol?
  initial-value : any/c
Creates an immutable declaration for one named interpolable scene value. This is a scene-timeline convenience handle, not a Racket dynamic parameter. Its identity and initial value are immutable, and it has no mutable current value. Pass it to scene-set-value to install its initial value or wherever a scene-value API accepts an ID.

procedure

(scene-parameter? value)  boolean?

  value : any/c
Returns #t when value is a scene parameter declaration.

procedure

(parameter-id value)  symbol?

  value : scene-parameter?
Returns the stable named scene-value identity carried by value.

procedure

(parameter-initial-value value)  any/c

  value : scene-parameter?
Returns the interpolable initial semantic value carried by value.

procedure

(vec2+ a b)  vec2?

  a : vec2?
  b : vec2?
Returns the componentwise sum of a and b.

procedure

(vec2- a b)  vec2?

  a : vec2?
  b : vec2?
Returns the componentwise difference a minus b.

procedure

(vec2-scale scalar value)  vec2?

  scalar : finite-real?
  value : vec2?
Multiplies both components of value by scalar.

procedure

(vec2* a b)  vec2?

  a : vec2?
  b : vec2?
Returns the componentwise product of a and b. This operation is used for non-uniform scale factors.

procedure

(vec2-lerp from to progress)  vec2?

  from : vec2?
  to : vec2?
  progress : finite-real?
Interpolates the x and y components independently. Like real-lerp, this procedure permits extrapolation when progress is outside the unit interval.

17.2 Path Geometry🔗

Path geometry describes outlines and filled regions without using Pict. It supports straight line segments and cubic Bézier segments. Both kinds are semantic model values. They do not contain drawing-context commands or a rendered approximation.

A path may contain several subpaths. Subpath order is significant. Within each subpath, the start point comes first and segments follow in traversal order. Each segment begins where the preceding segment ended. A closed subpath reconnects its final endpoint to its start when it is rendered and measured. That implicit closing edge is straight.

Length, partial extraction, and morphing use local coordinates before a Visual’s scale, rotation, or translation is applied. Compound paths traverse one subpath fully before continuing with the next one. Morphing pairs subpaths and segments by their stored order; it does not reorder them automatically.

struct

(struct line-path-segment (end)
    #:transparent)
  end : vec2?
Represents one straight segment. The segment starts at the previous point in its containing path-subpath and ends at end.

The structure is immutable and transparent. Its public bindings include line-path-segment, line-path-segment?, line-path-segment-end, and struct:line-path-segment.

struct

(struct cubic-bezier-path-segment (control1 control2 end)
    #:transparent)
  control1 : vec2?
  control2 : vec2?
  end : vec2?
Represents one cubic Bézier segment. The segment start is the previous point in its containing path-subpath. The structure therefore stores only the two control points and the endpoint.

The curve starts in the direction from its start toward control1. It approaches end from the direction of control2. Control points usually do not lie on the visible curve. They may lie outside the curve’s actual bounds.

The structure is immutable and transparent. Its public bindings include cubic-bezier-path-segment, cubic-bezier-path-segment?, the three field accessors, and struct:cubic-bezier-path-segment.

procedure

(path-segment? value)  boolean?

  value : any/c
Returns #t when value is a supported semantic path segment. The supported values are line-path-segment and cubic-bezier-path-segment values.

struct

(struct path-subpath (start segments closed?)
    #:transparent)
  start : vec2?
  segments : (listof path-segment?)
  closed? : boolean?
Represents one connected figure in a path.

The start field is the first local point. The segments list contains segment values in significant traversal order. Each segment begins where the preceding segment ended. An empty segment list represents one point.

When closed? is true, rendering reconnects the last endpoint to start with an implicit straight edge. The edge is not stored as an extra segment. It participates in bounds, length, partial extraction, and rendering. A closed subpath can be filled. An open subpath is only stroked by the built-in Pict renderer.

The structure is immutable and transparent. Its public bindings include its constructor, predicate, three field accessors, and structure descriptor.

struct

(struct path-geometry (subpaths)
    #:transparent)
  subpaths : (listof path-subpath?)
Represents zero or more subpaths. Subpath order is significant for traversal, creation, morphing, and filling. The built-in renderer combines all closed subpaths with the odd-even fill rule, so an inner closed subpath can form a hole.

An empty list is valid and represents geometry that draws nothing. The structure is immutable and transparent. Its public bindings include its constructor, predicate, field accessor, and structure descriptor.

A path-geometry value containing no subpaths. It is the semantic invisible geometry used at the beginning of create and at the visible end of uncreate. It is not a blank Pict.

procedure

(path-geometry-empty? geometry)  boolean?

  geometry : path-geometry?
Returns #t when geometry contains no subpaths. A geometry value containing a point-only subpath is not empty.

procedure

(path-subpath-points subpath)  (listof vec2?)

  subpath : path-subpath?
Returns subpath’s start point followed by every segment endpoint. The returned list is in significant traversal order. Cubic control points are not included; use the cubic segment accessors to obtain them. The implicit closing edge of a closed subpath does not repeat the start point at the end.

procedure

(path-geometry-subpath-points geometry)

  (listof (listof vec2?))
  geometry : path-geometry?
Returns one traversal-ordered start-and-endpoint list for each subpath. The outer list preserves subpath order. Cubic control points are omitted in the same way as by path-subpath-points.

procedure

(path-geometry-map-points geometry    
  transform-point)  path-geometry?
  geometry : path-geometry?
  transform-point : (procedure-arity-includes/c 1)
Calls transform-point once for every stored point and returns new path geometry with the same subpath, segment, and closure structure. Stored points include each subpath start, every line endpoint, and every cubic control point and endpoint. The procedure must return a vec2 for every input point. The original geometry is not changed.

This operation transforms local geometry only. It does not change a Visual’s reference position, rotation, or scale.

procedure

(path-geometry-translate geometry    
  displacement)  path-geometry?
  geometry : path-geometry?
  displacement : vec2?
Returns new geometry with displacement added to every stored local point. Segment kinds, subpath order, and closure are preserved.

procedure

(path-geometry-reverse geometry)  path-geometry?

  geometry : path-geometry?
Returns path geometry with every subpath traversed in the opposite direction. Subpath order is preserved. Line segments reverse their endpoints. Cubic Bézier segments reverse their endpoints and exchange their first and second control points, preserving the exact traced curve.

For an open subpath, the former final endpoint becomes the new start. For a closed subpath, the original stored start is preserved so the loop changes direction without also changing phase. When the original implicit closing line has positive length, that line is materialized as the first explicit reversed segment; the resulting synthetic closing edge is then zero length. This representation detail can change segment count, but it does not change the visible or measured loop.

For a positive finite closed loop, point lookup on the reversed result follows the original loop at fraction (- 1 fraction) (subject to the same deterministic cubic arc-length approximation as path-geometry-point-at). Tangent direction is reversed correspondingly away from corner one-sided boundaries.

The operation is purely semantic and does not apply a containing Visual transform. Empty geometry and zero-length subpaths are valid.

procedure

(path-geometry->cubic geometry)  path-geometry?

  geometry : path-geometry?
Converts every stored line segment in geometry to an exactly equivalent cubic Bézier segment. A line from start to end becomes a cubic with these control points:

(vec2-lerp start end 1/3)
(vec2-lerp start end 2/3)

The resulting cubic traces the same straight line in the same direction. An existing cubic segment is kept unchanged. Subpath starts, subpath order, segment order, endpoints, and closure values are preserved. Point-only subpaths and empty geometry are valid.

Only stored line segments are converted. The implicit straight closing edge of a closed subpath remains implicit and is not added to the segment list.

When geometry already contains no stored line segments, the procedure returns geometry itself. Otherwise it returns new immutable geometry.

procedure

(path-geometry-morph-normalizable? from to)  boolean?

  from : path-geometry?
  to : path-geometry?
Returns #t when from and to can be made strictly morph-compatible by the limited normalization performed by path-geometry-normalize-for-morph.

The paths are normalizable when all of the following hold:

  • They contain the same number of subpaths.

  • Corresponding subpaths have the same closure value.

  • Each pair of corresponding subpaths is either point-only on both sides or contains at least one stored segment on both sides.

Stored segment counts and line-versus-cubic kinds may differ. Coordinates and path lengths do not affect this predicate. The predicate does not change either path.

procedure

(path-geometry-align-for-morph 
  source 
  destination 
  [#:allow-reverse? allow-reverse? 
  #:sample-count sample-count]) 
  path-geometry?
  source : path-geometry?
  destination : path-geometry?
  allow-reverse? : boolean? = #t
  sample-count : (and/c exact-integer? (>=/c 8)) = 64
Returns destination with an automatically selected closed-loop cyclic start and, when allow-reverse? is true, traversal direction that gives a low-distance geometric correspondence to source. Both arguments must contain exactly one positive-length finite closed subpath.

Correspondence is measured in total-arc-length coordinates. The procedure samples source at sample-count evenly spaced fractions, scores candidate destination phases by mean Euclidean point distance, and chooses the lowest score deterministically. Candidate phases include a uniform full-loop grid and every stored positive-edge boundary of the destination. A fixed number of local refinement rounds then improves the best phase. The algorithm does not depend on frame rate, camera scale, rendering, wall-clock time, or randomness.

When reverse traversal is allowed, the same search is performed on (path-geometry-reverse destination). An exact score tie prefers the original forward traversal. Exact phase ties prefer the smaller phase. An already aligned destination therefore remains unchanged rather than being reversed or split gratuitously.

The returned value is ordinary semantic path geometry. Use it before path-geometry-normalize-for-morph when a caller wants explicit access to the chosen correspondence, or use morph-to-aligned to perform both steps during timeline compilation.

This operation deliberately handles one closed loop on each side. It does not pair multiple subpaths, add or remove subpaths, change closure, solve arbitrary global shape correspondence, or guarantee a globally optimal continuous phase for every pathological curve. Increasing sample-count makes the fixed deterministic geometric score finer.

procedure

(path-geometry-align-open-for-morph 
  source 
  destination 
  [#:allow-reverse? allow-reverse? 
  #:sample-count sample-count]) 
  path-geometry?
  source : path-geometry?
  destination : path-geometry?
  allow-reverse? : boolean? = #t
  sample-count : (and/c exact-integer? (>=/c 8)) = 64
Returns destination with the traversal direction that best matches one open source path. Both paths must contain exactly one open subpath with positive finite total arc length.

The procedure samples source and destination at sample-count evenly spaced total-arc-length fractions including both endpoints and scores mean Euclidean point distance. When allow-reverse? is true, the same score is computed for (path-geometry-reverse destination). Reverse traversal is selected only when its score is strictly lower; an exact score tie keeps the caller’s stored forward destination. There is no cyclic phase search for an open path.

When forward traversal wins, the exact destination object is returned. When reverse traversal wins, the returned geometry traces the same destination from its former endpoint toward its former start. The operation is pure semantic geometry and does not inspect Visual transforms, renderers, camera state, frame rate, pixels, or output files.

Use the result before path-geometry-normalize-for-morph for explicit preparation, or use morph-to-open-aligned for timeline compilation. This operation deliberately handles one open subpath on each side; it does not select cyclic phases, pair compound subpaths, change closure, or add/remove subpaths.

procedure

(path-geometry-align-open-compound-for-morph 
  source 
  destination 
  [#:allow-reverse? allow-reverse? 
  #:sample-count sample-count]) 
  path-geometry?
  source : path-geometry?
  destination : path-geometry?
  allow-reverse? : boolean? = #t
  sample-count : (and/c exact-integer? (>=/c 8)) = 64
Returns compound destination geometry reordered and direction-aligned so its open subpaths correspond to the open subpaths of source. The two paths must contain the same nonzero number of subpaths, and every subpath must be open with positive finite arc length.

For every source/destination subpath pair, the procedure uses the SCENE-AE endpoint-direction score from path-geometry-align-open-for-morph: both paths are sampled at sample-count inclusive total-arc-length fractions, and destination reversal is selected only when its score is strictly lower. One source subpath’s samples are cached while every destination candidate for that assignment row is evaluated.

After all pair costs are known, the same deterministic minimum-total-cost assignment policy as SCENE-AD chooses one distinct destination subpath for every source subpath. Pairing is global rather than greedy. Exact assignment ties use the deterministic destination-index policy of path-geometry-align-compound-for-morph, while exact per-pair direction ties preserve the caller’s stored forward traversal.

The returned value is ordinary immutable path-geometry whose subpath order matches source correspondence. A destination subpath whose stored direction wins is reused exactly. When pairing and direction alignment change nothing, the exact destination object is returned.

Use the result with path-geometry-normalize-for-morph for explicit preparation, or use morph-to-open-compound-aligned for timeline compilation. This specialized operation deliberately requires all subpaths to be open and counts to match. Use path-geometry-align-mixed-compound-for-morph when matching-count open and closed topology is combined in one compound. Use path-geometry-prepare-topology-changing-morph or morph-to-topology-changing when topology-class counts differ.

procedure

(path-geometry-align-mixed-compound-for-morph 
  source 
  destination 
  [#:allow-reverse? allow-reverse? 
  #:sample-count sample-count]) 
  path-geometry?
  source : path-geometry?
  destination : path-geometry?
  allow-reverse? : boolean? = #t
  sample-count : (and/c exact-integer? (>=/c 8)) = 64
Returns destination reordered/aligned to the topology and subpath correspondence of source. The paths must be nonempty; every subpath must have positive finite arc length; source/destination open-subpath counts must match; and source/destination closed-subpath counts must match.

The procedure partitions both paths by path-subpath-closed?. Open candidates are evaluated with the SCENE-AE endpoint-direction rule used by path-geometry-align-open-for-morph. Closed candidates are evaluated with the SCENE-AC phase/direction rule used by path-geometry-align-for-morph. The deterministic global assignment policy is then solved independently inside the open and closed classes. An open subpath is therefore never paired with a closed loop merely because it is spatially nearby.

After both assignments, selected destination subpaths are placed in the exact subpath order of source. Each open pair may reverse only for a strictly lower score; each closed pair may choose cyclic phase and optional reversal under SCENE-AC’s deterministic tie rules. Unchanged destination subpath objects are reused, and when the entire correspondence is already a no-op the exact destination object is returned.

When one topology class is absent, this operation reduces to the corresponding SCENE-AF all-open or SCENE-AD all-closed behavior. Use path-geometry-prepare-topology-changing-morph when either topology-class count differs.

Use the result with path-geometry-normalize-for-morph for explicit preparation, or use morph-to-mixed-compound-aligned for timeline compilation.

procedure

(path-geometry-prepare-topology-changing-morph 
  source 
  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]) 
  
path-geometry? path-geometry?
  source : path-geometry?
  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()
Returns two equal-count interior correspondence paths suitable for path-geometry-normalize-for-morph even when source and destination contain different numbers of open or closed subpaths. The first result is the prepared source and the second is the aligned/prepared destination.

Every real subpath on either side must have positive finite arc length. The whole source or destination may nevertheless be empty, allowing pure birth from empty geometry and pure death to empty geometry. Open and closed topology classes are handled independently and are never paired directly with one another.

Within each topology class, real open pairs use the SCENE-AE forward/reverse endpoint score and real closed pairs use the SCENE-AC phase/direction score. With the default 'forced value for both penalty keywords, the rectangular assignment pads only the forced class-count difference with zero-cost dummy slots; matching topology counts therefore reduce exactly to SCENE-AG as before.

SCENE-AJ adds an optional numeric policy. When both birth-penalty and death-penalty are finite nonnegative reals, the procedure solves an augmented global assignment. A real source/destination edge costs its geometric correspondence score, a source-to-dummy edge costs death-penalty, a dummy-to-destination edge costs birth-penalty, and unused dummy pairs cost zero. A poor real pair may therefore be replaced by death plus birth even when topology counts match. Exact primary-cost ties minimize the number of birth/death edges as a secondary objective, so equal-cost replacement does not occur. The two penalty keywords must either both be 'forced or both be finite nonnegative real numbers. SCENE-AL additionally accepts sparse birth-penalty-map and death-penalty-map hashes in numeric mode. Birth-map keys are exact nonnegative original destination subpath indexes; death-map keys are exact nonnegative original source subpath indexes. Map values are finite nonnegative real costs and missing keys fall back to the corresponding shared numeric penalty. Nonempty penalty maps are rejected in 'forced mode. The sparse costs replace dummy-edge costs only. SCENE-AM additionally accepts match-penalty-map in both forced and numeric modes. Each key is (cons source-index destination-index) using original caller subpath indexes, and each finite nonnegative value is added to that real edge’s existing geometric correspondence score. Missing pairs add zero. Pair penalties do not change topology classes or direction/phase alignment; they bias the global assignment after those per-edge geometric choices are scored. In numeric mode a penalized real edge also competes against death plus birth, while AJ’s exact-tie preference for fewer topology changes remains unchanged. Direct preparation rejects out-of-range pair indexes and keys that name impossible open/closed correspondence edges. allow-reverse? and sample-count retain their existing correspondence meanings.

A destination subpath assigned to a dummy source is a birth. By default its prepared source is a one-line-segment degenerate subpath at the exact axis-aligned bounds center of that destination subpath. A source subpath assigned to a dummy destination is a death and receives the analogous prepared destination seed at its own bounds center. Each seed preserves the real subpath’s path-subpath-closed? value. The controlled seeds have zero arc length; pre-existing zero-length real subpaths are rejected before assignment.

SCENE-AI extends this placement with birth-anchor and death-anchor. Each accepts exactly 'bounds-center or a finite vec2. An explicit point is local path geometry and is shared by every unmatched subpath on that side. SCENE-AK additionally accepts sparse birth-anchor-map and death-anchor-map hashes. Birth-map keys are exact nonnegative original destination subpath indexes; death-map keys are exact nonnegative original source subpath indexes. Map values use the same anchor syntax. A missing key falls back to the corresponding shared anchor, while an explicit 'bounds-center map value may override a shared vec2. Direct preparation rejects out-of-range keys. Anchor selection affects seed placement only; real-pair scores, direction/phase correspondence, penalties, and slot ordering are unchanged. In numeric SCENE-AJ penalty mode the selected assignment may contain additional voluntary unmatched slots, and those slots use the same original-index map lookup.

All slots corresponding to real source subpaths remain first in exact source order. Birth-only slots are appended in exact caller destination order. When open and closed class counts already match under the default forced-only policy, the operation reduces exactly to path-geometry-align-mixed-compound-for-morph, returning the original source as the first value. Numeric penalties may intentionally produce additional interior slots even when endpoint counts match.

Use the two results with path-geometry-normalize-for-morph for explicit preparation, or use morph-to-topology-changing for timeline compilation. This stage does not infer semantic holes, pair an open subpath directly with a closed loop, use appearance-aware scores, or accept arbitrary per-pair scoring callbacks beyond SCENE-AM sparse numeric additions.

procedure

(path-geometry-align-compound-for-morph 
  source 
  destination 
  [#:allow-reverse? allow-reverse? 
  #:sample-count sample-count]) 
  path-geometry?
  source : path-geometry?
  destination : path-geometry?
  allow-reverse? : boolean? = #t
  sample-count : (and/c exact-integer? (>=/c 8)) = 64
Returns compound destination geometry reordered and aligned so its closed subpaths correspond to the subpaths of source. The two paths must contain the same nonzero number of subpaths, and every subpath must be closed with positive finite arc length.

The procedure computes every source/destination pair cost with the SCENE-AC closed-loop algorithm used by path-geometry-align-for-morph. Thus each candidate pair may choose a cyclic phase and, when allow-reverse? is true, reverse traversal. One source loop’s score samples are cached while all destination candidates for that source are evaluated.

After all pair costs are known, a deterministic minimum-total-cost assignment selects one distinct destination subpath for every source subpath. Pairing is global rather than greedy. Exact assignment ties preserve earlier source-row matches when an equally good free destination exists and then prefer the lower destination index. Per-pair direction and phase ties retain the SCENE-AC rules.

The returned value is ordinary immutable path-geometry whose subpath order matches source correspondence. When pairing and per-loop alignment change nothing, the exact destination object is returned. Otherwise untouched destination subpath objects are reused whenever possible.

Use the result with path-geometry-normalize-for-morph for explicit preparation, or use morph-to-compound-aligned for timeline compilation. This stage does not add/remove subpaths, pair open subpaths, infer semantic hole nesting, or support topology changes.

Returns two path-geometry values that trace the same figures as from and to but have corresponding structure suitable for path-geometry-lerp. The first result is the normalized source, and the second result is the normalized destination.

Normalization works independently on corresponding subpaths:

  • Every stored line segment is converted to an equivalent cubic Bézier segment using path-geometry->cubic.

  • The target segment count is the larger of the two stored segment counts.

  • The side with fewer segments repeatedly splits its longest current cubic at parameter 1/2 until the counts match.

  • Segment length is the same deterministic approximate cubic arc length used by path-subpath-length. When lengths tie, the earliest segment in traversal order is split.

De Casteljau subdivision preserves the exact cubic curve. Splitting therefore changes representation and correspondence, not the traced source or destination figure. Both returned paths contain only cubic stored segments, and path-geometry-morph-compatible? returns #t for them. Point-only corresponding subpaths remain point-only.

The implicit closing edge of a closed subpath is not stored, converted, or split. Closure remains represented by path-subpath-closed?. Subpath order and stored starting points are preserved.

This is deliberately limited normalization. It does not add or remove subpaths, change closure, turn a point-only subpath into a drawn path, reverse traversal, rotate the starting point of a closed path, reorder subpaths, or choose a geometric best match. A difference requiring one of those operations raises an exception that identifies the first unsupported subpath difference. Reported indexes start at zero.

Example:

(define triangle
  (polygon-path
   (list (vec2 -3 -1)
         (vec2 3 -1)
         (vec2 0 2))))
 
(define rectangle
  (polygon-path
   (list (vec2 -3 -2)
         (vec2 3 -2)
         (vec2 3 2)
         (vec2 -3 2))))
 
(define-values (normalized-triangle normalized-rectangle)
  (path-geometry-normalize-for-morph triangle rectangle))
 
(path-geometry-morph-compatible? normalized-triangle
                                 normalized-rectangle)

procedure

(path-geometry-morph-compatible? from to)  boolean?

  from : path-geometry?
  to : path-geometry?
Returns #t when from and to have corresponding structure for pointwise path morphing.

Compatibility requires all of the following:

  • The paths contain the same number of subpaths.

  • Each corresponding subpath has the same closed? value.

  • Each corresponding subpath contains the same number of segments.

  • Each corresponding segment has the same kind: line with line, or cubic Bézier with cubic Bézier.

Subpaths and segments are paired by their stored order. Only stored segments are counted. A closed subpath’s implicit closing edge is represented by its closed? value, not by another stored line segment. Coordinates, path lengths, and style do not affect compatibility. Empty geometry is compatible with empty geometry. Point-only subpaths are compatible when their closure values correspond.

The procedure remains strict. It does not insert segments, convert lines to cubics, reverse a path, rotate a closed path’s starting point, or reorder subpaths. Use path-geometry-morph-normalizable? and path-geometry-normalize-for-morph for the limited explicit normalization provided by this version.

procedure

(path-geometry-lerp from to progress)  path-geometry?

  from : path-geometry?
  to : path-geometry?
  progress : (real-in 0 1)
Interpolates the stored points of two morph-compatible paths. The subpath start points are interpolated pairwise. Line endpoints are interpolated pairwise. For cubic segments, both control points and the endpoint are interpolated pairwise. Subpath order, segment order, segment kind, and closure are preserved.

progress must be a finite real in the closed unit interval. A value of 0 returns from itself. A value of 1 returns to itself. An interior value returns new path geometry.

The interpolation uses local mathematical coordinates. It does not change a Visual’s identity, reference position, rotation, scale, fill, stroke, stroke width, or drawing order. Use morph-to to place this interpolation on a timeline.

When the paths are incompatible, the procedure raises an exception describing the first mismatch in deterministic traversal order. The diagnostic identifies a subpath-count, closure, segment-count, or segment-kind mismatch. Reported subpath and segment indexes start at zero. The procedure remains strict and does not normalize incompatible paths automatically. Normalize explicitly or use morph-to-normalized for the limited supported cases.

Example:

(define rectangle-path
  (polygon-path
   (list (vec2 -2 -1)
         (vec2 2 -1)
         (vec2 2 1)
         (vec2 -2 1))))
 
(define diamond-path
  (polygon-path
   (list (vec2 0 -2)
         (vec2 3 0)
         (vec2 0 2)
         (vec2 -3 0))))
 
(path-geometry-lerp rectangle-path diamond-path 1/2)

Returns four values: minimum x, minimum y, maximum x, and maximum y over the visible path. Coordinates are local world units.

Line bounds come from their endpoints. Cubic bounds include their endpoints and every interior parameter where the x or y derivative is zero. The result therefore follows the curve itself instead of using the usually larger box of its control points. The result is mathematically tight subject to the ordinary numeric behavior of the supplied coordinates.

The implicit straight closing edge of a closed subpath is included. Empty geometry has no bounds and raises an exception.

procedure

(path-geometry-center geometry)  vec2?

  geometry : path-geometry?
Returns the center of geometry’s axis-aligned local bounding box. It is the midpoint of the minimum and maximum x values and of the minimum and maximum y values. Cubic extrema are included. Empty geometry raises an exception.

procedure

(path-subpath-length subpath)  (and/c real? (>=/c 0))

  subpath : path-subpath?
Returns the local arc length of subpath in world units. A line segment contributes the Euclidean distance from its start to its endpoint. A cubic segment contributes a deterministic approximation of its curve length. When the subpath is closed, the implicit straight edge from the final endpoint back to (path-subpath-start subpath) is included.

Cubic length is computed by repeatedly splitting the curve in half. For each piece, the implementation compares the control-polygon length with the chord length. Subdivision stops when their difference is at most the larger of 1e-10 world units and 1e-8 times the original cubic’s control-polygon length, or after 20 subdivisions. The piece estimate is the average of its chord and control-polygon lengths. These constants are part of the current numeric behavior, but they are not a formal error guarantee.

A point-only subpath has length zero. Repeated adjacent points and completely degenerate cubics contribute zero. The affine transform of a containing Visual is not applied. The line distance calculation avoids unnecessary overflow, but a true distance beyond the inexact number range can produce +inf.0.

procedure

(path-geometry-length geometry)  (and/c real? (>=/c 0))

  geometry : path-geometry?
Returns the sum of the local arc lengths of all subpaths in geometry. Empty geometry has length zero. Line portions use Euclidean length, and cubic portions use the deterministic approximation described for path-subpath-length.

This is a geometric model operation. A non-uniform scale on a path Visual can change the displayed world-space length, but it does not change the value returned here. The result can be +inf.0 when an inexact distance is too large to represent. create, uncreate, and non-full partial extraction require a finite result.

procedure

(path-geometry-point-at geometry fraction)  vec2?

  geometry : path-geometry?
  fraction : (real-in 0 1)
Returns the point at fraction of geometry’s total ordered arc length. fraction must be a finite real in the closed unit interval, and the computed total path length must be positive and finite.

Traversal uses the same significant edge order and length model as path-geometry-partial. Line portions use exact Euclidean distance. A point inside a cubic uses the same deterministic adaptive arc-length table described for path-subpath-length to approximate the corresponding curve parameter. The returned value is a semantic local point; no containing Visual transform is applied.

Compound path geometry is traversed subpath by subpath in stored order. Zero-length edges consume no positive fraction. The implicit closing edge of a closed subpath is included. Exact 0 returns the start of the first positive-length edge, and exact 1 returns the endpoint of the final positive-length traversal edge. At an exact boundary between positive subpaths, the endpoint of the preceding subpath is selected.

Empty geometry, zero-total-length geometry, or a non-finite computed total length raises an exception. move-along-path adds a stricter continuity rule and accepts only one positive-length subpath as a motion route.

procedure

(path-geometry-tangent-at geometry    
  fraction)  vec2?
  geometry : path-geometry?
  fraction : (real-in 0 1)
Returns the forward unit tangent at fraction of geometry’s total ordered arc length. The fraction, positive finite length requirement, edge order, zero-length-edge handling, implicit closing edge, and compound-subpath traversal rules match path-geometry-point-at. At an exact positive-length edge boundary, the preceding traversal edge owns both the point and the tangent.

Line tangents are normalized directed edge vectors. Cubic tangents are computed from the cubic derivative at the parameter selected by the same adaptive arc-length table used for point lookup. When that derivative is zero at a stationary endpoint or cusp, deterministic one-sided curve probes recover the forward traversal direction when one exists. The result is semantic local geometry; no containing Visual transform is applied.

Empty geometry, zero-total-length geometry, a non-finite computed total length, or a selected positive-length cubic point with no recoverable traversal direction raises an exception.

procedure

(path-geometry-normal-at geometry fraction)  vec2?

  geometry : path-geometry?
  fraction : (real-in 0 1)
Returns the left unit normal corresponding to path-geometry-tangent-at at fraction. For tangent (vec2 tx ty), the result is (vec2 (- ty) tx). The operation therefore has the same domain and error behavior as tangent lookup.

procedure

(path-geometry-offset geometry 
  distance 
  [#:join join 
  #:miter-limit miter-limit]) 
  path-geometry?
  geometry : path-geometry?
  distance : finite-real?
  join : (or/c 'miter 'bevel 'round) = 'miter
  miter-limit : (and/c finite-real? (>=/c 1)) = 4
Returns a continuous signed parallel offset of straight-segment subpaths in geometry. Positive distance is to the left of stored traversal direction; negative distance is to the right. A zero distance returns geometry unchanged. The result is ordinary immutable semantic path geometry and can be passed directly to make-path-visual, move-along-path, orient-along-path, and other path operations.

For an outside corner, join selects how adjacent shifted edge lines are connected. 'miter extends them to their intersection. The distance from the original vertex to that intersection is divided by the absolute offset distance; when the ratio exceeds miter-limit, the outside corner falls back to bevel. 'bevel connects the shifted edge endpoints with one straight segment. 'round uses one or more cubic Bézier circular-arc approximations centered at the original vertex, with no cubic spanning more than a quarter turn.

Inside corners always use the natural intersection of the two shifted lines, independent of join. This keeps the offset path traversing forward along both adjacent lines; the short centered arc on the inside would have the wrong endpoint tangents. Collinear same-direction edges share their shifted point.

Open subpath endpoints are shifted by their endpoint edge normals. Closed subpaths are joined cyclically, including the stored start vertex. Every edge participating in a nonzero offset must be a positive-length line-path-segment. A zero-length edge, an exact 180-degree reversal, or a cubic source segment raises an exception in this stage. Cubic segments may still appear in the result as round-join pieces.

The construction is geometric rather than renderer-dependent: camera scale, stroke width, and output resolution do not affect the generated path. The result has its own arc length, so move-along-path with linear easing moves uniformly along the joined offset route itself.

procedure

(path-geometry-partial geometry start end)  path-geometry?

  geometry : path-geometry?
  start : (real-in 0 1)
  end : (real-in 0 1)
Returns the interval from fraction start through fraction end of geometry’s total local arc length. Both fractions must be finite, and start must not be greater than end.

Fractions apply to the whole compound path. Traversal completes each subpath before beginning the next. A cut inside a line is inserted by linear interpolation. A cut inside a cubic uses the same deterministic adaptive length table as path-subpath-length to approximate the corresponding curve parameter. The selected cubic interval is then extracted with de Casteljau subdivision. The result remains a cubic segment; it is not replaced by a polyline or a Pict command.

A partially selected closed subpath becomes open. Its implicit closing edge is part of the traversal and may itself be returned as an open line segment. A closed subpath keeps its closure only when its complete traversal is selected. This rule prevents a partial prefix from being filled as if it were complete.

When start and end are equal, the result is empty-path-geometry. The exact interval from 0 through 1 returns geometry unchanged. This preserves segment types, control points, point-only subpaths, and other zero-length structure. For a zero-length path, every other interval returns empty geometry.

Zero-length edges and subpaths do not consume a positive portion of the arc length. They can therefore disappear from a non-full partial result. Any non-full partial extraction requires the computed total path length to be finite. Extremely large inexact coordinates whose distance overflows raise an exception here.

Examples:

(define mixed-path
  (path-geometry
   (list
    (path-subpath
     origin
     (list (line-path-segment (vec2 2 0))
           (cubic-bezier-path-segment (vec2 3 2)
                                      (vec2 4 2)
                                      (vec2 5 0)))
     #f))))
 
(path-geometry-partial mixed-path 0 1/2)

procedure

(path-geometry-cycle-start geometry    
  fraction)  path-geometry?
  geometry : path-geometry?
  fraction : (real-in 0 1)
Moves the stored start of one closed loop to fraction of that loop’s total arc length while preserving its forward traversal direction and traced geometry. The fraction uses the same deterministic line/cubic length model as path-geometry-point-at.

geometry must contain exactly one closed subpath with positive finite length. Exact 0 and 1 return geometry unchanged. For any other fraction, the selected point becomes both fraction zero and fraction one of the returned closed loop.

When the selected point lies inside a line, the line is split by interpolation. When it lies inside a cubic, the arc-length table chooses the deterministic curve parameter and de Casteljau subdivision splits the curve. The tail from the selected point through the original closing edge is followed by the old prefix back to the selected point. The returned subpath remains closed; its final synthetic closing edge has zero length.

The operation deliberately handles one closed subpath rather than assigning one phase to every subpath of compound geometry. Automatic per-subpath phase selection is a separate correspondence problem. The returned value is ordinary path geometry and can be used directly by make-path-visual, move-along-path, orient-along-path, camera following, partial extraction, and normalized morph preparation.

procedure

(polyline-path points)  path-geometry?

  points : (listof vec2?)
Creates path geometry containing one open line-segment subpath. At least two points are required. Points are stored in the supplied order and adjacent points are connected by line-path-segment values.

The points are local coordinates. Use make-path-visual to choose the reference position in the Visual’s containing coordinate system. That system is world space at the top level and group-local space for a child.

procedure

(polygon-path points)  path-geometry?

  points : (listof vec2?)
Creates path geometry containing one closed line-segment subpath. At least three points are required. The closing edge from the final point to the first point is represented by the subpath’s closed? field and is not stored as a repeated endpoint.

Point order determines traversal direction and can affect later path operations. The built-in renderer uses the odd-even fill rule, so clockwise and counter-clockwise order produce the same simple fill in this version.

procedure

(cubic-bezier-path start    
  segments    
  [#:closed? closed?])  path-geometry?
  start : vec2?
  segments : 
(and/c pair?
       (listof cubic-bezier-path-segment?))
  closed? : boolean? = #f
Creates path geometry containing one subpath made from one or more cubic Bézier segments. The subpath begins at start. Segment order is significant, and each segment begins at the preceding segment’s endpoint.

When closed? is true, the subpath also has an implicit straight edge from its final endpoint back to start. A smooth closed cubic shape normally includes a final cubic whose endpoint is start and also sets closed? to true so that the built-in renderer fills it. In that case, the implicit closing edge has length zero.

Example:

(define arch
  (cubic-bezier-path
   (vec2 -2 0)
   (list
    (cubic-bezier-path-segment (vec2 -2 2)
                               (vec2 2 2)
                               (vec2 2 0)))))

17.2.1 General Boolean Path Geometry and Clipping🔗

SCENE-DY extends the immutable Boolean path operations to simple concave and compound closed paths. Each cubic contour is uniformly sampled into #:curve-samples line pieces before clipping, so curve results are deterministic polygonal approximations rather than exact Bézier intersections. Each input contour must still be simple and closed.

The default 'odd-even fill rule matches the path renderers: nested contours alternate between filled regions and holes regardless of their orientation. 'nonzero additionally supports ordinary oriented, nonintersecting contour nests, where reversing an inner loop makes it a hole. The implementation triangulates internally but reconstructs exterior and hole loops before returning a path-geometry?, so applying a cosmetic stroke does not reveal triangulation seams.

procedure

(path-union first    
  second    
  [#:curve-samples curve-samples    
  #:fill-rule fill-rule])  path-geometry?
  first : path-geometry?
  second : path-geometry?
  curve-samples : exact-positive-integer? = 16
  fill-rule : (or/c 'odd-even 'nonzero) = 'odd-even
Returns the filled union of first and second.

procedure

(path-intersection first 
  second 
  [#:curve-samples curve-samples 
  #:fill-rule fill-rule]) 
  path-geometry?
  first : path-geometry?
  second : path-geometry?
  curve-samples : exact-positive-integer? = 16
  fill-rule : (or/c 'odd-even 'nonzero) = 'odd-even
Returns the filled region common to first and second. A touching-without-area intersection is empty-path-geometry.

procedure

(path-difference first 
  second 
  [#:curve-samples curve-samples 
  #:fill-rule fill-rule]) 
  path-geometry?
  first : path-geometry?
  second : path-geometry?
  curve-samples : exact-positive-integer? = 16
  fill-rule : (or/c 'odd-even 'nonzero) = 'odd-even
Returns the filled portion of first outside second.

procedure

(path-xor first    
  second    
  [#:curve-samples curve-samples    
  #:fill-rule fill-rule])  path-geometry?
  first : path-geometry?
  second : path-geometry?
  curve-samples : exact-positive-integer? = 16
  fill-rule : (or/c 'odd-even 'nonzero) = 'odd-even
Returns points covered by exactly one operand.

procedure

(cutout outer    
  inner    
  [#:curve-samples curve-samples    
  #:fill-rule fill-rule])  path-geometry?
  outer : path-geometry?
  inner : path-geometry?
  curve-samples : exact-positive-integer? = 16
  fill-rule : (or/c 'odd-even 'nonzero) = 'odd-even
A readable alias for path-difference, intended for a filled outer shape from which inner is removed.

procedure

(clip-to subject    
  clip    
  [#:curve-samples curve-samples    
  #:fill-rule fill-rule])  path-geometry?
  subject : path-geometry?
  clip : path-geometry?
  curve-samples : exact-positive-integer? = 16
  fill-rule : (or/c 'odd-even 'nonzero) = 'odd-even
With two paths, returns the geometry-level clipping of subject to clip. It is a readable alias for path-intersection.

clip-to also accepts an affine Visual as subject and a local path-geometry? as clip. In that form, supply #:id to receive a clipped-visual?: a normal affine/opacity Visual that clips the complete vector content at render time. Its content and clip path move, rotate, scale, and fade together. The optional Boolean quality and fill rule keywords apply only to the two-path form.

procedure

(mask-with subject    
  mask    
  [#:curve-samples curve-samples    
  #:fill-rule fill-rule])  path-geometry?
  subject : path-geometry?
  mask : path-geometry?
  curve-samples : exact-positive-integer? = 16
  fill-rule : (or/c 'odd-even 'nonzero) = 'odd-even
With two paths, returns the geometry-level mask of subject by mask. Its filled-region semantics are the same as clip-to. The affine-Visual-plus-path overload constructs the same vector-preserving wrapper, but uses masking terminology.

procedure

(clip-visual content    
  path    
  #:id id    
  [#:center center    
  #:rotation rotation    
  #:scale scale    
  #:opacity opacity])  clipped-visual?
  content : affine-visual?
  path : path-geometry?
  id : symbol?
  center : vec2? = origin
  rotation : finite-real? = 0
  scale : scale-factor? = 1
  opacity : opacity? = 1
Constructs the low-level clipping wrapper used by the Visual overloads above. Normally clip-to is clearer.

procedure

(clipped-visual? value)  boolean?

  value : any/c
Recognizes the vector-preserving clipping wrapper returned by the Visual overloads of clip-to, mask-with, and clip-visual.

Current limits: 'odd-even repairs contours with proper self-crossings, but rejects touching or overlapping segments; 'nonzero rejects crossing contour boundaries; and cubic input is flattened rather than preserved as cubic output.

17.3 Affine Transforms🔗

Affine-transform values are created through make-affine-transform. The raw structure constructor is not part of the public API.

procedure

(affine-transform? value)  boolean?

  value : any/c
Returns #t when value is an affine-transform value.

procedure

(affine-transform-translation transform)  vec2?

  transform : affine-transform?
Returns the translation component of transform in its containing coordinate system. A top-level Visual normally uses world coordinates. A child Visual normally uses coordinates local to its group.

procedure

(affine-transform-rotation transform)  finite-real?

  transform : affine-transform?
Returns the counter-clockwise rotation of transform in radians.

procedure

(affine-transform-scale transform)  vec2?

  transform : affine-transform?
Returns the positive x and y scale factors of transform. A uniform scale is stored with equal components.

procedure

(scale-factor? value)  boolean?

  value : any/c
Returns #t when value is either a positive finite real number or a vec2 whose two components are positive. A number represents uniform scale. A vec2 represents separate x and y scale.

procedure

(scale-factor->vec2 value)  vec2?

  value : scale-factor?
Converts a scale factor to two components. A numeric value s becomes (vec2 s s). A vec2 value is returned unchanged.

procedure

(make-affine-transform [#:translation translation 
  #:rotation rotation 
  #:scale scale]) 
  affine-transform?
  translation : vec2? = origin
  rotation : finite-real? = 0
  scale : scale-factor? = 1
Creates a validated decomposed transform. Translation is measured in the coordinate system that contains the transformed value. Translation is applied last, rotation is counter-clockwise, and scale is normalized with scale-factor->vec2.

The transform with zero translation, zero rotation, and scale (vec2 1 1). It leaves every point unchanged.

procedure

(affine-transform-with-translation transform 
  translation) 
  affine-transform?
  transform : affine-transform?
  translation : vec2?
Returns a new transform with translation installed. Rotation and scale are preserved.

procedure

(affine-transform-with-rotation transform    
  rotation)  affine-transform?
  transform : affine-transform?
  rotation : finite-real?
Returns a new transform with rotation installed. Translation and scale are preserved.

procedure

(affine-transform-with-scale transform    
  scale)  affine-transform?
  transform : affine-transform?
  scale : scale-factor?
Returns a new transform with scale installed. Translation and rotation are preserved. Numeric scales are normalized to a vec2.

procedure

(affine-transform-lerp from to progress)  affine-transform?

  from : affine-transform?
  to : affine-transform?
  progress : (and/c finite-real? (>=/c 0) (<=/c 1))
Interpolates translation, rotation, and scale componentwise. Progress must be between 0 and 1, inclusive.

Rotation is interpolated as an ordinary number. The procedure does not choose the shortest path around a circle. For example, interpolation from 0 to (* 2 pi) describes one full turn.

procedure

(affine-transform-apply-vector transform    
  vector)  vec2?
  transform : affine-transform?
  vector : vec2?
Applies scale and rotation to a displacement vector. Translation is ignored, because a displacement has no fixed position.

procedure

(affine-transform-apply-point transform    
  point)  vec2?
  transform : affine-transform?
  point : vec2?
Applies scale, rotation, and translation to a point, in that order.

17.3.1 General Affine Maps🔗

SCENE-CY-A keeps affine-transform as the established decomposed placement protocol and adds linear2 and affine2 for general mathematical maps. A linear2 value represents the matrix

| a b | | c d |

acting on column vectors. Matrix entries may be negative or form a singular matrix. In particular, entry-wise interpolation toward a reflection normally passes through a singular map.

The public constructor follows the displayed row order, which keeps matrix literals readable when split across lines:

(linear2 a b
         c d)

An affine2 adds the translation column in the same order:

(affine2 a b h
         c d k)

This represents (values (+ (* a x) (* b y) h) (+ (* c x) (* d y) k)).

procedure

(linear2? value)  boolean?

  value : any/c
Returns #t for a general two-dimensional linear map.

procedure

(linear2 a b c d)  linear2?

  a : finite-real?
  b : finite-real?
  c : finite-real?
  d : finite-real?
Constructs the matrix | a b | | c d |. The arguments are in row order.

procedure

(make-linear2 a b c d)  linear2?

  a : finite-real?
  b : finite-real?
  c : finite-real?
  d : finite-real?
Convenience constructor for the matrix | a b | | c d |. Its arguments have the same row order as linear2.

The identity matrix.

procedure

(linear2-a map)  finite-real?

  map : linear2?
Returns the a entry.

procedure

(linear2-b map)  finite-real?

  map : linear2?
Returns the b entry.

procedure

(linear2-c map)  finite-real?

  map : linear2?
Returns the c entry.

procedure

(linear2-d map)  finite-real?

  map : linear2?
Returns the d entry.

procedure

(linear2-determinant map)  finite-real?

  map : linear2?
Returns (- (* a d) (* b c)) for map.

procedure

(linear2-invert map)  (or/c linear2? #f)

  map : linear2?
Returns the inverse of map, or #f when its determinant is zero. This is useful when converting a world-space affine request to a mapped child’s local coordinate system.

procedure

(linear2-compose outer inner)  linear2?

  outer : linear2?
  inner : linear2?
Returns outer composed after inner.

procedure

(linear2-apply-vector map vector)  vec2?

  map : linear2?
  vector : vec2?
Applies map to one displacement vector.

procedure

(affine2? value)  boolean?

  value : any/c
Returns #t for a general linear map followed by translation.

procedure

(affine2 a b h c d k)  affine2?

  a : finite-real?
  b : finite-real?
  h : finite-real?
  c : finite-real?
  d : finite-real?
  k : finite-real?
Constructs the affine map (values (+ (* a x) (* b y) h) (+ (* c x) (* d y) k)). The entries are in augmented-row order: (affine2 a b h c d k).

procedure

(make-affine2 [#:linear linear    
  #:translation translation])  affine2?
  linear : linear2? = identity-linear2
  translation : vec2? = origin
Constructs a general affine map. The linear map acts first and translation is added afterward. Use this keyword form when the linear and translation parts are already available separately; use affine2 for a matrix literal.

The identity general affine map.

procedure

(affine2-linear map)  linear2?

  map : affine2?
Returns map’s linear component.

procedure

(affine2-translation map)  vec2?

  map : affine2?
Returns map’s translation component.

procedure

(affine2-a map)  finite-real?

  map : affine2?
Returns the a entry.

procedure

(affine2-b map)  finite-real?

  map : affine2?
Returns the b entry.

procedure

(affine2-h map)  finite-real?

  map : affine2?
Returns the x translation h.

procedure

(affine2-c map)  finite-real?

  map : affine2?
Returns the c entry.

procedure

(affine2-d map)  finite-real?

  map : affine2?
Returns the d entry.

procedure

(affine2-k map)  finite-real?

  map : affine2?
Returns the y translation k.

procedure

(affine2-with-linear map linear)  affine2?

  map : affine2?
  linear : linear2?
Returns map with a replacement linear component.

procedure

(affine2-with-translation map translation)  affine2?

  map : affine2?
  translation : vec2?
Returns map with a replacement translation.

procedure

(affine2-invert map)  (or/c affine2? #f)

  map : affine2?
Returns the inverse of map, or #f when its linear component is singular.

procedure

(affine2-compose outer inner)  affine2?

  outer : affine2?
  inner : affine2?
Returns outer composed after inner.

procedure

(affine2-lerp from to progress)  affine2?

  from : affine2?
  to : affine2?
  progress : (and/c finite-real? (>=/c 0) (<=/c 1))
Interpolates the four matrix entries and translation componentwise. It does not preserve invertibility.

procedure

(affine2-apply-vector map vector)  vec2?

  map : affine2?
  vector : vec2?
Applies only map’s linear component to vector.

procedure

(affine2-apply-point map point)  vec2?

  map : affine2?
  point : vec2?
Applies both the linear component and translation to point.

procedure

(affine-transform->affine2 transform)  affine2?

  transform : affine-transform?
Converts the established scale-then-rotate-then-translate representation to an exact general affine map.

17.4 Cameras🔗

A camera is an immutable orthographic view. Its pixel dimensions, visible world width, center, and background are explicit values. The raw camera constructor is not public; use make-camera. A scene can store and animate camera values without mutating them.

procedure

(camera? value)  boolean?

  value : any/c
Returns #t when value is a camera value.

procedure

(camera-width camera)  exact-positive-integer?

  camera : camera?
Returns the output width in pixels.

procedure

(camera-height camera)  exact-positive-integer?

  camera : camera?
Returns the output height in pixels.

procedure

(camera-world-width camera)  (and/c finite-real? positive?)

  camera : camera?
Returns the visible width in world units.

procedure

(camera-center camera)  vec2?

  camera : camera?
Returns the world point placed at the center of the output frame.

procedure

(camera-background camera)  any/c

  camera : camera?
Returns the background style stored in the camera. The built-in Pict adapter passes this value to Pict as a color. A color name string is the usual choice. The value is not checked until an adapter uses it.

procedure

(make-camera [#:width width    
  #:height height    
  #:world-width world-width    
  #:center center    
  #:background background])  camera?
  width : exact-positive-integer? = 1280
  height : exact-positive-integer? = 720
  world-width : (and/c finite-real? positive?) = 14
  center : vec2? = origin
  background : any/c = "white"
Creates an immutable orthographic camera value. The aspect ratio comes from width and height. The visible world height is derived from that aspect ratio and world-width.

The default camera: 1280 by 720 pixels, 14 world units wide, centered at origin, with a white background.

procedure

(camera-scale camera)  (and/c real? positive?)

  camera : camera?
Returns the number of pixels per world unit. It is (/ (camera-width camera) (camera-world-width camera)).

procedure

(camera-world-height camera)  (and/c real? positive?)

  camera : camera?
Returns the visible height in world units. It preserves the camera’s pixel aspect ratio.

procedure

(camera-length->pixels camera length)  real?

  camera : camera?
  length : finite-real?
Converts a signed world-space length to pixels. The procedure multiplies by camera-scale. It does not take an absolute value.

procedure

(camera-world->pixel camera point)  
real? real?
  camera : camera?
  point : vec2?
Returns two values: the pixel x coordinate and the pixel y coordinate of point. The camera center maps to the center of the output frame. World y increases upward, while pixel y increases downward.

procedure

(camera-pan-to center)  camera-pan-to-request?

  center : vec2?
Creates an absolute camera-center request for scene-play. At eased progress one, center is the world point in the middle of the frame. The camera’s pixel dimensions, visible width, aspect ratio, and background are unchanged.

procedure

(camera-pan-to-request? value)  boolean?

  value : any/c
Returns #t when value is a request created by camera-pan-to.

procedure

(camera-pan-by delta)  camera-pan-by-request?

  delta : vec2?
Creates a relative camera-center request. When scene-play compiles the request, it adds delta to the camera center at the beginning of that clip. The request does not capture a camera when it is constructed.

procedure

(camera-pan-by-request? value)  boolean?

  value : any/c
Returns #t when value is a request created by camera-pan-by.

procedure

(camera-zoom-to world-width)  camera-zoom-to-request?

  world-width : (and/c finite-real? positive?)
Creates an absolute camera zoom request. world-width is the requested visible frame width in world units. A smaller width shows less of the world and therefore appears more magnified. During a clip, visible world width is interpolated linearly after easing; interpolation is not logarithmic. Pixel width, pixel height, center, and background remain unchanged unless another request changes the center.

procedure

(camera-zoom-to-request? value)  boolean?

  value : any/c
Returns #t when value is a request created by camera-zoom-to.

procedure

(camera-zoom-by factor)  camera-zoom-by-request?

  factor : (and/c finite-real? positive?)
Creates a relative magnification request. The target visible width is the clip-start camera width divided by factor. Thus 2 zooms in by two, 1 leaves magnification unchanged, and 1/2 zooms out by two. The resulting target width is interpolated linearly from the clip-start width after easing.

Compilation raises an exception if the division produces a nonpositive or non-finite visible width.

procedure

(camera-zoom-by-request? value)  boolean?

  value : any/c
Returns #t when value is a request created by camera-zoom-by.

procedure

(camera-follow target)  camera-follow-request?

  target : (or/c visual? symbol?)
Creates a clip-local request that keeps the reference position of target at its clip-start pixel position. The target is resolved as a current top-level scene Visual when scene-play compiles the request. Nested group children are not searched.

Frame-space Visuals are not valid follow targets; camera following is defined only for world-space top-level Visuals. A SCENE-AW derived target is resolved against the same sampled scalar state before its world-space position is read.

At each scene sample, following reads the target’s actual sampled visual-position at the same eased progress as its Visual animations. The pre-removal motion state is retained for camera completion, so the request can follow a Visual introduced by fade-in or create, and it can follow a Visual until a same-clip fade-out or uncreate removes it at the structural endpoint. The sampled visual-position result must be a vec2.

Following preserves normalized horizontal and vertical frame position. When a zoom request runs in the same clip, the camera adjusts its world-space offset as the visible width changes, so the target remains at the same pixel coordinates. Because the sampled Visual state supplies the position, the camera also follows nonlinear motion such as move-along-path through a bend or curve rather than interpolating only between clip endpoints. The request changes the camera-center component and may run with one zoom request. It conflicts with pan, fitting, or another follow request.

Following tracks only visual-position, not the target’s rendered box, rotation, scale, or shape. It lasts for one play clip. It does not install a persistent observer. Camera-only clips without following do not require scene state sampling.

procedure

(camera-follow-request? value)  boolean?

  value : any/c
Returns #t when value is a request created by camera-follow.

procedure

(camera-fit-request? value)  boolean?

  value : any/c
Returns #t when value is a camera-fit request created by camera-fit-layout-box, camera-fit-visuals, or camera-fit-scene, including the camera-focus specialization. A fit request changes camera center and visible world width together. It therefore reserves both camera components in one play clip.

procedure

(camera-fit-request-center request)  vec2?

  request : camera-fit-request?
Returns the immutable measured center stored in request. This is most useful when inspecting a fit or passing it to a secondary camera; ordinary scene code normally supplies the complete request directly to scene-play or camera-view-fit.

Returns the immutable measured visible world width stored in request.

procedure

(camera-fit-layout-box box    
  [#:camera camera    
  #:padding padding])  camera-fit-request?
  box : layout-box?
  camera : camera? = default-camera
  padding : (and/c finite-real? (>=/c 0)) = 1/2
Creates a camera-fit request for box. The target camera center is (layout-box-center box). The target visible width is the larger of:

  • the box width plus padding on both horizontal sides; and

  • the width needed to contain the padded box height while preserving the pixel aspect ratio of camera.

Padding is measured in world units and is applied equally on all four sides before aspect-ratio correction. Pixel width, pixel height, and background are preserved. A zero-size box with zero padding is rejected because it would produce a nonpositive visible width.

The result is a snapshot request. It stores only the concrete target center and visible width; it does not retain box or recompute it during animation. Use a camera with the same pixel aspect ratio as the scene camera that will run the request. The stored fit is not recomputed for a different aspect ratio.

procedure

(camera-fit-visuals visuals 
  [#:camera camera 
  #:renderers renderers 
  #:padding padding]) 
  camera-fit-request?
  visuals : (and/c (listof visual?) pair?)
  camera : camera? = default-camera
  renderers : pict-renderer-list? = default-pict-renderers
  padding : (and/c finite-real? (>=/c 0)) = 1/2
Measures the complete rendered union of visuals with visuals-layout-box, then creates the corresponding fit request. The list must be nonempty, every Visual must use the same containing coordinate system, and every supplied Visual must belong to world space. Frame-space overlays and callouts are not camera-fit geometry.

Measurement uses camera and the first-supporting renderer rule from renderers. Custom renderer padding therefore participates in fitting. A standalone derived-visual? cannot be measured here because this function has no scene-state scalar context; use camera-fit-scene for a derived top-level target. Measuring a nonempty formula through the built-in formula renderer can invoke LaTeX and Poppler. Opacity does not change the measured Pict dimensions.

The values are measured when this function is called. Geometry or renderer changes later in the same clip do not cause automatic remeasurement. The fit is also not recomputed at its target zoom. A custom renderer whose Pict has a fixed pixel size can therefore occupy a different world-space size at the endpoint. To fit a planned endpoint in the same clip, supply Visual values and a measurement camera that describe the intended view as closely as possible.

procedure

(camera-fit-scene scene    
  [#:targets targets    
  #:renderers renderers    
  #:padding padding])  camera-fit-request?
  scene : scene?
  targets : 
(or/c false/c
      (and/c pair?
             (listof (or/c visual? symbol? visual-path?))))
   = #f
  renderers : pict-renderer-list? = default-pict-renderers
  padding : (and/c finite-real? (>=/c 0)) = 1/2
Creates a fit request from scene’s current endpoint state and current camera. When targets is #f, all current top-level world-space Visuals are measured in back-to-front order; frame-space overlays and callouts are ignored. Otherwise, targets must be a nonempty list of Visual values, symbols, or explicit nonempty Visual paths, and every resolved target must be a world-space Visual. A top-level target is resolved by stable identity against (scene-current-state scene), so a stale constructor value still selects the current scene value. A nested path is resolved with every enclosing group/formula transform and opacity composed into an independently measurable world-space Visual. SCENE-AW derived definitions are additionally evaluated against the current endpoint scalar values before measurement.

A scene with no world-space Visuals, an empty target list, a missing target, or an explicitly selected frame-space target raises an exception. The result is a snapshot of the current endpoint state and does not follow later scene changes.

procedure

(camera-focus scene    
  focus    
  [#:context context    
  #:renderers renderers    
  #:padding padding])  camera-fit-request?
  scene : scene?
  focus : (or/c visual? symbol? visual-path?)
  context : (listof (or/c visual? symbol? visual-path?)) = '()
  renderers : pict-renderer-list? = default-pict-renderers
  padding : (and/c finite-real? (>=/c 0)) = 1/2
Creates a renderer-aware fit around one explanatory subject and zero or more explicit context Visuals. focus and every value in context may be a current top-level Visual, its symbol identity, or an explicit nested Visual path. The request is equivalent to a camera-fit-scene selection whose first target is the focus, but its named #:context argument makes the pedagogical framing decision clear at the call site.

All selected targets are measured in fully composed world coordinates. This makes an imported SVG element or formula/group child a useful focus subject without rebuilding its parent. Frame-space targets are rejected. The request is a current-scene snapshot: it does not choose context automatically, remeasure during the clip, or live-follow later subject/context motion.

17.5 Frame-Space Overlays and Callouts🔗

Frame-space Visuals are semantic wrappers, not cached Picts. They remain ordinary top-level Visuals with stable identity and can participate in move-to, rotation, scale, opacity, structural fade, and ordinary scene ordering. Their reference positions are interpreted in frame coordinates by the adapter rather than world coordinates.

procedure

(frame-space-visual? value)  boolean?

  value : any/c
Returns #t for the built-in fixed-in-frame, camera-view, and callout wrapper values.

Returns the visible width of visual’s captured frame coordinate system. This is normally the camera-world-width of the camera supplied when the wrapper was constructed.

procedure

(frame-space-camera camera frame-width)  camera?

  camera : camera?
  frame-width : (and/c finite-real? positive?)
Returns an origin-centered camera with the pixel width, pixel height, and background of camera, but with visible world width frame-width. The Pict adapter uses this derived camera for frame-space measurement, local rendering, and frame-coordinate placement. The input camera’s center and visible world width are deliberately ignored.

procedure

(fixed-in-frame content    
  [#:camera camera    
  #:at position    
  #:rotation rotation    
  #:scale scale    
  #:opacity opacity])  fixed-in-frame-visual?
  content : visual?
  camera : camera? = default-camera
  position : (or/c vec2? false/c) = #f
  rotation : finite-real? = 0
  scale : scale-factor? = 1
  opacity : opacity? = 1
Wraps content as a frame-space overlay. The wrapper preserves (visual-id content) as its top-level identity. The content remains semantic model data and is rendered locally at the frame-space origin; its own geometry, rotation, scale, style, and opacity remain significant. The wrapper’s rotation, scale, and opacity are additional outer transforms.

The wrapper snapshots (camera-world-width camera). If position is #f, its initial frame position is (vec2- (visual-position content) (camera-center camera)). Therefore a Visual wrapped with the camera through which it is currently being viewed keeps the same pixel position and size at the moment it becomes fixed. An explicit position is interpreted directly in the captured origin-centered frame coordinate system.

Later world-camera pan and zoom do not change the overlay’s frame position or local rendering scale. Animated output pixel dimensions are not supported by the camera model, but a static rendering override with different pixel dimensions scales frame content with the output frame. Frame-space wrappers cannot be nested inside one another and must remain top-level scene Visuals. For a compound overlay, construct an ordinary group first and wrap the complete group.

procedure

(fixed-in-frame-visual? value)  boolean?

  value : any/c
Returns #t when value is a fixed-in-frame wrapper.

procedure

(fixed-in-frame-visual-content visual)  visual?

  visual : fixed-in-frame-visual?
Returns the semantic content stored by visual. The returned content is not a scene-state child and cannot be targeted directly while it is wrapped.

procedure

(camera-view [target]    
  #:id id    
  [#:targets targets]    
  #:camera camera    
  [#:frame-camera frame-camera    
  #:at position    
  #:width width    
  #:clip clip    
  #:opacity opacity])  camera-view-visual?
  target : (or/c false/c visual? symbol? visual-path?) = #f
  id : symbol?
  targets : 
(or/c false/c
      (and/c pair?
             (listof (or/c visual? symbol? visual-path?))))
   = #f
  camera : camera?
  frame-camera : camera? = default-camera
  position : vec2? = origin
  width : (and/c finite-real? positive?) = 3
  clip : (or/c 'rectangle 'rounded 'rounded-frame) = 'rectangle
  opacity : opacity? = 1
Creates a frame-fixed viewport onto live world-space Visuals. camera is the immutable orthographic camera used inside the inset. frame-camera supplies the captured origin-centered frame coordinate system for position and width, just as #:camera does for fixed-in-frame. The default position is the frame origin.

Supply either positional target or a nonempty #:targets list, not both. A target may be a Visual, top-level symbol, or built-in group/formula visual-path?. If both target arguments are omitted, the view uses every top-level world-space layer in ordinary drawing order. Frame-space overlays, including other views, are deliberately excluded from this all-layer form so an inset cannot recursively render itself. Each explicit target must resolve to world space.

At scene rendering time, selected Visuals are resolved against the same sampled state as ordinary Visuals and painted in order onto the complete inset camera canvas. Their complete world transforms and opacity are therefore synchronized between main view and inset. #:clip chooses a rectangle or rounded rectangle viewport border; 'rounded-frame is a compatibility alias for 'rounded. The viewport itself is an affine/opacity frame-space Visual and can be moved, rotated, scaled, faded, or structurally removed.

Unlike a fixed overlay, the viewport cannot be converted by visual->pict by itself because resolving its live targets needs a sampled scene state. Use complete scene rendering such as scene-state->pict or render-frames! instead.

procedure

(camera-view-visual? value)  boolean?

  value : any/c
Returns #t when value is a camera-view wrapper.

Compatibility accessor for a one-target view. It returns the normalized world target for a positional target, but #f for a multi-target or all-layer view. Use camera-view-visual-targets for the general selection.

procedure

(camera-view-visual-targets visual)

  (or/c false/c (listof (or/c symbol? visual-path?)))
  visual : camera-view-visual?
Returns the normalized explicit target list in declaration order. It returns #f when visual represents an all-world-layers view.

procedure

(camera-view-visual-camera visual)  camera?

  visual : camera-view-visual?
Returns the immutable world-space camera used to render the inset.

procedure

(camera-view-visual-with-camera visual    
  camera)  camera-view-visual?
  visual : camera-view-visual?
  camera : camera?
Returns visual with only its immutable secondary camera replaced. The helper preserves its identity, fixed frame pose, target selection, clip, and opacity. It is also the semantic operation used by sampled view-camera animations.

procedure

(camera-view-visual-width visual)

  (and/c finite-real? positive?)
  visual : camera-view-visual?
Returns the inset’s width in its captured frame coordinate system.

procedure

(camera-view-visual-clip visual)  (or/c 'rectangle 'rounded)

  visual : camera-view-visual?
Returns the normalized viewport clip shape. A constructor’s 'rounded-frame value is normalized to 'rounded.

procedure

(camera-view-pan-to view center)  camera-view-pan-to-request?

  view : (or/c symbol? camera-view-visual?)
  center : vec2?
Creates an absolute center transition for the named secondary camera. The ordinary scene camera is unaffected.

procedure

(camera-view-pan-to-request? value)  boolean?

  value : any/c
Returns #t when value is a request created by camera-view-pan-to.

procedure

(camera-view-pan-by view delta)  camera-view-pan-by-request?

  view : (or/c symbol? camera-view-visual?)
  delta : vec2?
Creates a clip-start-relative secondary-camera center transition.

procedure

(camera-view-pan-by-request? value)  boolean?

  value : any/c
Returns #t when value is a request created by camera-view-pan-by.

procedure

(camera-view-zoom-to view world-width)

  camera-view-zoom-to-request?
  view : (or/c symbol? camera-view-visual?)
  world-width : (and/c finite-real? positive?)
Creates an absolute secondary-camera visible-width transition. A smaller width appears more magnified.

procedure

(camera-view-zoom-to-request? value)  boolean?

  value : any/c
Returns #t when value is a request created by camera-view-zoom-to.

procedure

(camera-view-zoom-by view factor)  camera-view-zoom-by-request?

  view : (or/c symbol? camera-view-visual?)
  factor : (and/c finite-real? positive?)
Creates a relative magnification transition: a factor of 2 divides the clip-start secondary-camera width by two.

procedure

(camera-view-zoom-by-request? value)  boolean?

  value : any/c
Returns #t when value is a request created by camera-view-zoom-by.

procedure

(camera-view-follow view target)  camera-view-follow-request?

  view : (or/c symbol? camera-view-visual?)
  target : (or/c visual? symbol? visual-path?)
Creates one clip-local follow. At each sample the request first samples ordinary world motion, then reads target’s world-space reference position and updates the inset center to retain that target’s clip-start offset. This remains a random-access computation; it retains no previous frame. Follow may run with a view zoom, but conflicts with another view-center request for the same inset.

procedure

(camera-view-follow-request? value)  boolean?

  value : any/c
Returns #t when value is a request created by camera-view-follow.

procedure

(camera-view-fit view fit)  camera-view-fit-request?

  view : (or/c symbol? camera-view-visual?)
  fit : camera-fit-request?
Creates a joint center/width secondary-camera transition to a prior measured fit. Build it with camera-fit-layout-box, camera-fit-visuals, or camera-fit-scene using a camera with the inset’s pixel aspect ratio. The fit remains a snapshot: it is not remeasured while the clip runs.

procedure

(camera-view-fit-request? value)  boolean?

  value : any/c
Returns #t when value is a request created by camera-view-fit.

procedure

(callout content    
  target    
  [#:camera camera    
  #:at position    
  #:rotation rotation    
  #:scale scale    
  #:opacity opacity    
  #:target-anchor target-anchor    
  #:connector-stroke connector-stroke    
  #:connector-width connector-width])  callout-visual?
  content : visual?
  target : (or/c visual? symbol? visual-path? vec2?)
  camera : camera? = default-camera
  position : (or/c vec2? false/c) = #f
  rotation : finite-real? = 0
  scale : scale-factor? = 1
  opacity : opacity? = 1
  target-anchor : 
(or/c 'bottom-left 'bottom 'bottom-right
      'left 'center 'right
      'top-left 'top 'top-right)
   = 'center
  connector-stroke : any/c = "black"
  connector-width : (and/c finite-real? (>=/c 0)) = 2
Creates a fixed frame-space annotation with a leader line to a world-space target. The frame placement and outer transform use the same semantics as fixed-in-frame. A Visual target is stored by stable identity. A symbol is already a top-level target identity, and a nonempty visual-path? selects a built-in group/formula descendant. A vec2 is a fixed world-space point.

Visual-path targets are resolved against each sampled scene state when a complete scene is converted to a Pict. A nested result has every enclosing group/formula transform and opacity composed before its world position is read. This makes the connector follow ordinary movement of a target or its parent without adding observer state to the timeline. SCENE-AW derived targets are resolved from the same sampled scalar state. The resolved Visual must belong to world space. A missing target or a frame-space target raises an exception at scene rendering.

The connector is drawn beneath the annotation from the target’s current world pixel position to the edge of the complete annotation Pict box. For a Visual target, target-anchor selects the target’s live renderer-box center, edge, or corner; it is measured as each scene sample is rendered, so it follows target size changes as well as movement. A literal vec2 target accepts only the default 'center anchor. Its width is a cosmetic pixel width. A false connector-stroke or a zero connector-width suppresses the line. The callout’s outer opacity also applies to the connector.

visual->pict on a callout returns only its local annotation Pict, because resolving a symbolic connector target requires a complete sampled scene state. scene-state->pict and higher-level frame rendering include both the leader and the annotation.

procedure

(callout-visual? value)  boolean?

  value : any/c
Returns #t when value is a built-in callout Visual.

procedure

(callout-visual-content visual)  visual?

  visual : callout-visual?
Returns the semantic annotation content stored by visual.

procedure

(callout-visual-target visual)

  (or/c symbol? visual-path? vec2?)
  visual : callout-visual?
Returns the normalized callout target. Visual-valued constructor targets appear here as their stable symbol identity; an explicit nested path is preserved; fixed world points remain vec2 values.

procedure

(callout-visual-target-anchor visual)  symbol?

  visual : callout-visual?
Returns the selected live renderer-box anchor for a Visual target.

procedure

(callout-visual-connector-stroke visual)  any/c

  visual : callout-visual?
Returns the opaque connector stroke style stored by visual. A false value disables the connector during built-in scene rendering.

procedure

(callout-visual-connector-width visual)

  (and/c finite-real? (>=/c 0))
  visual : callout-visual?
Returns the cosmetic connector width in output pixels.

Renderer-aware relative layout recognizes frame-space coordinates. Layout of a single frame-space Visual uses its captured frame scale, so the result is independent of later world-camera pan or zoom. Pair and list layout operations may combine frame-space Visuals only when they have the same captured frame width. Mixing world and frame coordinate domains in one relative-layout calculation raises an exception. A callout layout box measures its fixed annotation Pict only; the cross-space connector is deliberately not part of frame-space layout.

World-camera operations deliberately exclude frame-space content: camera-fit-visuals rejects it, camera-fit-scene ignores it for implicit all-scene fitting and rejects it when explicitly selected, and camera-follow cannot follow a frame-space target.

17.6 Semantic Colors and Paints🔗

SCENE-AT introduces a small renderer-independent color representation for style interpolation. Existing Visual constructors still accept their historical color strings; semantic RGBA values are needed only when an animation is sampled in its interior or when callers choose to construct one explicitly.

struct

(struct rgba-color (red green blue alpha)
    #:transparent)
  red : (and/c finite-real? (>=/c 0) (<=/c 255))
  green : (and/c finite-real? (>=/c 0) (<=/c 255))
  blue : (and/c finite-real? (>=/c 0) (<=/c 255))
  alpha : opacity?
Represents one semantic sRGB color. Red, green, and blue are channel values from zero through 255; alpha is in the closed unit interval. The constructor rejects infinities, NaN values, and out-of-range components. The value has no dependency on racket/draw.

procedure

(rgb-color red green blue)  rgba-color?

  red : (and/c finite-real? (>=/c 0) (<=/c 255))
  green : (and/c finite-real? (>=/c 0) (<=/c 255))
  blue : (and/c finite-real? (>=/c 0) (<=/c 255))
Constructs an opaque rgba-color whose alpha component is one.

procedure

(color-spec? value)  boolean?

  value : any/c
Returns #t for an rgba-color or a supported textual color. Text accepts X11-style names from Racket’s drawing color family case-insensitively (common spaces, hyphens, and underscores are ignored), #RGB, #RGBA, #RRGGBB, and #RRGGBBAA. transparent is the zero-alpha black semantic color.

procedure

(color-spec->rgba-color value [who])  rgba-color?

  value : any/c
  who : symbol? = 'color-spec->rgba-color
Resolves value to semantic RGBA channels. An existing rgba-color is returned unchanged. Unsupported strings and other values raise an argument error attributed to who.

procedure

(rgba-color-lerp from to progress)  rgba-color?

  from : rgba-color?
  to : rgba-color?
  progress : (and/c finite-real? (>=/c 0) (<=/c 1))
Interpolates red, green, blue, and alpha componentwise in sRGB value space. This is intentionally a deterministic semantic interpolation rather than a color-managed or perceptual color-space conversion.

17.6.1 Semantic fill paints🔗

SCENE-EC extends a fill from a solid color-spec? to an immutable semantic paint?. These values contain neither a drawing-context brush nor a bitmap. The Pict/racket-draw adapter creates a native vector gradient brush only when it renders a supported Visual, so a paint remains ordinary scene data through sampling, affine transforms, and clipping.

struct

(struct paint-stop (offset color)
    #:transparent)
  offset : (and/c finite-real? (>=/c 0) (<=/c 1))
  color : color-spec?
One ordered gradient stop. Stop offsets are positions along the unit gradient range. A gradient requires at least two stops in nondecreasing offset order.

struct

(struct linear-gradient-paint (start end stops)
    #:transparent)
  start : vec2?
  end : vec2?
  stops : (listof paint-stop?)
An immutable local-coordinate linear gradient description. Use linear-gradient rather than constructing the structure directly in ordinary code.

procedure

(linear-gradient start end stops)  linear-gradient-paint?

  start : vec2?
  end : vec2?
  stops : (and/c list? pair?)
Creates a linear gradient from start to end. Both points are in the receiving Visual’s local coordinate system, rather than video pixels or world coordinates. The same gradient therefore follows the Visual when it is moved, rotated, or scaled.

struct

(struct radial-gradient-paint (focal-center
    focal-radius
    center
    radius
    stops)
    #:transparent)
  focal-center : vec2?
  focal-radius : nonnegative-real?
  center : vec2?
  radius : nonnegative-real?
  stops : (listof paint-stop?)
An immutable two-circle radial gradient description. A zero focal radius gives the ordinary point-focused form.

procedure

(radial-gradient center 
  radius 
  stops 
  [#:focal-center focal-center 
  #:focal-radius focal-radius]) 
  radial-gradient-paint?
  center : vec2?
  radius : nonnegative-real?
  stops : (and/c list? pair?)
  focal-center : vec2? = center
  focal-radius : nonnegative-real? = 0
Creates a radial gradient. Centre, focal centre, and radii are local to the receiving Visual. The focal keywords select a general focal circle; by default it is the zero-radius point at center.

struct

(struct checker-pattern-paint (first second cell-size)
    #:transparent)
  first : color-spec?
  second : color-spec?
  cell-size : positive-real?
An immutable two-colour checker pattern description.

procedure

(checker-pattern first 
  second 
  [#:cell-size cell-size]) 
  checker-pattern-paint?
  first : color-spec?
  second : color-spec?
  cell-size : positive-real? = 1
Creates a local square checker pattern whose individual cells have side length cell-size.

procedure

(paint? value)  boolean?

  value : any/c
Returns #t for a supported solid color-spec?, a linear or radial gradient, or a checker pattern. #f is the existing absent-fill sentinel and is deliberately not a paint.

procedure

(paint-lerp from to progress)  paint?

  from : paint?
  to : paint?
  progress : (and/c finite-real? (>=/c 0) (<=/c 1))
Interpolates compatible paints, retaining from and to exactly at progress zero and one. Solid colours interpolate in sRGB value space. Gradient endpoints, radii, stop offsets, and stop colours interpolate componentwise, but corresponding gradients must have the same number of stops. Checker colours and cell size interpolate similarly.

Paint kinds must agree. In particular, this function does not invent a meaningless halfway value between a solid fill and a gradient; use a deliberate cross-fade of two Visuals for that visual change.

The built-in Pict renderer supports paints on the fills of paths, circles, and rectangles; a structured paint on a circle or rectangle is sent through its vector path equivalent. Strokes remain solid colours. Gradients are native vector brushes. The checker implementation is a deterministic device-aligned stipple and does not yet follow an enclosing affine transform. Formula, SVG, image, and custom renderer paint support remains renderer-specific.