marks

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 27, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package marks holds the per-mark encoders that turn rows of a materialised table into scene.Mark entries with pixel-resolved geometry. P05 supports five marks: bar (Rect), line, area, point, rule. Other types (arc/text/path/image, plus composite/specialty) emit a PRISM_WARN_MARK_NOT_IMPLEMENTED warning.

Index

Constants

View Source
const ViolinResolution = 64

ViolinResolution is the default sample count along the value axis per violin group. See D061.

Variables

This section is empty.

Functions

func AttachDatum

func AttachDatum(marks []scene.Mark, layerID string, rowCount int)

AttachDatum stamps a *scene.Datum back-reference on the first rowCount marks in the slice. layerID identifies the SceneLayer the marks belong to (defaults to "layer-0" for flat specs; composite encoders override via Inputs.LayerID).

Per D077, only the (layer_id, row_id) pair is populated by default. The Fields bag stays nil to keep the JSON payload small; tooltip channels already carry pre-formatted field values via D063. Composite encoders that emit aggregation rows (boxplot whiskers, histogram bin edges) can call AttachDatum on the appropriate prefix and leave the trailing helper marks without a Datum — the JS hit-test silently ignores marks without the data-prism-datum-row attribute.

rowCount typically equals the number of rows in the upstream table (the per-row encoders produce one mark per row). When rowCount is larger than len(marks), the helper stops at the end of the marks slice — both bounds are respected.

func AttachKeys

func AttachKeys(marks []scene.Mark, tbl *table.Table, field string)

AttachKeys stamps Mark.Key on per-row marks for use by the client-side animator. The key string format is "<field>=<value>"; missing or out-of-range rows yield an empty key (mark falls back to positional matching at tween time).

Like AttachDatum, only the leading rowCount marks are stamped; composite encoders that emit trailing helper marks (boxplot whiskers, histogram bin edges) are left untouched.

func AttachTooltips

func AttachTooltips(marks []scene.Mark, tooltips []*scene.Tooltip)

AttachTooltips attaches tooltips[i] to marks[i].Tooltip in 1:1 order. When len(tooltips) != len(marks), attaches as far as the shorter slice allows (single-mark types like line/area receive tooltips[0]; per-row types receive 1:1). Mutates marks in place.

func BuildTooltips

func BuildTooltips(tbl *table.Table, ch *spec.TooltipChannel, rowCount int) []*scene.Tooltip

BuildTooltips materialises one *scene.Tooltip per row from a TooltipChannel binding. Returns nil when ch is nil (caller leaves Mark.Tooltip nil — per-mark renderers skip the <title> child).

Each TooltipLine is formatted as "<field>: <formatted_value>" where the formatter is the channel's `format` specifier through encode/format (matching axis-label formatting), falling back to fmt.Sprintf("%v", value) when no format is set.

Single tooltip: 1 line per row. Multi tooltip: N lines per row (one per Multi entry).

Missing fields render as "<field>: <missing>" — tooltips are diagnostic, never blocking. See D063.

func Encode

func Encode(markType string, in Inputs) ([]scene.Mark, *scene.Warning, error)

Encode dispatches markType to its per-mark helper. Returns the generated marks + an optional warning (for unsupported types). Errors bubble PRISM_ENCODE_001 or PRISM_RENDER_001 from the helpers.

P10: tooltip materialisation runs post-dispatch. When in.Tooltip != nil, BuildTooltips walks the upstream table once and AttachTooltips attaches the *scene.Tooltip to each mark in per-row order (single-mark types receive row 0's tooltip).

func Quantile

func Quantile(sorted []float64, q float64) float64

Quantile returns the q-th quantile (q in [0, 1]) of a sorted slice via linear interpolation between order statistics. Matches Pulse's AGG_PERCENTILE convention (R-7 quantile algorithm).

func SequentialColor

func SequentialColor(v, mn, mx float64) *scene.Color

SequentialColor returns a color along a light-blue → dark-blue gradient for v in [min, max]. Degenerate range (min == max) returns the mid-tone anchor. P10 keeps the gradient hardcoded; theme-level sequential palettes land in P12 alongside richer color tooling.

func SkipNullRows

func SkipNullRows(tbl *table.Table, fields ...string) (kept []int, dropped int, offending []string)

SkipNullRows returns the indices of rows in tbl where every named field is non-null. Encoders that consume `fields` per-row use this to drop rows where any required channel is null so geometries don't render at zero positions or default colors.

The skipped count + offending field names are reported back so the caller can surface PRISM_WARN_NULL_DROPPED. See `.planning/tier1-02-hash-join-null-bitmap-plan.md`.

Types

type BandScaler

type BandScaler interface {
	BandWidth() float64
}

BandScaler is the optional capability bar marks ask for to size rect widths. Implemented by encode.BandScale.

type BoxplotSummary

type BoxplotSummary struct {
	Group    string
	Q1       float64
	Median   float64
	Q3       float64
	Min      float64
	Max      float64
	ReachLow float64
	ReachHi  float64
	Outliers []float64
}

BoxplotSummary holds per-group statistics computed by the boxplot encoder. Exposed for parity tests (see boxplot_parity_test.go).

func ComputeBoxplotSummaries

func ComputeBoxplotSummaries(in Inputs) ([]BoxplotSummary, error)

ComputeBoxplotSummaries partitions the table by the category axis (in.X.Field) and computes q1/median/q3/min/max + whisker reach + outliers per group. Exposed so the parity test can compare summaries directly against Pulse-mapped quantiles.

type Channel

type Channel struct {
	Field string
	Scale Scale
}

Channel binds an encoding channel to a resolved Scale and the field name on the upstream table. Created by encode.Encode and handed to each mark encoder.

type ColorChannel

type ColorChannel struct {
	Field      string
	Categories []string
	Palette    []*scene.Color
}

ColorChannel binds a color encoding (categorical field + palette).

type HistogramResult

type HistogramResult struct {
	Marks    []scene.Mark
	XScale   *scale.LinearScale
	YScale   *scale.LinearScale
	BinEdges []float64
	Counts   []int
}

HistogramResult bundles the encoded marks with the synthetic scales the histogram encoder builds inline. The encode.go path uses XScale / YScale to build axes; standard callers via marks.Encode read only Marks. See D060.

XScale / YScale are exported as concrete *scale.LinearScale so callers can hand them to encode.BuildAxisWithOpts (which expects the richer encode.Scale interface, not the minimal marks.Scale).

func EncodeHistogram

func EncodeHistogram(in Inputs) (*HistogramResult, error)

EncodeHistogram builds bins inline, counts rows per bin, then emits one RectGeom per bin. Defaults to Sturges' rule (ceil(log2(n) + 1)) for bin count; honors mark_def.maxbins when set.

Bin edges use the nice-step algorithm from compile/inmem/bin.go (duplicated here per D060; cross-package re-export was rejected to keep encode/marks dependency-free of compile/).

type Inputs

type Inputs struct {
	Table   *table.Table
	X       Channel
	Y       Channel
	Color   *ColorChannel
	Layout  scene.Rect // the Plot region
	Style   scene.Style
	Mark    *spec.MarkDef        // mark-level overrides; nil ok
	Tooltip *spec.TooltipChannel // encoding.tooltip binding; nil ok
	Source  Channel              // sankey source-node field (no scale)
	Target  Channel              // sankey target-node field (no scale)
	Value   Channel              // sankey flow-magnitude field (no scale)
	// Feature (P18) is the geoshape feature-id binding — the table
	// column whose values are geodata IDs (USA, US-CA, …).
	Feature Channel
	// Longitude / Latitude (P18) are geopoint bindings; field-only.
	Longitude Channel
	Latitude  Channel
	// Projection (P18) maps lon/lat → pixel space for geoshape and
	// geopoint marks. Nil for non-geo marks.
	Projection projection.Projection
	// GeoStore (P18) is the feature-geometry source. Defaults to
	// geodata.DefaultStore() at dispatch time.
	GeoStore geodata.Store
	// GeoTier (P18) is the manifest tier the encoder pulls features
	// from. Defaults to TierWorld110m.
	GeoTier geodata.Tier
	// LayerID is the scene-layer identifier stamped onto every
	// per-row mark's Datum back-reference (D077). Empty defaults to
	// "layer-0" — matches the flat-encoder hardcoded layer ID.
	// Composite callers (layer / facet / repeat) override per-cell.
	LayerID string
	// KeyField (animation) is the encoding-channel field name flagged
	// with key:true in the spec. When non-empty, per-row marks get
	// Mark.Key = "<field>=<value>" so the client-side animator can
	// match marks across scene swaps. Empty (default) leaves Mark.Key
	// blank; SVG and PDF renderers ignore Mark.Key either way.
	KeyField string
}

Inputs carries the per-Encode-call context: table, encoded channels, layout, mark style.

Tooltip (P10) carries the encoding.tooltip binding; when non-nil, the dispatch attaches one *scene.Tooltip per produced mark via AttachTooltips (per-row marks: 1:1; single-mark types: row 0's tooltip). See D063.

Source / Target / Value (P11) carry the sankey-specific channel bindings — Field name only, no scale. See D064. Used exclusively by encodeSankey; other encoders ignore them.

type SankeyLayout

type SankeyLayout struct {
	Nodes []SankeyNode
	Links []SankeyLink
}

SankeyLayout bundles the encoder's layout output. Exposed for the PHASE.md gate to hand-verify deterministic positions.

func ComputeSankeyLayout

func ComputeSankeyLayout(in Inputs) (*SankeyLayout, error)

ComputeSankeyLayout runs the place-once layout algorithm. Exposed for the PHASE.md test gate so positions can be hand-verified.

type SankeyLink struct {
	Source string
	Target string
	Value  float64
	SX     float64 // source-side x (right edge of source node)
	SY     float64 // source-side y (center of link's slice within source)
	TX     float64 // target-side x (left edge of target node)
	TY     float64 // target-side y (center of link's slice within target)
	Width  float64 // visual stroke width = Value * heightScale
}

SankeyLink is one link between two nodes. Source/Target are node IDs; SourceY / TargetY are the y-pixel offsets within the source and target nodes (cumulative-flow stacking).

type SankeyNode

type SankeyNode struct {
	ID      string
	Depth   int
	Column  int
	Rank    int     // vertical rank within column (0-based)
	X       float64 // top-left pixel
	Y       float64
	W       float64
	H       float64
	Inflow  float64
	Outflow float64
}

SankeyNode is one node in the sankey diagram. Exposed for parity tests (TestPrismSankeyLayout in sankey_test.go).

type Scale

type Scale interface {
	Apply(value any) (float64, error)
	Domain() []any
}

Scale is the minimal scale surface mark encoders need. Matches encode.Scale by structural duck-typing without an import cycle.

Directories

Path Synopsis
Package layout holds the shared graph + tree layout helpers used by the encode/marks tree, dendrogram, and network encoders.
Package layout holds the shared graph + tree layout helpers used by the encode/marks tree, dendrogram, and network encoders.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL