Liking cljdoc? Tell your friends :D

scicloj.plotje.impl.pose

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}
raw docstring

check-explicit-mapping!clj

(check-explicit-mapping! k v)

Throw when an explicit mapping is malformed. Nothing here reads the layer's data, which is what lets api run it at the pj/pose or lay-* call rather than at pj/draft: a mistyped key inside the map is a mistake about the form, and the form is fully visible where it is written. The checks that need the data -- whether the column is there, whether the value is one the aesthetic can draw -- stay in validate-columns.

Called both from the call site and from normalize-explicit-mapping, so a pose built by hand and threaded straight to pj/draft is held to the same rules.

Throw when an explicit mapping is malformed. Nothing here reads the
layer's data, which is what lets `api` run it at the `pj/pose` or
`lay-*` call rather than at `pj/draft`: a mistyped key inside the
map is a mistake about the form, and the form is fully visible
where it is written. The checks that need the data -- whether the
column is there, whether the value is one the aesthetic can draw --
stay in `validate-columns`.

Called both from the call site and from
`normalize-explicit-mapping`, so a pose built by hand and threaded
straight to `pj/draft` is held to the same rules.
sourceraw docstring

combine-scalesclj

(combine-scales outer inner)

Combine an outer scale statement with an inner one.

Scale settings accumulate down the scope chain: where both name a spec the two merge key by key, so a pose that sets a range and a layer that names a type give a plot with both. The innermost wins per key.

true and false are not specs and do not merge. false says the value passes through no scale at all, so it replaces whatever was set above; true says it does pass through one without saying which, so it leaves an outer spec standing.

Combine an outer scale statement with an inner one.

Scale settings accumulate down the scope chain: where both name a
spec the two merge key by key, so a pose that sets a range and a
layer that names a type give a plot with both. The innermost wins
per key.

`true` and `false` are not specs and do not merge. `false` says the
value passes through no scale at all, so it replaces whatever was
set above; `true` says it does pass through one without saying
which, so it leaves an outer spec standing.
sourceraw docstring

composite?clj

(composite? f)

A composite pose has at least one sub-pose.

A composite pose has at least one sub-pose.
sourceraw docstring

compute-layoutclj

(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).
sourceraw docstring

explicit-mapping-keysclj

The keys an explicit mapping map may carry: one source, and optionally the scale to read it through.

The keys an explicit mapping map may carry: one source, and
optionally the scale to read it through.
sourceraw docstring

explicit-mapping?clj

(explicit-mapping? v)

True of a mapping value written in the explicit form -- a map naming its source, as {:column :species} or {:value "red" :scale true}, or naming only a scale, as pj/scale writes.

A map is unambiguous here because no aesthetic takes one as a value: a color is a string or a keyword, a size is a number, a shape is a symbol from a fixed list.

True of a mapping value written in the explicit form -- a map naming
its source, as `{:column :species}` or `{:value "red" :scale true}`,
or naming only a scale, as `pj/scale` writes.

A map is unambiguous here because no aesthetic takes one as a value:
a color is a string or a keyword, a size is a number, a shape is a
symbol from a fixed list.
sourceraw docstring

inject-shared-scalesclj

(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.
sourceraw docstring

last-leaf-pathclj

(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).
sourceraw docstring

last-matching-leaf-pathclj

(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.

What a mapping names decides the match, not how it is written: a position carrying a scale names the same position as the plain form, and a mapping that names only a scale names no position at all.

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.

What a mapping names decides the match, not how it is written: a
position carrying a scale names the same position as the plain
form, and a mapping that names only a scale names no position at
all.
sourceraw docstring

leaf->draftclj

(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, each aesthetic's resolved scale spec under its own key, and plot-level :coord stamped from :opts. The scales come from the mapping, which is where pj/scale writes them; only :coord is still an option.

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, each aesthetic's
resolved scale spec under its own key, and plot-level :coord
stamped from :opts. The scales come from the mapping, which is
where `pj/scale` writes them; only :coord is still an option.

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.
sourceraw docstring

leaf-atclj

(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.
sourceraw docstring

leaf?clj

(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.)
sourceraw docstring

mapping-sourceclj

(mapping-source v)

What a mapping value names, with the full form unwrapped: the :column, the :value or the :from. A mapping that names only a scale names no source, and answers nil.

Identity is decided on this rather than on the written value, so that {:x {:from :a :scale ...}} and a plain {:x :a} are the same position -- which they are, since pj/scale produces the first from the second.

What a mapping value names, with the full form unwrapped: the
`:column`, the `:value` or the `:from`. A mapping that names only a
scale names no source, and answers nil.

Identity is decided on this rather than on the written value, so
that `{:x {:from :a :scale ...}}` and a plain `{:x :a}` are the same
position -- which they are, since `pj/scale` produces the first from
the second.
sourceraw docstring

matrix-axesclj

(matrix-axes composite)

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.

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.
sourceraw docstring

merge-mappingsclj

(merge-mappings outer inner)

Merge an outer mapping into an inner one, the way pose mappings have always merged -- the inner value wins -- except for the scale, which accumulates. See merge-mapping-value.

Merge an outer mapping into an inner one, the way pose mappings have
always merged -- the inner value wins -- except for the scale, which
accumulates. See `merge-mapping-value`.
sourceraw docstring

path->update-in-pathclj

(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 [].
sourceraw docstring

pose?clj

(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.
sourceraw docstring

put-scaleclj

(put-scale mapping aesthetic spec)

Write a scale spec into mapping for aesthetic -- what pj/scale does.

A scale lives with the mapping it reads, so this is an update of the mapping rather than of the pose's options. Where the aesthetic is mapped plainly there is no room for a scale, so the value is rewritten in the full form under :from, which says the same thing: ask the data. Where it is not mapped here at all, the scale is written on its own and applies to whatever source is named below.

A mapping cancelled with nil stays cancelled: an aesthetic that draws nothing has nothing to scale. A mapping that says :scale false on this same pose is refused rather than overridden: one pose cannot both draw a value as it stands and read it through a scale.

Write a scale spec into `mapping` for `aesthetic` -- what `pj/scale`
does.

A scale lives with the mapping it reads, so this is an update of the
mapping rather than of the pose's options. Where the aesthetic is
mapped plainly there is no room for a scale, so the value is
rewritten in the full form under `:from`, which says the same thing:
ask the data. Where it is not mapped here at all, the scale is
written on its own and applies to whatever source is named below.

A mapping cancelled with `nil` stays cancelled: an aesthetic that
draws nothing has nothing to scale. A mapping that says `:scale
false` on this same pose is refused rather than overridden: one pose
cannot both draw a value as it stands and read it through a scale.
sourceraw docstring

resolve-treeclj

(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:

  • :data -- nearest ancestor wins (child overrides parent).
  • :mapping -- merged, with child keys overriding parent keys, and scale settings accumulating (see merge-mappings).
  • :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.

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, and
               scale settings accumulating (see `merge-mappings`).
- :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.
sourceraw docstring

scale-only-mapping?clj

(scale-only-mapping? v)

True of a mapping value that names a scale and no source -- what pj/scale writes for an aesthetic this pose does not map. It says how to read whatever source is named elsewhere, and where none is, nothing is drawn and the scale is inert.

True of a mapping value that names a scale and no source -- what
`pj/scale` writes for an aesthetic this pose does not map. It says
how to read whatever source is named elsewhere, and where none is,
nothing is drawn and the scale is inert.
sourceraw docstring

source-keysclj

The three ways an explicit mapping can name its source.

:column reads the value from the layer's data and :value is the value itself; :from is the plain reading spelled out -- ask the data, and take whichever answer it gives, exactly as a mapping written plainly does. :from is what lets a plain mapping carry a :scale, since a bare :size :weight has no room for one.

The three ways an explicit mapping can name its source.

`:column` reads the value from the layer's data and `:value` is the
value itself; `:from` is the plain reading spelled out -- ask the
data, and take whichever answer it gives, exactly as a mapping
written plainly does. `:from` is what lets a plain mapping carry a
`:scale`, since a bare `:size :weight` has no room for one.
sourceraw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close