Liking cljdoc? Tell your friends :D

scicloj.plotje.impl.pose-schema

Malli schema for the Pose data model.

A pose is a plain recursive map. A leaf pose has no :poses (or an empty :poses vector). A composite pose has :poses and an optional :layout describing how sub-poses tile a bounding rectangle.

Validation is not wired into any runtime path yet; Phase 6 of the pre-alpha refactor adds validation at public API boundaries. Until then, impl.pose operates on structurally-valid poses by convention; this schema is the authoritative definition of that convention.

The three stages are documented to different depths and enforced differently. The plan schema is checked on every plan, unless the :validate configuration key turns it off -- it is what raises Plan does not conform to schema, which is where a mapping value with no reading tends to surface. Pose and draft are documented and unenforced. What keeps this schema honest instead is impl/pose_schema_test.clj, which validates a pose from each public constructor.

Decisions made in Phase 2:

  • A leaf with no :data and no :mapping is valid -- leaves inherit context from ancestors via impl.pose/resolve-tree.
  • :share-scales is structurally allowed on any pose; it is a no-op on leaves (nothing to share).
  • :layout :weights length is not required to equal (count :poses); impl.pose/compute-layout tolerates short/long weight vectors.

aesthetic-value-schemas below writes down the mapping value grammar per aesthetic, as the code behaves today. It decides nothing: where two aesthetics disagree, both readings are recorded, because what a mapping value should mean is an open design question and this schema is a description of the answer in force rather than an argument for one.

Malli schema for the Pose data model.

A pose is a plain recursive map. A leaf pose has no :poses (or
an empty :poses vector). A composite pose has :poses and an
optional :layout describing how sub-poses tile a bounding
rectangle.

Validation is not wired into any runtime path yet; Phase 6 of the
pre-alpha refactor adds validation at public API boundaries. Until
then, impl.pose operates on structurally-valid poses by convention;
this schema is the authoritative definition of that convention.

The three stages are documented to different depths and enforced
differently. The plan schema is checked on every plan, unless the
`:validate` configuration key turns it off -- it is what raises
`Plan does not conform to schema`, which is where a mapping value
with no reading tends to surface. Pose and draft are documented and
unenforced. What keeps this schema honest instead is
`impl/pose_schema_test.clj`, which validates a pose from each public
constructor.

Decisions made in Phase 2:
- A leaf with no :data and no :mapping is valid -- leaves inherit
  context from ancestors via impl.pose/resolve-tree.
- :share-scales is structurally allowed on any pose; it is a no-op
  on leaves (nothing to share).
- :layout :weights length is not required to equal (count :poses);
  impl.pose/compute-layout tolerates short/long weight vectors.

`aesthetic-value-schemas` below writes down the mapping value
grammar per aesthetic, as the code behaves today. It decides
nothing: where two aesthetics disagree, both readings are
recorded, because what a mapping value should mean is an open
design question and this schema is a description of the answer
in force rather than an argument for one.
raw docstring

aesthetic-value-schemasclj

Value grammar per aesthetic, as the code behaves today. The key set is impl.defaults/column-keys; pose-mapping-value-grammar-test pins the two together so a new aesthetic cannot be added without a reading for it.

Three things Malli cannot state, so they are stated here:

  1. Which values name a column is decided against the data, for some aesthetics only. A string :color names a column when the data has a column of that name and is a CSS color otherwise (impl.pose/validate-columns carves it out, and impl.resolve/resolve-aesthetics does the lookup). A number in :x, :y or :x-end names a column on the same terms (impl.pose/resolve-positional-values), which matters because a dataset built without column names gets integer ones. Every other aesthetic reads a keyword or a string as a column reference and demands that the column exist.

  2. Where the mapping is written decides what a number in :x or :y means. In a layer's options map it places a mark, either as one mark or broadcast over the layer's data. In pj/pose, or in the :x / :y arguments of a lay-* call, api/check-position-mapping refuses it, so an integer column name reports the rename it needs.

  3. Which layer types accept the key. :size, :shape, :text, :fill, :x-end, :y-min and :y-max are not universal layer options -- each is on the :accepts list of particular layer types in layer-type.clj, and a layer type that does not accept one warns and strips it. :y-min / :y-max are the sharpest case: a column reference on :errorbar and :pointrange, a literal value in data space on :band-h.

nil is a legal value for every aesthetic -- it cancels a mapping inherited from an ancestor, and survives into the draft as an explicit nil, so each entry is wrapped in :maybe by mapping-schema.

Value grammar per aesthetic, as the code behaves today. The key set
is `impl.defaults/column-keys`; `pose-mapping-value-grammar-test`
pins the two together so a new aesthetic cannot be added without a
reading for it.

Three things Malli cannot state, so they are stated here:

1. **Which values name a column is decided against the data, for
   some aesthetics only.** A string `:color` names a column when the
   data has a column of that name and is a CSS color otherwise
   (`impl.pose/validate-columns` carves it out, and
   `impl.resolve/resolve-aesthetics` does the lookup). A number in
   `:x`, `:y` or `:x-end` names a column on the same terms
   (`impl.pose/resolve-positional-values`), which matters because a
   dataset built without column names gets integer ones. Every other
   aesthetic reads a keyword or a string as a column reference and
   demands that the column exist.

2. **Where the mapping is written decides what a number in `:x` or
   `:y` means.** In a layer's options map it places a mark, either
   as one mark or broadcast over the layer's data. In `pj/pose`, or
   in the `:x` / `:y` arguments of a `lay-*` call,
   `api/check-position-mapping` refuses it, so an integer column
   name reports the rename it needs.

3. **Which layer types accept the key.** `:size`, `:shape`, `:text`,
   `:fill`, `:x-end`, `:y-min` and `:y-max` are not universal layer
   options -- each is on the `:accepts` list of particular layer
   types in `layer-type.clj`, and a layer type that does not accept
   one warns and strips it. `:y-min` / `:y-max` are the sharpest
   case: a column reference on `:errorbar` and `:pointrange`, a
   literal value in data space on `:band-h`.

`nil` is a legal value for every aesthetic -- it cancels a mapping
inherited from an ancestor, and survives into the draft as an
explicit nil, so each entry is wrapped in `:maybe` by
`mapping-schema`.
sourceraw docstring

Colorclj

A value that unmistakably names a color: a #-prefixed hex string, or a CSS color name as a string or a keyword. Mirrors impl.defaults/names-a-color?, which is deliberately narrower than what hex->rgba will convert -- a bare abc converts, and is a mistyped column name far more often than it is a shade.

A value that unmistakably names a color: a `#`-prefixed hex string,
or a CSS color name as a string or a keyword. Mirrors
`impl.defaults/names-a-color?`, which is deliberately narrower than
what `hex->rgba` will convert -- a bare `abc` converts, and is a
mistyped column name far more often than it is a shade.
sourceraw docstring

ColumnRefclj

A value that can name a dataset column: a keyword or a string. Mirrors impl.resolve/column-ref?. Matching against the dataset is strict -- :species does not name a column whose name is the string species, and the other way round.

A value that can name a dataset column: a keyword or a string.
Mirrors `impl.resolve/column-ref?`. Matching against the dataset is
strict -- `:species` does not name a column whose name is the string
`species`, and the other way round.
sourceraw docstring

ColumnTypeOverrideclj

A :x-type / :y-type / :color-type value: the classification impl.resolve/column-type would otherwise infer.

A `:x-type` / `:y-type` / `:color-type` value: the classification
`impl.resolve/column-type` would otherwise infer.
sourceraw docstring

drawn-value-schemasclj

What each aesthetic accepts as a written value -- the half of the grammar that is not a column reference.

Split out from aesthetic-value-schemas because one [:or ColumnRef ...] cannot answer "is this drawable", which is the third step of the rule the draft resolves by: ask the layer's data, then ask what the aesthetic can draw, then report that neither reading fits. Fusing the two halves is the same mistake the registry's old :literal key made a level up, and it is corrected the same way -- state each half, compose the whole.

impl.aesthetics is the runtime consumer. That matters: a schema nothing validates drifts from the code, which is how the six disagreeing aesthetic lists came about.

An aesthetic absent from this map has no reading for a written value at all -- :group splits the data and draws nothing of its own, so there is nothing a value could mean. :shape and :fill are listed because a value on them means something; whether one is accepted yet is the registry's :value?, and the two are deliberately separate.

What each aesthetic accepts as a **written value** -- the half of the
grammar that is not a column reference.

Split out from `aesthetic-value-schemas` because one `[:or ColumnRef
...]` cannot answer "is this drawable", which is the third step of
the rule the draft resolves by: ask the layer's data, then ask what
the aesthetic can draw, then report that neither reading fits. Fusing
the two halves is the same mistake the registry's old `:literal` key
made a level up, and it is corrected the same way -- state each half,
compose the whole.

`impl.aesthetics` is the runtime consumer. That matters: a schema
nothing validates drifts from the code, which is how the six
disagreeing aesthetic lists came about.

An aesthetic absent from this map has no reading for a written value
at all -- `:group` splits the data and draws nothing of its own, so
there is nothing a value could mean. `:shape` and `:fill` are listed
because a value on them *means* something; whether one is accepted
yet is the registry's `:value?`, and the two are deliberately
separate.
sourceraw docstring

explainclj

(explain x)

Explain why x does not conform to the pose schema, or nil if valid.

Explain why x does not conform to the pose schema, or nil if valid.
sourceraw docstring

ExplicitMappingclj

A mapping written out in full: which source it takes, and optionally which side of the scale to read it through.

{:column :species} insists on the column even where a value of that name could be drawn; {:value "blue"} insists on the color even where the data carries a column called blue; {:from :species} is the plain reading spelled out -- ask the data, and take whichever answer it gives. :from is what lets a mapping that leaves the source to the data still carry a :scale.

:scale false draws what the convention would scale, :scale true scales what it would draw, and omitting :scale -- or writing nil -- leaves the convention in charge. A scale type or a spec map there names which scale.

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

A mapping written out in full: which source it takes, and
optionally which side of the scale to read it through.

`{:column :species}` insists on the column even where a value of
that name could be drawn; `{:value "blue"}` insists on the color
even where the data carries a column called blue; `{:from :species}`
is the plain reading spelled out -- ask the data, and take whichever
answer it gives. `:from` is what lets a mapping that leaves the
source to the data still carry a `:scale`.

`:scale false` draws what the convention would scale, `:scale true`
scales what it would draw, and omitting `:scale` -- or writing
`nil` -- leaves the convention in charge. A scale type or a spec map
there names which scale.

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

Layoutclj

Compositor layout spec for a composite pose. :matrix places leaves on a grid derived from their :x / :y mappings and ignores :weights; it is what pj/pose stamps when a threaded :x or :y promotes a leaf into a composite. See impl.pose/compute-layout.

Compositor layout spec for a composite pose. `:matrix` places
leaves on a grid derived from their `:x` / `:y` mappings and
ignores `:weights`; it is what `pj/pose` stamps when a threaded
`:x` or `:y` promotes a leaf into a composite. See
`impl.pose/compute-layout`.
sourceraw docstring

Mappingclj

Aesthetic mapping on a pose or a layer. See aesthetic-value-schemas for what each value may be and for the three things that grammar cannot state.

Aesthetic mapping on a pose or a layer. See
`aesthetic-value-schemas` for what each value may be and for the
three things that grammar cannot state.
sourceraw docstring

mapping-schemaclj

(mapping-schema & extra-entries)

Build a mapping schema from aesthetic-value-schemas, optionally with extra typed entries. Open on purpose, and open in two different ways at the two places a mapping appears:

  • a pose's :mapping carries the aesthetics plus the three column type overrides (api/pose-mapping-keys); :data is stripped into the pose itself rather than left in the mapping;
  • a layer's :mapping carries the aesthetics plus every layer option that is not :data, :mark, :stat or :position -- :jitter, :in, :nudge-x, :font-size, :bandwidth and the rest all live there (api/build-layer).

So the name :mapping covers more than mappings, and the two :mapping maps admit different key sets. Keys are always keywords.

Build a mapping schema from `aesthetic-value-schemas`, optionally
with extra typed entries. Open on purpose, and open in two
different ways at the two places a mapping appears:

- a pose's `:mapping` carries the aesthetics plus the three column
  type overrides (`api/pose-mapping-keys`); `:data` is stripped
  into the pose itself rather than left in the mapping;
- a layer's `:mapping` carries the aesthetics plus every layer
  option that is not `:data`, `:mark`, `:stat` or `:position` --
  `:jitter`, `:in`, `:nudge-x`, `:font-size`, `:bandwidth` and the
  rest all live there (`api/build-layer`).

So the name `:mapping` covers more than mappings, and the two
`:mapping` maps admit different key sets. Keys are always keywords.
sourceraw docstring

PoseLayerclj

A pose-layer: a layer declaration attached to a pose. :layer-type names a registered entry; :mark / :stat / :position are layer-structural siblings extracted from user opts by build-layer (Phase 6 decision 1).

A pose-layer: a layer declaration attached to a pose. :layer-type
names a registered entry; :mark / :stat / :position are
layer-structural siblings extracted from user opts by build-layer
(Phase 6 decision 1).
sourceraw docstring

PoseSchemaclj

Structural schema for a pose tree.

Shape: {:data ? dataset (inherited from ancestor if absent) :mapping ? aesthetic mappings (merges with ancestors) :layers ? PoseLayer vec at this level (accumulates into leaves) :poses ? sub-poses; absent or empty = leaf :layout ? Layout for composites :opts ? plot options (inheritable) :share-scales ? ShareScales}

Permissive {:closed false} intentionally -- generators like facet and mosaic attach metadata keys (:panel-label, :facet-row, ...) to leaves that pass through resolve-tree unchanged.

Structural schema for a pose tree.

Shape:
  {:data         ?  dataset (inherited from ancestor if absent)
   :mapping      ?  aesthetic mappings (merges with ancestors)
   :layers       ?  PoseLayer vec at this level (accumulates into leaves)
   :poses       ?  sub-poses; absent or empty = leaf
   :layout       ?  Layout for composites
   :opts         ?  plot options (inheritable)
   :share-scales ?  ShareScales}

Permissive {:closed false} intentionally -- generators like facet
and mosaic attach metadata keys (:panel-label, :facet-row, ...) to
leaves that pass through resolve-tree unchanged.
sourceraw docstring

PositionalAestheticclj

What may be written for :x, :y and the range endpoints: a column reference, or a literal value in data space.

What may be written for `:x`, `:y` and the range endpoints: a
column reference, or a literal value in data space.
sourceraw docstring

PositionalLiteralclj

A literal value that places a mark on its own, as {:x 6.5} does, rather than naming a column to read it from. Mirrors impl.resolve/literal-position?.

A literal value that places a mark on its own, as `{:x 6.5}` does,
rather than naming a column to read it from. Mirrors
`impl.resolve/literal-position?`.
sourceraw docstring

Shapeclj

One of the marker symbols a :shape mapping draws with.

One of the marker symbols a `:shape` mapping draws with.
sourceraw docstring

ShareScalesclj

Axis keys whose scale domains union across descendants of a composite.

Axis keys whose scale domains union across descendants of a composite.
sourceraw docstring

Temporalclj

A temporal value, which a scale coerces like any other datum. Mirrors the instance checks in impl.resolve/literal-position?.

A temporal value, which a scale coerces like any other datum.
Mirrors the instance checks in `impl.resolve/literal-position?`.
sourceraw docstring

valid?clj

(valid? x)

Check if x conforms to the pose schema.

Check if x conforms to the pose schema.
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