58.17 Relations and Live Layout
A relation records dependencies and recomputes concrete geometry from a sampled scene state. Choose semantic resolution for model coordinates and layout resolution for renderer-measured anchors or boxes. Following helpers are relations too; they do not require a mutable frame updater.
See also Visual Protocols and Groups, Live Endpoints and Mathematical Annotations.
58.17.1 First-Class Relation Visuals
A relation is an immutable Visual whose concrete geometry is recomputed from explicit dependencies in each sampled scene state. No relation uses a mutable updater or needs the preceding frame.
procedure
(relation-visual template [ #:depends-on dependencies #:phase phase #:structure structure #:space space #:cache-key cache-key] resolver) → relation-visual? template : visual? dependencies : (listof relation-dependency?) = '() phase : (or/c 'semantic 'layout) = 'semantic structure : (or/c 'root-only 'fixed) = 'root-only space : (or/c 'world 'local) = 'world cache-key : any/c = #f resolver : (-> relation-context? visual? visual?)
The ordinary movement, rotation, scale, opacity, fill, stroke, and stroke-width controls form an outer envelope. They are applied after the resolver has computed the current geometry, so a relation may be animated concurrently with its own changing dependencies. A requested style must be supported by both the template and the concrete result. A 'fixed relation preserves the template’s complete child-ID tree and may expose nested paths; a 'root-only relation deliberately exposes only its root ID.
A 'semantic relation is resolved from model data. A 'layout relation may use relation-context-anchor-ref, relation-context-layout-box, or selection boxes after the active renderer has measured its targets. Layout relations are currently top-level only, and their measurements are complete Pict boxes rather than tight visible outlines.
procedure
(relation-context-layout-box context visual) → layout-box? context : relation-context? visual : visual?
procedure
(relation-visual? value) → boolean?
value : any/c
procedure
(relation-visual-dependencies relation)
→ (listof relation-dependency?) relation : relation-visual?
procedure
(relation-visual-cacheability relation)
→ (or/c 'serializable 'explicit-key 'disabled) relation : relation-visual?
procedure
(relation-dependency? value) → boolean?
value : any/c
procedure
(relation-context? value) → boolean?
value : any/c
procedure
(relation-context-anchor-ref context target anchor) → vec2? context : relation-context? target : (or/c visual? symbol? visual-path?) anchor : symbol?
procedure
(value-dependency target) → relation-dependency?
target : (or/c symbol? scene-parameter?)
procedure
(visual-dependency target) → relation-dependency?
target : (or/c visual? symbol? visual-path?)
procedure
(anchor-dependency target anchor) → relation-dependency?
target : (or/c visual? symbol? visual-path?) anchor : symbol?
procedure
(selection-dependency selection) → relation-dependency?
selection : visual-selection?
procedure
(scene-validate-relations state) → immutable-hash?
state : scene-state?
procedure
(scene-relation-report state [target]) → any/c
state : scene-state? target : (or/c #f visual? symbol? visual-path?) = #f
procedure
(scene-relation-sample-report state [target]) → any/c
state : scene-state? target : (or/c #f visual? symbol? visual-path?) = #f
For a 'layout relation those fields are #f. Determining its actual reads requires the active renderer’s measured layout boxes, which this headless report intentionally does not invent.
Built-in line-between, arrow-between, ray-from, parameter-display, and follow-anchor use serializable relation specifications. The live angle, brace, and curved-arrow constructors use generic relations because their builder procedure is author-specific; therefore they deliberately do not claim automatic persistent-cache reuse.
procedure
(follow-anchor content target [ #:offset offset #:target-anchor target-anchor #:self-anchor self-anchor]) → relation-visual? content : visual? target : (or/c visual? symbol? visual-path?) offset : vec2? = origin
target-anchor :
(or/c 'bottom-left 'bottom 'bottom-right 'left 'center 'right 'top-left 'top 'top-right) = 'center
self-anchor :
(or/c 'bottom-left 'bottom 'bottom-right 'left 'center 'right 'top-left 'top 'top-right) = 'center
The content must be a concrete, non-frame-space Visual, and content and target must have distinct identities. Attachments may be animated through the normal relation envelope. One attachment may target another when the resulting relation graph is acyclic; they neither avoid other labels nor inherit target rotation. Layout attachments are top-level and renderer-dependent; a semantic centre attachment can be queried directly from a sampled scene state.
An attachment is a relation with an inspectable built-in specification.
> (define attachment (follow-anchor (circle #:id 'follower) 'target #:offset (vec2 1 0))) > (relation-visual? attachment) #t
> (relation-visual-cacheability attachment) 'serializable
58.17.1.1 Acyclic Live Layout
The live-layout procedures give renderer-aware attachments concise relationship names. Their relation graph is resolved from a concrete target outward at each render. A direct or indirect cycle raises an exception; no prior frame is consulted.
procedure
(follow-above content target [#:gap gap]) → relation-visual?
content : visual? target : (or/c visual? symbol? visual-path?) gap : (and/c finite-real? (>=/c 0)) = 0
procedure
(follow-below content target [#:gap gap]) → relation-visual?
content : visual? target : (or/c visual? symbol? visual-path?) gap : (and/c finite-real? (>=/c 0)) = 0
procedure
(follow-left-of content target [#:gap gap]) → relation-visual?
content : visual? target : (or/c visual? symbol? visual-path?) gap : (and/c finite-real? (>=/c 0)) = 0
procedure
(follow-right-of content target [#:gap gap]) → relation-visual?
content : visual? target : (or/c visual? symbol? visual-path?) gap : (and/c finite-real? (>=/c 0)) = 0