58.7 Matrices, Tables, and Vector Diagrams
Use matrix or table for rows of Visual entries. Their path helpers name cells without requiring a constructed matrix or table. Number planes and vector diagrams are also ordinary group trees; their stable child paths identify the components to animate.
See also Visual Protocols and Groups, Arrows and Coordinate Axes.
58.7.1 Matrices and Tables
matrix and table return ordinary immutable group-visual? values. Their rows and cells are regular nested groups, not a separate rendering or animation object. Existing path-addressed operations therefore work directly: (indicate (matrix-entry-path 'A 1 2)), move-to, follow-anchor, and transform-from-copy need no matrix/table variants.
Both constructors take a nonempty rectangular list of nonempty rows. Each entry must be an affine Visual. The constructor re-bases every entry at its cell centre, preserving identity, rotation, scale, opacity, style, and children but intentionally replacing its supplied reference position. Width and height can be one shared measure, one explicit per-axis list, or an 'auto construction-time measurement. The result is still an ordinary immutable group with no renderer dependency after construction.
procedure
(matrix rows #:id id [ #:center center #:rotation rotation #:scale scale #:opacity opacity #:entry-width entry-width #:entry-height entry-height #:entry-padding entry-padding #:column-gap column-gap #:row-gap row-gap #:brackets? brackets? #:bracket-width bracket-width #:bracket-gap bracket-gap #:stroke stroke #:stroke-width stroke-width #:typography typography]) → group-visual? rows : (listof (listof (and/c visual? affine-visual?))) id : symbol? center : vec2? = origin rotation : finite-real? = 0 scale : scale-factor? = 1 opacity : opacity? = 1
entry-width :
(or/c 'auto (and/c finite-real? positive?) (listof (and/c finite-real? positive?))) = 1
entry-height :
(or/c 'auto (and/c finite-real? positive?) (listof (and/c finite-real? positive?))) = 1 entry-padding : (and/c finite-real? (>=/c 0)) = 1/5 column-gap : (and/c finite-real? (>=/c 0)) = 1/4 row-gap : (and/c finite-real? (>=/c 0)) = 1/4 brackets? : boolean? = #t bracket-width : (and/c finite-real? positive?) = 1/5 bracket-gap : (and/c finite-real? (>=/c 0)) = 1/10 stroke : any/c = "black" stroke-width : (and/c finite-real? (>=/c 0)) = 2 typography : (or/c false/c typography-theme?) = #f
When brackets? is true, the matrix has ordinary open path children named 'left-bracket and 'right-bracket. They use square brackets, stroke, and stroke-width.
For either entry dimension, a positive scalar supplies one shared cell extent; a list supplies one extent per column or row; and 'auto measures each entry with the active default Pict renderer and selects the largest visible-box extent in that column or row. entry-padding is added on both sides of each auto-sized extent. This is a snapshot: later text/formula changes do not reflow a constructed matrix.
When entries include semantic text, pass #:typography to choose the snapshot used for 'auto measurement. Changing a project’s typography later changes that text’s rendering, but does not resize an already constructed matrix.
procedure
(matrix-row-id row) → symbol?
row : exact-positive-integer?
procedure
(matrix-column-id column) → symbol?
column : exact-positive-integer?
procedure
(matrix-row-path matrix-id row) → visual-path?
matrix-id : symbol? row : exact-positive-integer?
procedure
(matrix-entry-path matrix-id row column) → visual-path?
matrix-id : symbol? row : exact-positive-integer? column : exact-positive-integer?
Paths can be prepared without constructing or measuring a matrix.
> (matrix-row-path 'A 2) '(A row-2)
> (matrix-entry-path 'A 2 1) '(A row-2 col-1)
procedure
(matrix-bracket-path matrix-id side) → visual-path?
matrix-id : symbol? side : (or/c 'left 'right)
procedure
(table rows #:id id [ #:center center #:rotation rotation #:scale scale #:opacity opacity #:cell-width cell-width #:cell-height cell-height #:cell-padding cell-padding #:column-gap column-gap #:row-gap row-gap #:stroke stroke #:stroke-width stroke-width #:typography typography]) → group-visual? rows : (listof (listof (and/c visual? affine-visual?))) id : symbol? center : vec2? = origin rotation : finite-real? = 0 scale : scale-factor? = 1 opacity : opacity? = 1
cell-width :
(or/c 'auto (and/c finite-real? positive?) (listof (and/c finite-real? positive?))) = 1
cell-height :
(or/c 'auto (and/c finite-real? positive?) (listof (and/c finite-real? positive?))) = 3/4 cell-padding : (and/c finite-real? (>=/c 0)) = 1/5 column-gap : (and/c finite-real? (>=/c 0)) = 0 row-gap : (and/c finite-real? (>=/c 0)) = 0 stroke : any/c = "black" stroke-width : (and/c finite-real? (>=/c 0)) = 2 typography : (or/c false/c typography-theme?) = #f
The cell-size arguments follow the same scalar/list/'auto policy as matrix. Auto measurement adds cell-padding on all sides and does not remeasure after construction. Pass #:typography when automatic cell sizing measures semantic text.
procedure
(table-row-id row) → symbol?
row : exact-positive-integer?
procedure
(table-column-id column) → symbol?
column : exact-positive-integer?
procedure
(table-row-path table-id row) → visual-path?
table-id : symbol? row : exact-positive-integer?
procedure
(table-cell-path table-id row column) → visual-path?
table-id : symbol? row : exact-positive-integer? column : exact-positive-integer?
58.7.2 Linear-Algebra Diagrams
Linear-algebra diagram constructors create small, conventional diagrams without a mutable diagram class. Each constructor below returns an ordinary immutable Visual or group-visual?. Its children retain their normal nested paths, so existing scene operations work directly. In particular, apply-matrix can map one complete top-level diagram coherently.
procedure
(number-plane #:id id [ #:x-range x-range #:y-range y-range #:x-length x-length #:y-length y-length #:center center #:rotation rotation #:scale scale #:grid? grid? #:labels? labels? #:grid-stroke grid-stroke #:grid-stroke-width grid-stroke-width #:axes-stroke axes-stroke #:axes-stroke-width axes-stroke-width #:label-font-size label-font-size #:label-color label-color]) → group-visual? id : symbol? x-range : axis-range? = (axis-range -4 4 1) y-range : axis-range? = (axis-range -3 3 1) x-length : (and/c finite-real? positive?) = 8 y-length : (and/c finite-real? positive?) = 6 center : vec2? = origin rotation : finite-real? = 0 scale : scale-factor? = 1 grid? : boolean? = #t labels? : boolean? = #f grid-stroke : any/c = "lightsteelblue" grid-stroke-width : (and/c finite-real? (>=/c 0)) = 1 axes-stroke : any/c = "navy" axes-stroke-width : (and/c finite-real? (>=/c 0)) = 2 label-font-size : (and/c finite-real? positive?) = 1/4 label-color : any/c = "navy"
The stable direct-child paths are produced by number-plane-grid-path, number-plane-axes-path, and number-plane-labels-path. When #:grid? or #:labels? is false, its corresponding path intentionally does not resolve.
procedure
(number-plane-grid-path plane-id) → visual-path?
plane-id : symbol?
procedure
(number-plane-axes-path plane-id) → visual-path?
plane-id : symbol?
procedure
(number-plane-labels-path plane-id) → visual-path?
plane-id : symbol?
procedure
(vector-arrow endpoint [ #:start start] #:id id [ #:stroke stroke #:stroke-width stroke-width #:tip-length tip-length #:tip-width tip-width]) → arrow-visual? endpoint : vec2? start : vec2? = origin id : symbol? stroke : any/c = "darkorchid" stroke-width : (and/c finite-real? (>=/c 0)) = 3 tip-length : (and/c finite-real? positive?) = 3/10 tip-width : (and/c finite-real? positive?) = 1/4
procedure
(vector-coordinates arrow) → vec2?
arrow : arrow-visual?
procedure
(vector-label arrow #:id id [ #:text text #:offset offset #:font-size font-size #:color color]) → text-visual? arrow : arrow-visual? id : symbol? text : (or/c false/c string?) = #f offset : vec2? = (vec2 1/5 1/5) font-size : (and/c finite-real? positive?) = 1/4 color : any/c = "darkorchid"
procedure
(basis-vectors #:id id [ #:origin origin #:e1 e1 #:e2 e2 #:e1-color e1-color #:e2-color e2-color #:stroke-width stroke-width]) → group-visual? id : symbol? origin : vec2? = origin e1 : vec2? = (vec2 1 0) e2 : vec2? = (vec2 0 1) e1-color : any/c = "crimson" e2-color : any/c = "forestgreen" stroke-width : (and/c finite-real? (>=/c 0)) = 3
procedure
(linear-transformation-diagram #:id id [ #:x-range x-range #:y-range y-range #:vector-end vector-end #:unit-square? unit-square? #:grid? grid?]) → group-visual? id : symbol? x-range : axis-range? = (axis-range -4 4 1) y-range : axis-range? = (axis-range -3 3 1) vector-end : vec2? = (vec2 3 2) unit-square? : boolean? = #t grid? : boolean? = #t
For example, this keeps all geometric parts together while a title remains fixed outside the mapped group:
(define diagram (linear-transformation-diagram #:id 'diagram #:vector-end (vec2 3 2))) (scene-play (scene-add (make-scene) diagram) (apply-matrix 'diagram (linear2 1 1 0 1)) #:duration 3)