58.1 Visual Protocols and Groups
Use the basic protocol for identity and reference position. The optional protocols describe the updates a Visual supports; group supplies ordered composition rather than another leaf renderer. Positions are expressed in the containing coordinate system, so a child’s position is local to its group.
See also Shapes and Path Visuals, Relations and Live Layout.
58.1.1 The Basic Visual Protocol
procedure
(visual-path? value) → boolean?
value : any/c
procedure
(visual-target-path target) → visual-path?
target : (or/c visual? symbol? visual-path?)
A symbol becomes a one-element path; an existing nested path is retained.
> (visual-target-path 'marker) '(marker)
> (visual-target-path '(scatter marker)) '(scatter marker)
generic interface
A custom Visual implementation must always return a symbol. Immutable update methods must preserve the symbol.
procedure
(visual-position visual) → vec2?
visual : visual?
procedure
(visual-with-position visual position) → visual?
visual : visual? position : vec2?
Moving a model value preserves its identity without changing the original.
> (define dot (circle #:id 'dot)) > (define shifted-dot (visual-with-position dot (vec2 2 1))) > (visual-id shifted-dot) 'dot
> (visual-position dot) (vec2 0 0)
A minimal position-only Visual can be defined as follows:
(struct marker (id position) #:transparent #:methods gen:visual [(define (visual-id value) (marker-id value)) (define (visual-position value) (marker-position value)) (define (visual-with-position value position) (struct-copy marker value [position position]))])
Such a Visual can use move-to, but it cannot use rotation or scale animations.
58.1.2 Whole-Visual Affine Maps
procedure
(affine-map content map) → affine-map-visual?
content : visual? map : affine2?
The wrapper is itself an affine-visual?. It retains a canonical local copy of the subtree plus its full local-to-parent map, which lets ordinary group composition preserve the map and lets descendants remain addressable. This is the semantic bridge used by nested apply-affine requests.
procedure
(affine-map-visual? value) → boolean?
value : any/c
procedure
(affine-map-visual-content visual) → visual?
visual : affine-map-visual?
procedure
(affine-map-visual-map visual) → affine2?
visual : affine-map-visual?
58.1.3 The Affine-Visual Protocol
generic interface
procedure
(affine-visual? value) → boolean?
value : any/c
procedure
(visual-transform visual) → affine-transform?
visual : affine-visual?
procedure
(visual-with-transform visual transform) → affine-visual?
visual : affine-visual? transform : affine-transform?
procedure
(visual-rotation visual) → finite-real?
visual : affine-visual?
procedure
(visual-scale visual) → vec2?
visual : affine-visual?
procedure
(visual-with-rotation visual rotation) → affine-visual?
visual : affine-visual? rotation : finite-real?
procedure
(visual-with-scale visual scale) → affine-visual?
visual : affine-visual? scale : scale-factor?
A built-in group or formula assembly accepts only a uniform scale, so its x and y components must be equal. Other built-in affine Visuals, including arrows and axes, accept non-uniform scale.
58.1.4 The Opacity-Visual Protocol
generic interface
Opacity is semantic model data. Renderers should draw the Visual normally. The Pict adapter applies global opacity after it selects and runs a renderer.
procedure
(opacity-visual? value) → boolean?
value : any/c
procedure
(visual-opacity visual) → opacity?
visual : opacity-visual?
procedure
(visual-with-opacity visual opacity) → opacity-visual?
visual : opacity-visual? opacity : opacity?
The built-in circle, rectangle, path, arrow, axes, plain-text, formula, formula-assembly, and group Visuals implement this protocol.
A position-only custom Visual can implement opacity as follows:
(struct marker (id position opacity) #:transparent #:methods gen:visual [(define (visual-id value) (marker-id value)) (define (visual-position value) (marker-position value)) (define (visual-with-position value position) (struct-copy marker value [position position]))] #:methods gen:opacity-visual [(define (visual-opacity value) (marker-opacity value)) (define (visual-with-opacity value opacity) (struct-copy marker value [opacity opacity]))])
58.1.5 The Stroke-Width-Visual Protocol
procedure
(stroke-width? value) → boolean?
value : any/c
generic interface
The protocol is renderer-independent model data. Built-in renderers read the stored widths of the Visual types they support; third-party renderers decide how to interpret the width exposed by their own Visual implementations.
procedure
(stroke-width-visual? value) → boolean?
value : any/c
procedure
(visual-stroke-width visual) → (and/c finite-real? (>=/c 0))
visual : stroke-width-visual?
procedure
(visual-with-stroke-width visual stroke-width) → stroke-width-visual? visual : stroke-width-visual? stroke-width : (and/c finite-real? (>=/c 0))
The built-in circle, rectangle, path, arrow, axes, number-line, and point-marker Visuals implement this protocol. Coordinate plots and filled areas that are themselves path Visuals participate without a separate plot-animation mechanism. A scatter-plot result is instead a top-level group; its nested point-marker children are not independent scene-state animation targets, so the scatter group does not implement this protocol. A callout’s callout-visual-connector-width is likewise separate frame-space connector style and is not controlled by stroke-width-to.
A position-only custom Visual can opt in independently of affine transforms and opacity:
(struct width-marker (id position stroke-width) #:transparent #:methods gen:visual [(define (visual-id value) (width-marker-id value)) (define (visual-position value) (width-marker-position value)) (define (visual-with-position value position) (struct-copy width-marker value [position position]))] #:methods gen:stroke-width-visual [(define (visual-stroke-width value) (width-marker-stroke-width value)) (define (visual-with-stroke-width value stroke-width) (struct-copy width-marker value [stroke-width stroke-width]))])
58.1.6 Fill-Color and Stroke-Color Visual Protocols
generic interface
procedure
(fill-color-visual? value) → boolean?
value : any/c
procedure
(visual-fill-color visual) → any/c
visual : fill-color-visual?
procedure
(visual-with-fill-color visual color) → fill-color-visual?
visual : fill-color-visual? color : paint?
generic interface
procedure
(stroke-color-visual? value) → boolean?
value : any/c
procedure
(visual-stroke-color visual) → any/c
visual : stroke-color-visual?
procedure
(visual-with-stroke-color visual color) → stroke-color-visual?
visual : stroke-color-visual? color : color-spec?
Circles, rectangles, paths, and point markers implement both protocols. Arrows, axes, and number lines implement the stroke-color protocol. A scatter-plot result is a group whose nested marker children are not independent scene-state targets, so the group itself implements neither color protocol. Callout callout-visual-connector-stroke is separate frame-space connector style and is not controlled by stroke-color-to. The protocols are independent of affine transforms, opacity, and stroke width, so third-party Visuals may opt into either one separately.
58.1.7 Group Visuals
A group is a semantic composite. Its children are stored as ordinary Visual values, not as Picts. The child list is significant back-to-front order. Child positions are local to the group anchor.
All children must implement both gen:visual and gen:affine-visual. Circles, rectangles, paths, function graphs, parametric curves, data plots, arrows, axes, plain text, formulas, and groups all satisfy this requirement. A child may itself be a group. Direct siblings must have distinct identities, and a group identity may not occur anywhere below that group. The same child identity may be reused in separate nested branches; its complete Visual path identifies it unambiguously. A custom affine Visual is treated as one leaf because there is no public protocol for inspecting children hidden inside it.
procedure
(group children #:id id [ #:center center #:rotation rotation #:scale scale #:opacity opacity]) → group-visual? children : (listof (and/c visual? affine-visual?)) id : symbol? center : vec2? = origin rotation : finite-real? = 0 scale : scale-factor? = 1 opacity : opacity? = 1
The center value places the group anchor in its containing coordinate system. At the top level this is a world-space point. In a parent group it is a local point. Each child’s existing reference position is interpreted in the group’s local coordinates.
The group scale may be a positive finite scalar or a positive vec2, but its normalized x and y components must be equal. This uniform-scale restriction lets parent transforms compose exactly with rotated children without introducing shear. The group may be rotated by any finite angle.
The group opacity is applied to the complete composed result. Child opacity is applied first, so opacity is inherited multiplicatively through nested groups.
The constructor rejects a non-affine child, a nonsymbol child identity, a repeated identity anywhere in the built-in group tree, or a descendant whose identity equals id. For a custom affine child, its reported position must agree with the translation in its reported affine transform.
Nested children are addressed by nonempty paths such as '(parent child) for scene-state lookup and compatible animation requests. Their identity remains local to the containing group, so a bare child symbol is not a top-level scene identity.
procedure
(group-visual? value) → boolean?
value : any/c
procedure
(group-visual-children group)
→ (listof (and/c visual? affine-visual?)) group : group-visual?
Child order is available directly from the semantic group.
> (define pair (group (list (circle #:id 'left) (rectangle #:id 'right)) #:id 'pair)) > (map visual-id (group-visual-children pair)) '(left right)
procedure
(group-visual-with-children group children) → group-visual?
group : group-visual? children : (listof (and/c visual? affine-visual?))