facett-plotters — the everyday XY chart (line · bar · scatter) as one Elm Facet
Class:
design· What it IS: [ChartView] — a single themeable [Facet] that draws line, bar, or scatter over a set of named series, with the axes, gridlines, legend, and hover tooltip all derived from the data. It lives alongside the crate's hand-painted analytics charts (box/violin/heatmap/ candlestick/treemap) and, like them, is drawn in immediate mode on the eguiPainter— noegui_plot, no retainedplotterscrate. Sibling how-to:guide.md. This file states only what it is.
Function axis
- f0 — the chart: the 80%-case XY plot every dashboard needs.
egui_plot(viafacett-plot) already does zoom/pan line/bar;ChartViewis the self-contained, theme-categorical, headless-drivable version that fits the facett Elm/Facet contract and re-skins with the activeTheme. - f1 — the data model: the simplest thing that works —
Vec<[Series]>, each alabel+Vec<(x, y)>. Nothing else is supplied: axis bounds, ticks, the legend, and the tooltip are all derived, so the caller only owns the numbers. - f2 — the derived geometry (pure, unit-tested, no GPU): [
ChartView::x_bounds] / [ChartView::y_bounds] fold the finite points into a range (bar mode extends the y range to include the0baseline so bar heights read honestly). The paint code maps that range into the plot rect. - f3 — the mark: one enum, [
ChartKind] (Line·Bar·Scatter), is a single field in the model.Linejoins each series with a 2px polyline;Bardraws grouped bars per category anchored to 0;Scatterdraws markers only. - f4 — the Elm
Facet:ChartState/ChartMsg/ChartEffect/update/pureview, bridged toFacetviaimpl_facet_via_elm!(Form 3,custom_state_json), so it is headless-drivable (update_json/state_json) and paints in egui.
The Elm split (f4)
- Model — [
ChartState]: thekindand the hovered(series, point). The data (series) lives on the view; the interaction state is the model, so switching kind or moving the hover is the whole observable surface. - Msg — externally-tagged serde so a driver posts JSON:
SetKind(ChartKind)({"set_kind":"bar"}) andHover(Option<(usize,usize)>)({"hover":[0,1]}/{"hover":null}).updateis the ONLY mutation path; it returns no effects. - Effect — [
ChartEffect] is uninhabited: the chart does no I/O. - view — a pure
&self → Vec<Msg>: paints axes/gridlines, the series per the stored kind, the legend, and a tooltip from the persisted hover; hit-tests the pointer and returns the hovered-point change as aMsg(never mutatesself). Theimpl_facet_via_elm!bridge applies the returnedMsgs.
Colour — the categorical job, done right
Series are identity-coded, so [palette] supplies a small validated categorical set (the dataviz reference palette: eight hues in a fixed order that maximises the minimum adjacent CVD separation), assigned by slot index and cycling only past the eighth. It is not a per-string hash: identity follows the slot, so a series keeps its colour regardless of how many others are present. Which column (light-surface vs dark-surface steps of the same eight hues) is used follows the theme's surface luminance — so the palette re-skins with the theme without ever baking a colour into the component. Text (axis ticks, legend labels) wears the theme ink; only the legend swatch and the mark carry the series colour.
Introspection (the robot seam)
state_json is richer than the raw model (Form 3): it adds series_count, the derived x_bounds/y_bounds, and a per-series {label, points} summary a robot reads without rendering. update_json deserializes a Msg straight through update. The robot test drives the full loop headless: feed series → assert count + bounds → set_kind → assert the kind flipped (line→bar→scatter). Each painted series/bar also rides into the AccessKit tree via facett_core::a11y::node (FC-4) carrying its point count / y value, so egui_kittest can query it.
Constraints
- Theme-aware, nothing hardcoded — the surface, axes, gridlines, and ink read the facett
Theme; series colour comes from the theme-selected categorical set. - Self-contained, no network — deps are only
facett-core,egui,serde/serde_json. The optionaltestmatrixfeature (a passthrough tofacett-core/testmatrix) turns the render-lane + robot self-reports into nornir warehouse rows; compiled OUT of the default build. - Additive —
ChartViewsits beside the existing box/violin/heatmap/ candlestick/treemap views; nothing about them changes.