Pose substrate -- the recursive plain-map type that is the library's spec vocabulary. This namespace holds the pure tree operations (resolve, layout, shared-scale injection) and the leaf->draft emitter that feeds plan.clj.
Shape of a pose: {:data ? dataset (inherited from ancestor if absent) :mapping ? aesthetic mappings (merges with ancestors) :layers ? layers at this level (accumulate into leaves) :poses ? sub-poses; absence = leaf :layout ? {:direction :horizontal|:vertical :weights [pos-num ...]} :opts ? plot options (inheritable) :share-scales ? #{:x :y} for composites}
Pose substrate -- the recursive plain-map type that is the
library's spec vocabulary. This namespace holds the pure tree
operations (resolve, layout, shared-scale injection) and the
leaf->draft emitter that feeds plan.clj.
Shape of a pose:
{:data ? dataset (inherited from ancestor if absent)
:mapping ? aesthetic mappings (merges with ancestors)
:layers ? layers at this level (accumulate into leaves)
:poses ? sub-poses; absence = leaf
:layout ? {:direction :horizontal|:vertical
:weights [pos-num ...]}
:opts ? plot options (inheritable)
:share-scales ? #{:x :y} for composites}(composite? f)A composite pose has at least one sub-pose.
A composite pose has at least one sub-pose.
(compute-layout pose rect)(compute-layout pose [x y w h] path)Walk the pose tree and assign a pixel rectangle to each leaf. Returns a map of path -> [x y w h].
Composite :layout is {:direction :horizontal|:vertical|:matrix :weights [pos-num ...]}. Defaults: :direction :horizontal :weights (repeat n 1) (equal share)
Matrix layout (:direction :matrix) places leaves on a grid
derived from their :x / :y mappings -- distinct x-cols become
grid columns, distinct y-cols become grid rows, leaves land at
their (x, y) intersection cell. Duplicate (x, y) pairs stack
into new rows in DFS order. Empty cells get no entry. See
matrix-axes for the full algorithm and the corresponding
strip-label derivation. :weights are ignored under :matrix.
Rectangle arithmetic is in doubles; callers that need integer pixels should coerce at the render boundary (see pj/plot's width/height coercion).
Walk the pose tree and assign a pixel rectangle to each leaf.
Returns a map of path -> [x y w h].
Composite :layout is {:direction :horizontal|:vertical|:matrix
:weights [pos-num ...]}. Defaults:
:direction :horizontal
:weights (repeat n 1) (equal share)
Matrix layout (`:direction :matrix`) places leaves on a grid
derived from their :x / :y mappings -- distinct x-cols become
grid columns, distinct y-cols become grid rows, leaves land at
their (x, y) intersection cell. Duplicate (x, y) pairs stack
into new rows in DFS order. Empty cells get no entry. See
`matrix-axes` for the full algorithm and the corresponding
strip-label derivation. :weights are ignored under :matrix.
Rectangle arithmetic is in doubles; callers that need integer
pixels should coerce at the render boundary (see pj/plot's
width/height coercion).(inject-shared-scales pose)(inject-shared-scales pose inherited-domains inherited-mapping inherited-data)Walk a pose tree. For each composite with :share-scales, compute a union domain per (axis, effective-column) bucket across descendant leaves, and stamp those domains onto matching leaves' :opts as :x-scale-domain / :y-scale-domain. Returns a new tree.
:share-scales may live in (:opts pose) (the canonical location;
set via pj/options or pj/arrange) or directly at the top of the
pose (legacy location for hand-built composites). The :opts entry
wins if both are present.
inherited-domains carries {axis {col-ref [lo hi]}} down the
tree. inherited-mapping carries the ancestor-merged mapping so a
leaf can resolve its effective axis column from (inherited + own +
layer) when deciding which bucket to claim. inherited-data is
the nearest-ancestor dataset, threaded through so a leaf can
predict whether its layers' y axis is stat-driven (count/density)
and skip the shared y-domain stamp on such leaves -- e.g., the
diagonal histogram cells of a SPLOM.
Walk a pose tree. For each composite with :share-scales, compute a
union domain per (axis, effective-column) bucket across descendant
leaves, and stamp those domains onto matching leaves' :opts as
:x-scale-domain / :y-scale-domain. Returns a new tree.
:share-scales may live in `(:opts pose)` (the canonical location;
set via pj/options or pj/arrange) or directly at the top of the
pose (legacy location for hand-built composites). The :opts entry
wins if both are present.
`inherited-domains` carries `{axis {col-ref [lo hi]}}` down the
tree. `inherited-mapping` carries the ancestor-merged mapping so a
leaf can resolve its effective axis column from (inherited + own +
layer) when deciding which bucket to claim. `inherited-data` is
the nearest-ancestor dataset, threaded through so a leaf can
predict whether its layers' y axis is stat-driven (count/density)
and skip the shared y-domain stamp on such leaves -- e.g., the
diagonal histogram cells of a SPLOM.(last-leaf-path pose)Return the path vector of the last leaf visited in left-to-right depth-first order. Nil if the pose is itself a leaf with no path context (the caller is the root leaf).
Return the path vector of the last leaf visited in left-to-right depth-first order. Nil if the pose is itself a leaf with no path context (the caller is the root leaf).
(last-matching-leaf-path pose position-mapping)Walk pose in left-to-right DFS order. Return the :path of the
last leaf whose effective :x and :y (after ancestor-merge of
:mapping) match position-mapping. Matching is strict equality:
:x and "x" are different column references. Returns nil if
no leaf matches.
position-mapping may carry either or both of :x and :y; a
nil value matches a leaf whose effective mapping has no entry
for that axis. Matching is against resolved positional mappings
only -- a bare leaf (no :x/:y) matches a bare position
mapping.
Walk `pose` in left-to-right DFS order. Return the `:path` of the last leaf whose effective `:x` and `:y` (after ancestor-merge of `:mapping`) match `position-mapping`. Matching is strict equality: `:x` and `"x"` are different column references. Returns `nil` if no leaf matches. `position-mapping` may carry either or both of `:x` and `:y`; a `nil` value matches a leaf whose effective mapping has no entry for that axis. Matching is against resolved positional mappings only -- a bare leaf (no `:x`/`:y`) matches a bare position mapping.
(leaf->draft leaf)Emit a draft vector from a leaf pose. A draft has one entry per applicable layer; each entry is a flat map carrying the merged aesthetic mapping (pose < layer-type-info < layer), the layer's :stat/:position/:mark as first-class siblings, and plot-level :x-scale/:y-scale/:coord stamped from :opts.
If the leaf's :opts carry :facet-col or :facet-row, the draft is multiplied over distinct facet values. Each variant carries a filtered :data plus :facet-col / :facet-row labels that plan.clj detects to build the facet grid.
The leaf's :opts is passed through to plan/draft->plan; in particular the compositor uses :suppress-legend on grid cells.
An empty :layers vector yields one {:mark :infer ...} placeholder so downstream inference can still choose a layer type from the data.
Data precedence: layer :data > leaf :data.
Every emitted draft carries :__panel-idx 0 because a single leaf is a single panel; plan.clj uses the key to group layers by panel, and a leaf has no sub-panel structure.
Emit a draft vector from a leaf pose. A draft has one entry per
applicable layer; each entry is a flat map carrying the merged
aesthetic mapping (pose < layer-type-info < layer), the layer's
:stat/:position/:mark as first-class siblings, and plot-level
:x-scale/:y-scale/:coord stamped from :opts.
If the leaf's :opts carry :facet-col or :facet-row, the draft is
multiplied over distinct facet values. Each variant carries a
filtered :data plus :facet-col / :facet-row labels that plan.clj
detects to build the facet grid.
The leaf's :opts is passed through to plan/draft->plan; in
particular the compositor uses :suppress-legend on grid cells.
An empty :layers vector yields one {:mark :infer ...} placeholder so
downstream inference can still choose a layer type from the data.
Data precedence: layer :data > leaf :data.
Every emitted draft carries :__panel-idx 0 because a single leaf is
a single panel; plan.clj uses the key to group layers by panel, and
a leaf has no sub-panel structure.(leaf-at pose path)Fetch the leaf at path in pose. Returns nil if the path does
not land on a leaf.
Fetch the leaf at `path` in `pose`. Returns nil if the path does not land on a leaf.
(leaf? f)A leaf pose has no sub-poses. (An empty :poses vector also counts as leaf because it has nothing to tile.)
A leaf pose has no sub-poses. (An empty :poses vector also counts as leaf because it has nothing to tile.)
(matrix-axes composite)For a composite whose layout is :matrix, walk its leaves in
DFS order and compute the grid axes:
Univariate leaves (missing :x or :y) use the no-x-key / no-y-key sentinels so they get their own grid lane.
Returns {:col-keys [...] :row-keys [...] :col-labels [...|nil] :row-labels [...|nil] :positions {path -> [col-idx row-idx]} :x-vars [...] :y-vars [...]}.
The compositor consumes :positions for rect math and the labels for strip rendering; :x-vars / :y-vars surface in plan introspection.
For a composite whose layout is `:matrix`, walk its leaves in
DFS order and compute the grid axes:
- col-key per leaf: the leaf's :x mapping. Two leaves sharing
(x, y) keep the same col-key.
- row-key per leaf: the leaf's :y mapping, with a DFS-occurrence
discriminator when (x, y) repeats. The first (a, b) gets row
b; the second (a, b) gets row [b 1]; the third [b 2]; etc.
Same column, new row in DFS order.
- col-keys / row-keys: distinct keys in order of first appearance.
- col-labels / row-labels: human-readable strings via
defaults/fmt-name; nil when only one column or one row exists
so we don't render a redundant strip header.
Univariate leaves (missing :x or :y) use the no-x-key / no-y-key
sentinels so they get their own grid lane.
Returns {:col-keys [...] :row-keys [...]
:col-labels [...|nil] :row-labels [...|nil]
:positions {path -> [col-idx row-idx]}
:x-vars [...] :y-vars [...]}.
The compositor consumes :positions for rect math and the labels
for strip rendering; :x-vars / :y-vars surface in plan introspection.(path->update-in-path path)Translate a leaf path like [0 1] into the get-in / update-in navigation [:poses 0 :poses 1]. A root path [] translates to [].
Translate a leaf path like [0 1] into the get-in / update-in navigation [:poses 0 :poses 1]. A root path [] translates to [].
(pose? x)True if x looks pose-shaped: a map carrying at least one of :layers or :poses. Permissive by design -- schema-level validation lives in impl.pose-schema.
True if x looks pose-shaped: a map carrying at least one of :layers or :poses. Permissive by design -- schema-level validation lives in impl.pose-schema.
(resolve-tree pose)(resolve-tree pose parent-ctx path)Walk the pose tree top-down, merging parent context into each descendant. Returns a vector of resolved leaves; each leaf carries merged :data, :mapping, :layers, :opts, and a :path vector of indices describing its position in the tree.
Context inheritance rules:
Extra keys on a leaf (anything not in #{:data :mapping :layers :poses :layout :opts :share-scales}) pass through to the resolved leaf so callers can attach metadata like :path-labels from facet-style generators.
Walk the pose tree top-down, merging parent context into each
descendant. Returns a vector of resolved leaves; each leaf carries
merged :data, :mapping, :layers, :opts, and a :path vector of
indices describing its position in the tree.
Context inheritance rules:
- :data -- nearest ancestor wins (child overrides parent).
- :mapping -- merged, with child keys overriding parent keys.
- :layers -- concatenated (ancestor layers distribute down, then
the leaf's own layers append).
- :opts -- merged (child overrides on key collision).
Extra keys on a leaf (anything not in
#{:data :mapping :layers :poses :layout :opts :share-scales})
pass through to the resolved leaf so callers can attach metadata
like :path-labels from facet-style generators.cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |