@motionscript/charts 0.1.0 → 0.1.1-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/dist/bar-chart/bar-chart.d.ts +22 -93
- package/dist/bar-chart/bar-chart.d.ts.map +1 -1
- package/dist/bar-chart/bar-chart.js +48 -121
- package/dist/bar-chart/bar-chart.js.map +1 -1
- package/dist/bar-chart/category-axis.d.ts +11 -38
- package/dist/bar-chart/category-axis.d.ts.map +1 -1
- package/dist/bar-chart/category-axis.js +15 -38
- package/dist/bar-chart/category-axis.js.map +1 -1
- package/dist/bar-chart/chart-bars.d.ts +43 -74
- package/dist/bar-chart/chart-bars.d.ts.map +1 -1
- package/dist/bar-chart/chart-bars.js +31 -59
- package/dist/bar-chart/chart-bars.js.map +1 -1
- package/dist/bar-chart/index.d.ts +2 -10
- package/dist/bar-chart/index.d.ts.map +1 -1
- package/dist/bar-chart/index.js +0 -7
- package/dist/bar-chart/index.js.map +1 -1
- package/dist/bar-chart/shared.d.ts +27 -90
- package/dist/bar-chart/shared.d.ts.map +1 -1
- package/dist/bar-chart/shared.js +15 -54
- package/dist/bar-chart/shared.js.map +1 -1
- package/dist/browser/chunks/chunk-4SMRCU24.js +2 -0
- package/dist/browser/chunks/chunk-4SMRCU24.js.map +7 -0
- package/dist/browser/index.js +1 -1
- package/dist/browser/index.js.map +3 -3
- package/dist/browser/kit.js +1 -1
- package/dist/browser/manifest.json +1 -1
- package/dist/engine.d.ts +4 -16
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +4 -16
- package/dist/engine.js.map +1 -1
- package/dist/kit/cartesian.d.ts +8 -28
- package/dist/kit/cartesian.d.ts.map +1 -1
- package/dist/kit/cartesian.js +7 -19
- package/dist/kit/cartesian.js.map +1 -1
- package/dist/kit/chart-border.d.ts +9 -18
- package/dist/kit/chart-border.d.ts.map +1 -1
- package/dist/kit/chart-border.js +4 -9
- package/dist/kit/chart-border.js.map +1 -1
- package/dist/kit/chart-canvas.d.ts +25 -36
- package/dist/kit/chart-canvas.d.ts.map +1 -1
- package/dist/kit/chart-canvas.js +13 -13
- package/dist/kit/chart-canvas.js.map +1 -1
- package/dist/kit/chart-legend.d.ts +14 -61
- package/dist/kit/chart-legend.d.ts.map +1 -1
- package/dist/kit/chart-legend.js +17 -65
- package/dist/kit/chart-legend.js.map +1 -1
- package/dist/kit/entries.d.ts +2 -0
- package/dist/kit/entries.d.ts.map +1 -1
- package/dist/kit/entries.js +2 -0
- package/dist/kit/entries.js.map +1 -1
- package/dist/kit/motion.d.ts +11 -3
- package/dist/kit/motion.d.ts.map +1 -1
- package/dist/kit/motion.js +7 -1
- package/dist/kit/motion.js.map +1 -1
- package/dist/kit/shared.d.ts +60 -175
- package/dist/kit/shared.d.ts.map +1 -1
- package/dist/kit/shared.js +40 -157
- package/dist/kit/shared.js.map +1 -1
- package/dist/kit/vertical-axis.d.ts +16 -45
- package/dist/kit/vertical-axis.d.ts.map +1 -1
- package/dist/kit/vertical-axis.js +17 -42
- package/dist/kit/vertical-axis.js.map +1 -1
- package/dist/line-chart/chart-line.d.ts +16 -41
- package/dist/line-chart/chart-line.d.ts.map +1 -1
- package/dist/line-chart/chart-line.js +14 -43
- package/dist/line-chart/chart-line.js.map +1 -1
- package/dist/line-chart/chart-region.d.ts +14 -27
- package/dist/line-chart/chart-region.d.ts.map +1 -1
- package/dist/line-chart/chart-region.js +8 -22
- package/dist/line-chart/chart-region.js.map +1 -1
- package/dist/line-chart/horizontal-axis.d.ts +16 -43
- package/dist/line-chart/horizontal-axis.d.ts.map +1 -1
- package/dist/line-chart/horizontal-axis.js +13 -42
- package/dist/line-chart/horizontal-axis.js.map +1 -1
- package/dist/line-chart/index.d.ts +1 -13
- package/dist/line-chart/index.d.ts.map +1 -1
- package/dist/line-chart/index.js +0 -12
- package/dist/line-chart/index.js.map +1 -1
- package/dist/line-chart/line-chart.d.ts +23 -109
- package/dist/line-chart/line-chart.d.ts.map +1 -1
- package/dist/line-chart/line-chart.js +45 -141
- package/dist/line-chart/line-chart.js.map +1 -1
- package/dist/line-chart/shared.d.ts +33 -107
- package/dist/line-chart/shared.d.ts.map +1 -1
- package/dist/line-chart/shared.js +28 -81
- package/dist/line-chart/shared.js.map +1 -1
- package/dist/line-chart/x-scale.d.ts +14 -55
- package/dist/line-chart/x-scale.d.ts.map +1 -1
- package/dist/line-chart/x-scale.js +29 -111
- package/dist/line-chart/x-scale.js.map +1 -1
- package/dist/nodes.d.ts +0 -12
- package/dist/nodes.d.ts.map +1 -1
- package/dist/nodes.js +0 -12
- package/dist/nodes.js.map +1 -1
- package/dist/pie-chart/index.d.ts +1 -12
- package/dist/pie-chart/index.d.ts.map +1 -1
- package/dist/pie-chart/index.js +0 -9
- package/dist/pie-chart/index.js.map +1 -1
- package/dist/pie-chart/pie-chart.d.ts +37 -71
- package/dist/pie-chart/pie-chart.d.ts.map +1 -1
- package/dist/pie-chart/pie-chart.js +31 -65
- package/dist/pie-chart/pie-chart.js.map +1 -1
- package/dist/pie-chart/pie-legend.d.ts +11 -26
- package/dist/pie-chart/pie-legend.d.ts.map +1 -1
- package/dist/pie-chart/pie-legend.js +14 -39
- package/dist/pie-chart/pie-legend.js.map +1 -1
- package/dist/pie-chart/pie-ring.d.ts +50 -192
- package/dist/pie-chart/pie-ring.d.ts.map +1 -1
- package/dist/pie-chart/pie-ring.js +75 -247
- package/dist/pie-chart/pie-ring.js.map +1 -1
- package/dist/pie-chart/shared.d.ts +45 -131
- package/dist/pie-chart/shared.d.ts.map +1 -1
- package/dist/pie-chart/shared.js +29 -72
- package/dist/pie-chart/shared.js.map +1 -1
- package/package.json +4 -4
- package/src/bar-chart/bar-chart.ts +51 -165
- package/src/bar-chart/category-axis.ts +16 -57
- package/src/bar-chart/chart-bars.ts +43 -120
- package/src/bar-chart/index.ts +2 -10
- package/src/bar-chart/shared.ts +29 -97
- package/src/engine.ts +4 -16
- package/src/kit/cartesian.ts +11 -26
- package/src/kit/chart-border.ts +6 -20
- package/src/kit/chart-canvas.ts +15 -40
- package/src/kit/chart-legend.ts +17 -86
- package/src/kit/entries.ts +4 -1
- package/src/kit/motion.ts +11 -2
- package/src/kit/shared.ts +58 -219
- package/src/kit/vertical-axis.ts +18 -71
- package/src/line-chart/chart-line.ts +16 -44
- package/src/line-chart/chart-region.ts +10 -23
- package/src/line-chart/horizontal-axis.ts +13 -42
- package/src/line-chart/index.ts +1 -13
- package/src/line-chart/line-chart.ts +50 -184
- package/src/line-chart/shared.ts +51 -145
- package/src/line-chart/x-scale.ts +31 -121
- package/src/nodes.ts +0 -12
- package/src/pie-chart/index.ts +1 -12
- package/src/pie-chart/pie-chart.ts +34 -102
- package/src/pie-chart/pie-legend.ts +14 -55
- package/src/pie-chart/pie-ring.ts +80 -307
- package/src/pie-chart/shared.ts +50 -139
- package/dist/browser/chunks/chunk-TJNHLQGV.js +0 -2
- package/dist/browser/chunks/chunk-TJNHLQGV.js.map +0 -7
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/line-chart/line-chart.ts", "../../src/line-chart/chart-line.ts", "../../src/line-chart/shared.ts", "../../src/line-chart/chart-region.ts", "../../src/line-chart/horizontal-axis.ts", "../../src/line-chart/x-scale.ts", "../../src/bar-chart/bar-chart.ts", "../../src/bar-chart/category-axis.ts", "../../src/bar-chart/chart-bars.ts", "../../src/bar-chart/shared.ts", "../../src/pie-chart/pie-chart.ts", "../../src/pie-chart/pie-legend.ts", "../../src/pie-chart/pie-ring.ts", "../../src/pie-chart/shared.ts", "../../src/nodes.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n Rect,\n Types,\n component2d,\n easeOut,\n type ChildView,\n type DataRecord,\n type EntryOptions,\n type NodeOf,\n // Named so this component's emitted type reaches `Node2D` through the sdk, not core.\n type Node2D,\n type Node2DProps,\n type Size2D,\n type Timeline,\n type Vector2,\n} from \"@motionscript/sdk\"\nimport { formatNumber } from \"@motionscript/sdk/component\"\n\nimport { cartesianFrame, cartesianViews, laidOutSize } from \"../kit/cartesian\"\nimport { ChartBorder } from \"../kit/chart-border\"\nimport { ChartCanvas } from \"../kit/chart-canvas\"\nimport { ChartLegend } from \"../kit/chart-legend\"\nimport { asParts, fresh } from \"../kit/entries\"\nimport { beats, type Beat } from \"../kit/motion\"\nimport { VerticalAxis } from \"../kit/vertical-axis\"\nimport {\n SPOTLIGHT_DIM,\n formatMarker,\n lerpAxisDomain,\n lerpAxisEntry,\n markerStep,\n markerValues,\n resolveAxisDomain,\n resolveSeriesFill,\n resolveSeriesStroke,\n settleAxisDomain,\n type AxisDomainConfig,\n type Range,\n} from \"../kit/shared\"\nimport { ChartLine } from \"./chart-line\"\nimport { ChartRegion } from \"./chart-region\"\nimport { HorizontalAxis } from \"./horizontal-axis\"\nimport {\n DEFAULT_CHART_THEME,\n lerpChartTheme,\n resolveChartTheme,\n resolveRegionBox,\n resolveYScale,\n seriesPoints,\n type ChartConfig,\n type ChartTheme,\n type LineSeries,\n type PlotRegion,\n type RegionBox,\n} from \"./shared\"\nimport { resolveXScale, type XScale } from \"./x-scale\"\n\nexport interface LineChartProps extends Node2DProps {\n /** One row per sample — a plain record of field name to value. */\n data: DataRecord[]\n /**\n * Key into each row for its x value, shared by every series. Named `xField`\n * rather than `x` because every node already has an `x` — its position.\n */\n xField: string\n /** One polyline per entry: which field it plots, its legend `label`, its `fill`. */\n series: LineSeries[]\n /** X-axis data domain: caption, scale range, and tick placement. */\n xAxis?: AxisDomainConfig\n /** Y-axis data domain: caption, scale range, and tick placement. */\n yAxis?: AxisDomainConfig\n /**\n * A slice of the plot to spotlight, in **data units**. Dashed rules mark the\n * bounded edges and every series fades outside the box — see\n * `spotlightRegion`, which animates the same thing. `null` (the default) is no\n * region.\n */\n region?: PlotRegion | null\n /**\n * The chart's visual theme — series palette, axis text/grid/gutters, plot\n * panel paint, and legend styling. Every branch is optional and deep-merged\n * onto {@link DEFAULT_CHART_THEME}, so pass only what you want to override.\n */\n theme?: ChartTheme\n /** Whether the legend row below the plot is drawn at all. */\n showLegend?: boolean\n}\n\n/** Both axes' domains with their ranges and ticks resolved. */\nexport interface LineChartDomains {\n readonly x: AxisDomainConfig & { readonly range: Range; readonly ticks: number[] }\n readonly y: AxisDomainConfig & { readonly range: Range; readonly ticks: number[] }\n}\n\n/** What a line chart plots, as its entries hold it. */\nexport interface LineChartValues {\n readonly data: DataRecord[]\n readonly xField: string\n readonly series: LineSeries[]\n readonly xAxis: AxisDomainConfig\n readonly yAxis: AxisDomainConfig\n}\n\n/**\n * The argument every per-series command takes: an index into `series`. Its own\n * kind rather than `number` so a host can offer the series by name.\n */\nconst SERIES_ARG = Types.custom<number, number, EntryOptions & { readonly min: number; readonly step: number }>(0, { kind: \"series\", min: 0, step: 1 })\n\n/** One argument, two controls: the commands take a `PlotRegion`, so the x and y spans a host draws are declared inside it. */\nconst REGION_ARG = Types.custom<PlotRegion | undefined>(undefined, {\n kind: \"region\",\n properties: {\n x: { kind: \"range\", default: true, defaultMin: 0, defaultMax: 100 },\n y: { kind: \"range\", default: false, defaultMin: 0, defaultMax: 100 },\n },\n})\n\n/** How far the legend starts below its slot when it enters, in px. */\nconst LEGEND_SLIDE = 20\n\n/** Fallback marker count when neither axis's ticks are given. */\nconst DEFAULT_MARKERS = 5\n\n/**\n * How `enter` divides its duration between its three beats: the axes arriving,\n * the lines tracing, and the legend coming up. Shares of the whole, so they must\n * sum to 1 — the entrance takes exactly the time it was given.\n */\nconst AXES_SHARE = 0.35\nconst LINES_SHARE = 0.5\nconst LEGEND_SHARE = 0.15\n\n/** What the chart's own commands read: its entries, and the private `home` a zoom remembers. */\ninterface LineState extends LineChartValues {\n readonly theme: ChartConfig\n readonly region: PlotRegion | null\n readonly regionReveal: number\n readonly home: LineChartDomains | null\n}\n\n/**\n * What the x column *is* — a number, an instant, or a place in the file — and\n * with it how each row is positioned, ticked and captioned. See `x-scale.ts`.\n */\nfunction columnOf(s: LineChartValues, format = s.xAxis.format): XScale {\n return resolveXScale(s.data, s.xField, format)\n}\n\n/**\n * The horizontal axis with its range and ticks resolved: the explicit ones, else\n * the column's extent and five ticks across it, asked of the column because only\n * a numeric one has round numbers between its samples.\n */\nfunction drawnX(s: LineChartValues, domain: AxisDomainConfig): AxisDomainConfig {\n const column = columnOf(s, domain.format)\n const range = domain.range ?? column.extent\n return { ...domain, range, ticks: domain.ticks ?? column.ticks(range, DEFAULT_MARKERS - 1) }\n}\n\n/**\n * The vertical axis with its range and ticks resolved. Auto-ranging steps into\n * one fewer segment than the intended marker count, so the tidy bounds frame the\n * ticks the axis draws.\n */\nfunction drawnY(s: LineChartValues, domain: AxisDomainConfig): AxisDomainConfig {\n const divisions = (domain.ticks?.length ?? DEFAULT_MARKERS) - 1\n const range = resolveYScale(s.data, s.series, domain.range, divisions)\n return { ...domain, range, ticks: domain.ticks ?? markerValues(range, DEFAULT_MARKERS - 1) }\n}\n\n/** The last domains worked out from each data array, and what else they were read from. */\nconst domainsDrawn = new WeakMap<\n DataRecord[],\n Pick<LineChartValues, \"xField\" | \"xAxis\" | \"yAxis\" | \"series\"> & { readonly domains: LineChartDomains }\n>()\n\n/** Both axes as a line chart draws them now, an auto range or tick row read back as itself. */\nexport function lineChartDomains(s: LineChartValues): LineChartDomains {\n const { data, xField, xAxis, yAxis, series } = s\n const kept = domainsDrawn.get(data)\n if (kept && kept.xField === xField && kept.xAxis === xAxis && kept.yAxis === yAxis && kept.series === series) {\n return kept.domains\n }\n const domains = {\n x: settleAxisDomain(xAxis, (d) => drawnX(s, d)) as LineChartDomains[\"x\"],\n y: settleAxisDomain(yAxis, (d) => drawnY(s, d)) as LineChartDomains[\"y\"],\n }\n domainsDrawn.set(data, { xField, xAxis, yAxis, series, domains })\n return domains\n}\n\n/**\n * Map one series' data points into the plot's **centred** pixel space (origin at\n * the plot centre, y-up): x runs `[-w/2, +w/2]`, y runs `[-h/2, +h/2]`.\n */\nfunction seriesPixels(s: LineChartValues, domains: LineChartDomains, index: number, plot: Size2D): Vector2[] {\n const series = s.series[index]\n if (!series) return []\n const points = seriesPoints(s.data, columnOf(s), series)\n if (points.length === 0) return []\n const { width: w, height: h } = plot\n const [xMin, xMax] = domains.x.range\n const [yMin, yMax] = domains.y.range\n const xToPx = (x: number) => (xMax <= xMin ? 0 : ((x - xMin) / (xMax - xMin) - 0.5) * w)\n const yToPx = (y: number) => (yMax <= yMin ? 0 : ((y - yMin) / (yMax - yMin) - 0.5) * h)\n // `seriesPoints` already sorted them along x.\n return points.map(([x, y]) => ({ x: xToPx(x), y: yToPx(y) }))\n}\n\n/** `region` mapped into the plot's centred pixel space, against the live scales, so it travels with a zoom. */\nfunction regionBox(s: LineChartValues & { readonly region: PlotRegion | null }, domains: LineChartDomains, plot: Size2D): RegionBox {\n const { width: w, height: h } = plot\n return resolveRegionBox(s.region, { left: -w / 2, right: w / 2, bottom: -h / 2, top: h / 2 }, domains.x.range, domains.y.range)\n}\n\n/**\n * Whether a call-out is authored at all. Not whether it resolves to pixels: a\n * command is built before the first layout, and a region on a chart not yet\n * measured is a real one. A region bounding neither axis is none, as `null` is.\n */\nfunction hasCallout(s: { readonly region: PlotRegion | null }): boolean {\n const region = s.region\n return region != null && (region.x !== undefined || region.y !== undefined)\n}\n\n/** The `region` a document handed a command, or `{}`: a command is added before its arguments are filled in. */\nfunction regionArg(region: PlotRegion | null | undefined): PlotRegion {\n return region != null && typeof region === \"object\" ? region : {}\n}\n\n/** A range read as a span, whichever way round the author wrote it. */\nfunction span([a, b]: Range): Range {\n return a <= b ? [a, b] : [b, a]\n}\n\n/**\n * Rescale an axis to `range`, keeping the tick *count* it already has, so each\n * marker slides from its old value to its new one instead of the row popping.\n */\nfunction rescale(range: Range, ticks: readonly number[]): AxisDomainConfig {\n const to = span(range)\n return { range: to, ticks: markerValues(to, Math.max(2, ticks.length) - 1) }\n}\n\n/** The axis writes of a move from `from` toward `to` at `t`. */\nfunction axesAt(from: LineChartDomains, to: Partial<Record<\"x\" | \"y\", AxisDomainConfig>>, t: number) {\n return {\n ...(to.x && { xAxis: lerpAxisDomain(from.x, to.x, t) }),\n ...(to.y && { yAxis: lerpAxisDomain(from.y, to.y, t) }),\n }\n}\n\ntype AnyTimeline = Timeline<any, any>\n\n/** The parts a line chart's commands drive, found through its children. */\nfunction partsOf(children: readonly ChildView[]) {\n const frame = cartesianViews(children)\n if (!frame) return null\n const hAxis = frame.below.find(HorizontalAxis.is)\n if (!VerticalAxis.is(frame.axis) || !hAxis) return null\n return {\n vAxis: frame.axis,\n hAxis,\n lines: frame.plot.filter(ChartLine.is),\n legend: frame.below.find(ChartLegend.is) ?? null,\n }\n}\n\n/** Drop the call-out: the rules retract, the wash fades, and every series comes back to full. */\nfunction clearing(timeline: AnyTimeline, state: LineState) {\n const from = state.regionReveal\n const region = state.region\n // Nothing to retract, but the call still takes its slot on the timeline.\n if (from === 0) return timeline.animate(() => ({ region: null, regionReveal: 0 }), { eased: false })\n // `region` is held until the retraction lands, or there would be nothing left to retract.\n return timeline.animate((t) => (t >= 1 ? { region: null, regionReveal: 0 } : { region, regionReveal: from * (1 - t) }))\n}\n\n/** Trace series `index`'s line in (`show`) or back out, in lockstep with its legend entry. */\nfunction toggling(timeline: AnyTimeline, children: readonly ChildView[], index: number, show: boolean) {\n const parts = partsOf(children)\n const line = parts?.lines[index]\n if (!parts || !line) return timeline.wait(timeline.duration)\n return timeline.together([\n line.run(\"enter\", show ? { from: 0, to: 1 } : { from: 1, to: 0 }),\n ...(parts.legend ? [parts.legend.run(show ? \"showSeries\" : \"hideSeries\", { index })] : []),\n ])\n}\n\n/**\n * A multi-series line chart, composed from five independent parts rather than\n * one monolithic draw:\n *\n * - {@link ChartCanvas} — the plot panel with a two-way grid, under everything.\n * - {@link VerticalAxis} / {@link HorizontalAxis} — columns/rows of real text\n * markers that stagger in, each carrying its own axis caption.\n * - {@link ChartRegion} — the optional call-out marking a spotlit slice of the\n * plot: dashed rules on its bounded edges, and every series faded outside it.\n * - {@link ChartLine} (one per series) — the traced polylines over the canvas.\n * - {@link ChartLegend} — a row of entries below the chart, one per series.\n *\n * Every part reads the chart's entries through bindings, and the lines, the\n * region and the grid all map against the plot panel's own laid-out box, so\n * points land exactly on their markers and the chart re-flows if its box\n * changes.\n *\n * **Resting state is the finished chart**: an editor paints a node's Initial\n * state on a paused canvas. `enter` arms every part on its first frame.\n *\n * Past the entrance, `showSeries`/`hideSeries`/`spotlightSeries` pick out a\n * *series*, `spotlightRegion` marks a *slice of the plot* without changing the\n * scale, and `zoomTo` rescales the axes so a slice fills the plot. A spotlit\n * region resolves against the live scales, so it travels with a zoom. A `to` of\n * an axis `range` or `ticks` tweens from the domain as drawn (see\n * {@link lerpAxisEntry}), so an auto axis rescales continuously.\n */\nexport const LineChart = component2d({\n key: \"lineChart\",\n state: {\n data: fresh<DataRecord[]>(() => [], { kind: \"dataset\" }),\n xField: \"\",\n series: fresh<LineSeries[]>(() => [], { kind: \"series\" }),\n xAxis: Types.custom<AxisDomainConfig>({}, { resolve: resolveAxisDomain, lerp: lerpAxisEntry }),\n yAxis: Types.custom<AxisDomainConfig>({}, { resolve: resolveAxisDomain, lerp: lerpAxisEntry }),\n theme: Types.custom<ChartTheme, ChartConfig>(DEFAULT_CHART_THEME, { resolve: resolveChartTheme, lerp: lerpChartTheme }),\n /**\n * The spotlit slice of the plot in data units, or `null` for none. The box\n * snaps; the animation is {@link regionReveal} drawing the rules on.\n */\n region: Types.custom<PlotRegion | null>(null),\n /**\n * How far the `region` call-out is drawn in, `[0, 1]`: the rules grow out and\n * the wash fades up as it climbs, while every line's out-of-region stretch\n * fades to the theme's `region.dimOpacity`.\n */\n regionReveal: 0,\n /** Read as the chart is built: the legend is a part the chart builds or doesn't. */\n showLegend: Types.boolean(true, { animatable: false }),\n /**\n * The domain `zoomTo` first left, so `resetZoom` restores the chart as it\n * stood, auto-derived ticks included. Cleared by `resetZoom`, so the next zoom\n * remembers wherever the chart rests then.\n */\n home: Types.custom<LineChartDomains | null>(null, { private: true }),\n },\n // Its parts fill it; a node made of them would hug them.\n defaults: { width: \"fill\", height: \"fill\" },\n allowedChildren: [],\n children: (state) => {\n const theme = () => state.theme\n const domains = () => lineChartDomains(state)\n let plot: Node2D | undefined\n const plotSize = () => laidOutSize(plot)\n const enabled = state.series.map((s) => s.enabled ?? true)\n\n // Bound, not snapshotted, so a rescale re-labels and re-places the row as it\n // tweens. The marker *count* is read once, which is why `zoomTo` keeps it.\n const vAxis = new VerticalAxis({\n range: () => domains().y.range,\n markers: () => domains().y.ticks,\n // The y axis is always numbers, so its format is simply the pattern.\n captions: () => {\n const markers = domains().y.ticks\n const format = state.yAxis.format\n return markers.map((value) => (format === undefined ? formatMarker(value, markerStep(markers)) : formatNumber(value, format)))\n },\n axis: () => theme().yAxis,\n label: state.yAxis.label ?? \"\",\n width: \"hug\",\n height: \"fill\",\n })\n\n const hAxis = new HorizontalAxis({\n range: () => domains().x.range,\n markers: () => domains().x.ticks,\n // A tick on a month or category column is captioned with the cell it came\n // out of — `Jan`, not the instant it parsed to.\n captions: () => {\n const column = columnOf(state)\n return domains().x.ticks.map((value) => column.label(value))\n },\n axis: () => theme().xAxis,\n label: state.xAxis.label ?? \"\",\n width: \"fill\",\n height: \"hug\",\n })\n\n const lines = state.series.map(\n (s, i) =>\n new ChartLine({\n points: () => seriesPixels(state, domains(), i, plotSize()),\n stroke: () => resolveSeriesStroke(theme().seriesStyles, s, i),\n enabled: enabled[i],\n // Each line clips itself against the box the rules are drawn from, so\n // the dim/bright boundary can't drift from the dashes.\n spotlight: () => regionBox(state, domains(), plotSize()),\n // Ramped by the reveal that draws the rules: one signal, both halves.\n dimOpacity: () => 1 - (1 - theme().region.dimOpacity) * state.regionReveal,\n }) as unknown as Node2D\n )\n\n const below: Node2D[] = [hAxis as unknown as Node2D]\n if (state.showLegend !== false) {\n below.push(\n new ChartLegend({\n series: state.series,\n // The legend paints a swatch, not a line, so it is handed the colour each\n // stroke resolves to rather than the stroke itself.\n swatches: () => state.series.map((s, i) => resolveSeriesFill(theme().seriesStyles, s, i)),\n legend: () => theme().legend,\n // The row's spacing is the theme's as the chart was built, like the\n // entries' type and chips: a theme tween re-paints the swatches only.\n gap: state.theme.legend.itemGap,\n padding: state.theme.legend.padding,\n }) as unknown as Node2D\n )\n }\n\n plot = new Rect({\n clip: true,\n children: [\n new ChartCanvas({\n fill: () => theme().plotArea.fill,\n shadow: () => theme().plotArea.shadow,\n xGridStroke: () => theme().xAxis.lineStyle,\n yGridStroke: () => theme().yAxis.lineStyle,\n // A grid line under every axis marker; the canvas skips the edges.\n xMarkers: () => domains().x.ticks,\n yMarkers: () => domains().y.ticks,\n xExtent: () => domains().x.range,\n yExtent: () => domains().y.range,\n }),\n // Over the grid, under the lines, so a spotlit stretch of a series isn't\n // tinted by the highlight meant to pick it out.\n new ChartRegion({\n box: () => regionBox(state, domains(), plotSize()),\n stroke: () => theme().region.stroke,\n fill: () => theme().region.fill,\n reveal: () => state.regionReveal,\n }),\n ...lines,\n new ChartBorder({ stroke: () => theme().plotArea.stroke }),\n ],\n }) as unknown as Node2D\n\n const gutter = new VerticalAxis({\n opacity: 0,\n range: () => domains().y.range,\n markers: () => domains().y.ticks,\n axis: () => theme().yAxis,\n label: state.yAxis.label ?? \"\",\n width: \"hug\",\n height: 0,\n }) as unknown as Node2D\n\n return asParts([cartesianFrame({ axis: vAxis as unknown as Node2D, plot, gutter, below })])\n },\n commands: {\n /**\n * Animate the whole chart in: the two axes stagger their markers in, the\n * series lines trace left to right, and the legend fades up last. Its first\n * frame arms every part: markers shifted and transparent, lines untraced,\n * the legend dropped and transparent.\n *\n * `duration` is the **whole** entrance; the beats are shares of it. A chart\n * authored with a `region` lands its call-out last, in the final quarter.\n */\n enter: {\n args: {},\n // Held from the chart's arrival, so it never shows finished ahead of its draw.\n entrance: true,\n easing: easeOut(),\n run: (timeline, state, children) => {\n const parts = partsOf(children)\n if (!parts) return timeline.wait(timeline.duration)\n const duration = timeline.duration\n const revealing = hasCallout(state) && state.regionReveal === 0\n const body = revealing ? duration * 0.75 : duration\n const axes = body * AXES_SHARE\n const traced = body * LINES_SHARE\n const { legend } = parts\n const at0 = (piece: Beat[\"piece\"]): Beat => ({ at: 0, duration: 0, piece })\n // The first frame arms what every later beat moves: a beat ahead of the\n // playhead writes nothing. A beat with nothing to do still takes its share.\n return beats(timeline, [\n ...(revealing ? [at0(timeline.animate(() => ({ regionReveal: 0 })))] : []),\n ...parts.lines.map((line) => at0(line.to({ progress: 0 }))),\n ...(legend ? [at0(legend.to({ opacity: 0, y: -LEGEND_SLIDE }))] : []),\n { at: 0, duration: axes, piece: parts.vAxis.run(\"enter\", { slide: 24 }) },\n { at: 0, duration: axes, piece: parts.hAxis.run(\"enter\", { slide: 24 }) },\n ...parts.lines.map((line) => ({ at: axes, duration: traced, piece: line.run(\"enter\") })),\n ...(legend ? [{ at: axes + traced, duration: body * LEGEND_SHARE, piece: legend.to({ opacity: 1, y: 0 }) }] : []),\n ...(revealing ? [{ at: body, duration: duration * 0.25, piece: timeline.animate((t) => ({ regionReveal: t })) }] : []),\n ])\n },\n },\n /**\n * Spotlight a rectangular slice of the plot given in **data units**: dashed\n * rules draw onto each bounded edge while every series fades outside the box.\n * Either axis alone is a band; both are a box. Called while another region is\n * up, the old call-out retracts over the first half and the new one draws in\n * over the second, each half eased on its own.\n */\n spotlightRegion: {\n args: { region: REGION_ARG },\n easing: easeOut(),\n run: (timeline, state) => {\n const region = regionArg(timeline.args.region)\n const from = state.regionReveal\n const previous = state.region\n // A region bounding neither axis is no call-out: spotlighting it is a clear.\n if (region.x === undefined && region.y === undefined) return clearing(timeline, state)\n // Nothing up yet: the region is set from the first frame and just draws on.\n if (from <= 0 || !hasCallout(state)) return timeline.animate((t) => ({ region, regionReveal: from + (1 - from) * t }))\n return timeline.animate(\n (t) => (t < 0.5\n ? { region: previous, regionReveal: from * (1 - timeline.ease(t * 2)) }\n : { region, regionReveal: timeline.ease(t * 2 - 1) }),\n { eased: false }\n )\n },\n },\n /** Drop the region call-out. A no-op when nothing is spotlit, which still takes its slot. */\n clearRegion: {\n args: {},\n easing: easeOut(),\n run: (timeline, state) => clearing(timeline, state),\n },\n /**\n * Zoom the plot to a slice of itself, given in **data units**: the named axes\n * rescale so that slice fills the plot, the grid, both marker rows and every\n * series following. An omitted axis is left as it is. The first zoom\n * remembers the domain it left, for `resetZoom`.\n */\n zoomTo: {\n args: { region: REGION_ARG },\n easing: easeOut(),\n run: (timeline, state) => {\n const region = regionArg(timeline.args.region)\n if (!region.x && !region.y) return timeline.wait(timeline.duration)\n const from = lineChartDomains(state)\n const home = state.home ?? from\n const to = {\n ...(region.x && { x: { ...from.x, ...rescale(region.x, from.x.ticks) } }),\n ...(region.y && { y: { ...from.y, ...rescale(region.y, from.y.ticks) } }),\n }\n return timeline.animate((t) => ({ home, ...axesAt(from, to, t) }))\n },\n },\n /** Pull back out to the domain the first zoom left — the mirror of `zoomTo`, and a hold if it never zoomed. */\n resetZoom: {\n args: {},\n easing: easeOut(),\n run: (timeline, state) => {\n const home = state.home\n if (!home) return timeline.wait(timeline.duration)\n const from = lineChartDomains(state)\n return timeline.animate((t) => ({ home: null, ...axesAt(from, home, t) }))\n },\n },\n /**\n * Show series `index`: **trace** its line in left to right, in lockstep with\n * its legend entry growing in.\n */\n showSeries: {\n args: { seriesIndex: SERIES_ARG },\n easing: easeOut(),\n run: (timeline, _state, children) => toggling(timeline, children, timeline.args.seriesIndex, true),\n },\n /** Hide series `index`: **retrace** its line back out as its legend entry shrinks away. */\n hideSeries: {\n args: { seriesIndex: SERIES_ARG },\n easing: easeOut(),\n run: (timeline, _state, children) => toggling(timeline, children, timeline.args.seriesIndex, false),\n },\n /**\n * Fade every other series — each line **and its legend entry** — to focus on\n * `index`, which comes back to full. The legend dims in place, so the row\n * doesn't reflow.\n */\n spotlightSeries: {\n args: { seriesIndex: SERIES_ARG },\n easing: easeOut(),\n run: (timeline, _state, children) => {\n const parts = partsOf(children)\n if (!parts) return timeline.wait(timeline.duration)\n const index = timeline.args.seriesIndex\n return timeline.together([\n ...parts.lines.map((line, i) => line.to({ opacity: i === index ? 1 : SPOTLIGHT_DIM })),\n ...(parts.legend ? [parts.legend.run(\"spotlightSeries\", { index })] : []),\n ])\n },\n },\n },\n})\n\nexport type LineChart = NodeOf<typeof LineChart>\n", "import {\n Graphics2D,\n Types,\n component2d,\n strokeOps,\n type NodeOf,\n // Named so this component's emitted type reaches `Node2D` through the sdk, not core.\n type Node2D as _Node2D,\n type Stroke,\n type StrokeResolved,\n type Vector2,\n} from \"@motionscript/sdk\"\n\nimport { fresh } from \"../kit/entries\"\nimport { frameEase } from \"../kit/motion\"\nimport { loadPaints } from \"../kit/paints\"\nimport { SPOTLIGHT_DIM } from \"../kit/shared\"\nimport {\n EMPTY_REGION_BOX,\n clipPolylineToBox,\n hasRegion,\n type RegionBox,\n} from \"./shared\"\n\n/**\n * The prefix of `pts` revealed at fraction `p ∈ [0, 1]`, measuring the reveal\n * by *cumulative arc length* so the line traces at a steady pace regardless of\n * uneven point spacing. Returns the whole-point prefix plus one interpolated\n * point partway along the active segment.\n */\nfunction tracePoints(pts: Vector2[], p: number): Vector2[] {\n if (p >= 1 || pts.length < 2) return pts\n if (p <= 0) return []\n\n const segLen: number[] = []\n let total = 0\n for (let i = 1; i < pts.length; i++) {\n const d = Math.hypot(pts[i].x - pts[i - 1].x, pts[i].y - pts[i - 1].y)\n segLen.push(d)\n total += d\n }\n if (total === 0) return pts\n\n let target = total * p\n const out: Vector2[] = [pts[0]]\n for (let i = 0; i < segLen.length; i++) {\n if (target >= segLen[i]) {\n out.push(pts[i + 1])\n target -= segLen[i]\n } else {\n const t = segLen[i] === 0 ? 0 : target / segLen[i]\n out.push({\n x: pts[i].x + (pts[i + 1].x - pts[i].x) * t,\n y: pts[i].y + (pts[i + 1].y - pts[i].y) * t,\n })\n break\n }\n }\n return out\n}\n\n/**\n * A single series line of a {@link LineChart}: one rounded polyline traced\n * through pre-mapped pixel `points`, drawn as one {@link Graphics2D} command list.\n * Sized and positioned by the parent, which shares the plot's coordinate frame.\n *\n * Two signals drive it: `progress` traces the line in\n * (animated by `enter`) and `enabled` shows or hides it wholesale.\n */\nexport const ChartLine = component2d({\n key: \"chartLine\",\n state: {\n /** The series polyline, already mapped into this node's local pixel space (origin at centre, y-up) by the parent {@link LineChart}. Ordered along x. */\n points: fresh<Vector2[]>(() => []),\n /** Line colour + weight (+ optional dash/cap/join) — the per-series stroke. */\n stroke: Types.custom<Stroke, StrokeResolved[]>([], { resolve: strokeOps.resolve, lerp: strokeOps.lerp }),\n /**\n * Reveal of the line in `[0, 1]`: 0 hides it, 1 traces it fully from its left\n * end to its right. Defaults to 1, so the paused editor canvas paints the line;\n * `enter` drives it from 0 regardless of where it rests.\n */\n progress: 1,\n /** Whether this line is drawn at all: the switch the chart flips alongside its legend's per-series toggle. */\n enabled: true,\n /** A slice of the plot to spotlight, in this node's own pixel space: outside it the line drops to `dimOpacity`. */\n spotlight: fresh<RegionBox>(() => EMPTY_REGION_BOX),\n /** Opacity the out-of-region stretches fade to. */\n dimOpacity: SPOTLIGHT_DIM,\n },\n load: (loader, state) => loadPaints(loader, { strokes: [state.stroke] }),\n commands: {\n /**\n * Trace the line from `from` to `to` over the command — by default in from\n * its left end to its right. `showSeries` and `hideSeries` are the same\n * motion in opposite directions, so both are this with their ends stated.\n *\n * `enabled` rides along in the same value rather than being set before or\n * after the command: a line is drawable for every `t` the trace covers and\n * undrawn outside it, which is what makes arriving at a time backwards show\n * what arriving forwards does.\n */\n enter: {\n args: { from: Types.number(0, { min: 0, max: 1 }), to: Types.number(1, { min: 0, max: 1 }) },\n run: (timeline) => {\n const { from, to } = timeline.args\n const ease = frameEase(timeline)\n return timeline.animate((t) => {\n const progress = from + (to - from) * ease(t)\n // Hidden only once it has fully retraced, so the last frame of a hide is\n // the empty plot rather than a line that vanished a frame early.\n return { progress, enabled: progress > 0 }\n })\n },\n },\n },\n render: (ctx, state) => {\n if (!state.enabled) return\n const pts = state.points\n if (pts.length < 2) return\n\n const p = Math.max(0, Math.min(1, state.progress))\n if (p <= 0) return\n\n const drawn = tracePoints(pts, p)\n if (drawn.length < 2) return\n const stroke = state.stroke\n const radius = (stroke[0]?.weight ?? 0) * 2 // round the joins\n\n const box = state.spotlight\n if (!hasRegion(box)) {\n ctx.draw(new Graphics2D().line({ points: drawn, radius }).stroke(stroke))\n return\n }\n\n // Spotlit: the whole line goes down to `dimOpacity`, then the stretches\n // inside the region are re-drawn over it at full strength. Two passes rather\n // than one dim + one bright *complementary* pair, so the bright run has\n // continuous geometry through the region and its joins round the way an\n // unspotlit line's do — and so the boundary lands exactly on the region edge.\n ctx.draw(\n new Graphics2D()\n .line({ points: drawn, radius })\n .stroke(stroke)\n .opacity(Math.max(0, Math.min(1, state.dimOpacity)))\n )\n\n // One `draw` per run: shapes chained before a `stroke()` are combined into a\n // single surface, and stroking a union of disjoint open paths doesn't paint\n // — so a line that leaves the region and comes back would lose both halves.\n for (const run of clipPolylineToBox(drawn, box)) {\n ctx.draw(new Graphics2D().line({ points: run, radius }).stroke(stroke))\n }\n },\n})\n\nexport type ChartLine = NodeOf<typeof ChartLine>\n", "/**\n * Types and pure helpers specific to the {@link LineChart} family: the traced\n * polylines, the spotlit region they dim outside of, and the theme those two\n * add to the shared chart vocabulary.\n *\n * Everything a bar or a pie chart would want too — what a series is, how an axis\n * is scaled and formatted, the theme leaf resolvers, the legend and plot-panel\n * branches — lives in `../kit/shared.ts` and is imported here. What's\n * left below is what only a line chart has: points sorted along an x column,\n * a region clipped out of a polyline, and the `region` branch of the theme.\n *\n * Kept renderer-agnostic (no `Node` imports) so every part shares one\n * scale/format/palette/theme vocabulary, and so a host's own inspector\n * mapping can build a partial {@link ChartTheme} without\n * pulling the components in.\n */\n\nimport {\n Fills,\n fillOps,\n lerpNumber,\n strokeOps,\n type DataRecord,\n type Fill,\n type FillResolved,\n type Stroke,\n type StrokeResolved,\n} from \"@motionscript/sdk\"\n\nimport {\n CHART_PALETTE,\n DEFAULT_LEGEND,\n DEFAULT_PLOT_AREA,\n DEFAULT_SERIES_STROKES,\n DEFAULT_X_AXIS,\n DEFAULT_Y_AXIS,\n SPOTLIGHT_DIM,\n lerpAxisConfig,\n lerpLegendConfig,\n lerpPlotArea,\n lerpSeriesStrokes,\n niceStep,\n num,\n resolveAxisConfig,\n resolveLegendConfig,\n resolvePlotArea,\n resolveSeriesStrokes,\n type AxisConfig,\n type ChartSeries,\n type LegendConfig,\n type LegendTheme,\n type PlotAreaConfig,\n type Range,\n} from \"../kit/shared\"\nimport type { XScale } from \"./x-scale\"\n\n/**\n * One series in a {@link LineChart}: a polyline traced through the values in one\n * column of the chart's `data`, drawn in `fill` (or the theme's `seriesStyles`\n * palette when omitted) and captioned by `label` in the legend.\n *\n * The family-wide {@link ChartSeries} under a name that says what it draws here;\n * the chart's own `xField` prop names the column carrying the shared x values,\n * so every series is read off the same rows — one row per sample, one column per\n * series.\n */\nexport type LineSeries = ChartSeries\n\n// ---- Plot regions -------------------------------------------------------\n\n/**\n * A rectangular slice of the plot, in **data units** — the author-facing input\n * to {@link LineChart.spotlightRegion} / {@link LineChart.zoomTo}.\n *\n * Either axis may be omitted, and that's the point: `{ x: [60, 100] }` is a\n * vertical band spanning the full height, `{ y: [8, 10] }` a horizontal band\n * spanning the full width, and both together a box. A reversed range is read\n * the same as the forward one — a region is a span, not a direction.\n */\nexport interface PlotRegion {\n /** Horizontal span in x-axis data units. Omit to span the full plot width. */\n x?: Range\n /** Vertical span in y-axis data units. Omit to span the full plot height. */\n y?: Range\n}\n\n/**\n * A {@link PlotRegion} resolved into the plot's **centred pixel space** (origin\n * at the plot centre, y-up) — the same local frame the series points live in,\n * so a line can be clipped against it directly.\n *\n * `xBounded`/`yBounded` carry which axes the author actually constrained: an\n * unbounded axis has its edges pinned to the plot's own and draws no rule.\n */\nexport interface RegionBox {\n left: number\n right: number\n bottom: number\n top: number\n /** The author gave an x range — the left/right edges are real data bounds. */\n xBounded: boolean\n /** The author gave a y range — the bottom/top edges are real data bounds. */\n yBounded: boolean\n}\n\n/** The empty box, used as the \"no region\" resting value. */\nexport const EMPTY_REGION_BOX: RegionBox = {\n left: 0,\n right: 0,\n bottom: 0,\n top: 0,\n xBounded: false,\n yBounded: false,\n}\n\n/** Is `box` a real, non-degenerate region — i.e. is there anything to spotlight? */\nexport function hasRegion(box: RegionBox): boolean {\n return box.right > box.left && box.top > box.bottom\n}\n\n/**\n * Resolve a {@link PlotRegion} against the plot's pixel box and the chart's\n * scales. `plot` is the plot rectangle in centred pixel space, and\n * `xScale`/`yScale` the data ranges those edges span.\n *\n * An omitted axis takes the plot's own edges, so the region spans it. A given\n * axis is clamped to the plot, so a range running off the scale still produces\n * a box inside the panel.\n */\nexport function resolveRegionBox(\n region: PlotRegion | null | undefined,\n plot: { left: number; right: number; bottom: number; top: number },\n xScale: Range,\n yScale: Range\n): RegionBox {\n if (!region || (region.x === undefined && region.y === undefined)) {\n return EMPTY_REGION_BOX\n }\n\n const span = (\n range: Range | undefined,\n [min, max]: Range,\n lo: number,\n hi: number\n ): [number, number, boolean] => {\n if (range === undefined || max <= min) return [lo, hi, false]\n const a = Math.min(range[0], range[1])\n const b = Math.max(range[0], range[1])\n const toPx = (v: number) => lo + ((v - min) / (max - min)) * (hi - lo)\n return [\n Math.max(lo, Math.min(hi, toPx(a))),\n Math.max(lo, Math.min(hi, toPx(b))),\n true,\n ]\n }\n\n const [left, right, xBounded] = span(region.x, xScale, plot.left, plot.right)\n const [bottom, top, yBounded] = span(region.y, yScale, plot.bottom, plot.top)\n return { left, right, bottom, top, xBounded, yBounded }\n}\n\n/** A bare `{x, y}` — declared locally so this module stays free of node imports. */\ninterface Vector2Like {\n x: number\n y: number\n}\n\n/**\n * The parts of `points` that fall **inside** `box`, as a list of contiguous\n * sub-polylines — the geometry behind the region spotlight: a series is drawn\n * dimmed in full, then these runs are re-drawn over it at full strength.\n *\n * Segments are clipped against the box rather than filtered by endpoint, so a\n * line entering the region mid-segment is cut exactly on the boundary.\n */\nexport function clipPolylineToBox(\n points: Vector2Like[],\n box: RegionBox\n): Vector2Like[][] {\n if (points.length < 2 || !hasRegion(box)) return []\n\n const runs: Vector2Like[][] = []\n let run: Vector2Like[] = []\n\n for (let i = 1; i < points.length; i++) {\n const seg = clipSegment(points[i - 1], points[i], box)\n if (!seg) {\n // Segment misses the box entirely — close whatever run was open.\n if (run.length >= 2) runs.push(run)\n run = []\n continue\n }\n const [a, b] = seg\n // Continue the open run when this clipped segment starts where the last one\n // ended; otherwise the line left the box and came back, which is a new run.\n if (run.length > 0 && samePoint(run[run.length - 1], a)) run.push(b)\n else {\n if (run.length >= 2) runs.push(run)\n run = [a, b]\n }\n }\n if (run.length >= 2) runs.push(run)\n return runs\n}\n\n/** Points within half a pixel are the same point, for run-joining purposes. */\nfunction samePoint(a: Vector2Like, b: Vector2Like): boolean {\n return Math.abs(a.x - b.x) < 0.5 && Math.abs(a.y - b.y) < 0.5\n}\n\n/**\n * Liang–Barsky clip of one segment against the axis-aligned `box`. Returns the\n * surviving `[start, end]`, or `null` when the segment lies wholly outside.\n */\nfunction clipSegment(\n a: Vector2Like,\n b: Vector2Like,\n box: RegionBox\n): [Vector2Like, Vector2Like] | null {\n const dx = b.x - a.x\n const dy = b.y - a.y\n let t0 = 0\n let t1 = 1\n\n // Each edge as (p, q): the segment survives where p·t <= q.\n const edges: [number, number][] = [\n [-dx, a.x - box.left],\n [dx, box.right - a.x],\n [-dy, a.y - box.bottom],\n [dy, box.top - a.y],\n ]\n for (const [p, q] of edges) {\n if (p === 0) {\n // Parallel to this edge: outside it means the whole segment is out.\n if (q < 0) return null\n continue\n }\n const r = q / p\n if (p < 0) {\n if (r > t1) return null\n if (r > t0) t0 = r\n } else {\n if (r < t0) return null\n if (r < t1) t1 = r\n }\n }\n return [\n { x: a.x + t0 * dx, y: a.y + t0 * dy },\n { x: a.x + t1 * dx, y: a.y + t1 * dy },\n ]\n}\n\n// ---- Theme ---------------------------------------------------------------\n\n/**\n * How a spotlit {@link PlotRegion} is drawn — the dashed rules on its bounded\n * edges, the wash filling it, and how far the series fade outside it.\n */\nexport interface RegionConfig {\n /** The dashed rule marking each bounded edge. */\n stroke: Stroke\n /** Wash filling the region, drawn under the series lines. */\n fill: Fill\n /** Opacity the stretch of every series *outside* the region fades to. */\n dimOpacity: number\n}\n\n/** A {@link LineChart}'s full, resolved visual theme. */\nexport interface ChartConfig {\n /**\n * Default per-series line stroke, cycled by series index (`i % length`). A\n * series' own `fill`, when set, overrides just the colour.\n */\n seriesStyles: Stroke[]\n xAxis: AxisConfig\n yAxis: AxisConfig\n /** The plot panel: border, fill, and drop shadow. */\n plotArea: PlotAreaConfig\n legend: LegendConfig\n /** The spotlit-region annotation — see {@link RegionConfig}. */\n region: RegionConfig\n}\n\n/**\n * Author-facing partial theme, passed as {@link LineChart}'s `theme` prop —\n * every branch optional and deep-merged onto {@link DEFAULT_CHART_THEME}.\n */\nexport type ChartTheme = {\n seriesStyles?: Stroke[]\n xAxis?: Partial<AxisConfig>\n yAxis?: Partial<AxisConfig>\n plotArea?: Partial<PlotAreaConfig>\n legend?: LegendTheme\n region?: Partial<RegionConfig>\n}\n\n/**\n * The legend of a line chart: the shared one, with a small rounded-square\n * swatch in place of the wide chip. A bar's or a wedge's swatch stands in for\n * an area of paint and is drawn like one; a line's stands in for a colour, and\n * a square reads as the sample it is.\n */\nconst LINE_CHART_LEGEND: LegendConfig = {\n ...DEFAULT_LEGEND,\n markerStyle: {\n ...DEFAULT_LEGEND.markerStyle,\n width: 20,\n height: 20,\n borderRadius: 6,\n },\n}\n\n/** The chart's out-of-the-box look. */\nexport const DEFAULT_CHART_THEME: ChartConfig = {\n seriesStyles: DEFAULT_SERIES_STROKES,\n xAxis: DEFAULT_X_AXIS,\n yAxis: DEFAULT_Y_AXIS,\n plotArea: DEFAULT_PLOT_AREA,\n legend: LINE_CHART_LEGEND,\n region: {\n // Brighter than the grid and dashed, so the boundary reads as a call-out\n // rather than another axis line; `butt` caps keep the dash lengths exact.\n stroke: {\n weight: 3,\n fill: CHART_PALETTE.regionColor,\n dash: [14, 12],\n cap: \"butt\",\n },\n // A wash barely above the panel — enough to tint the band, not enough to\n // mute the series drawn over it.\n fill: Fills.color(CHART_PALETTE.regionColor, { opacity: 0.08 }),\n dimOpacity: SPOTLIGHT_DIM,\n },\n}\n\nfunction resolveRegionConfig(\n region: Partial<RegionConfig> | undefined,\n previous: RegionConfig\n): RegionConfig {\n return {\n stroke: strokeOps.resolve(region?.stroke ?? previous.stroke),\n fill: fillOps.resolve(region?.fill ?? previous.fill),\n dimOpacity: region?.dimOpacity ?? previous.dimOpacity,\n }\n}\n\nfunction lerpRegionConfig(\n from: RegionConfig,\n to: RegionConfig,\n t: number\n): RegionConfig {\n return {\n stroke: strokeOps.lerp(\n from.stroke as StrokeResolved[],\n to.stroke as StrokeResolved[],\n t\n ),\n fill: fillOps.lerp(\n from.fill as FillResolved[],\n to.fill as FillResolved[],\n t\n ),\n dimOpacity: lerpNumber(from.dimOpacity, to.dimOpacity, t),\n }\n}\n\n/**\n * {@link LineChart}'s `theme` entry's resolve: merges the author's partial\n * {@link ChartTheme} onto `previous` (falling back to\n * {@link DEFAULT_CHART_THEME} on first resolution), fully resolving every leaf.\n */\nexport function resolveChartTheme(\n theme: ChartTheme | undefined,\n previous?: ChartConfig\n): ChartConfig {\n const base = previous ?? DEFAULT_CHART_THEME\n return {\n seriesStyles: resolveSeriesStrokes(theme?.seriesStyles, base.seriesStyles),\n xAxis: resolveAxisConfig(theme?.xAxis, base.xAxis),\n yAxis: resolveAxisConfig(theme?.yAxis, base.yAxis),\n plotArea: resolvePlotArea(theme?.plotArea, base.plotArea),\n legend: resolveLegendConfig(theme?.legend, base.legend),\n region: resolveRegionConfig(theme?.region, base.region),\n }\n}\n\n/** {@link LineChart}'s `theme` entry's lerp — leaf by leaf. */\nexport function lerpChartTheme(\n from: ChartConfig,\n to: ChartConfig,\n t: number\n): ChartConfig {\n return {\n seriesStyles: lerpSeriesStrokes(from.seriesStyles, to.seriesStyles, t),\n xAxis: lerpAxisConfig(from.xAxis, to.xAxis, t),\n yAxis: lerpAxisConfig(from.yAxis, to.yAxis, t),\n plotArea: lerpPlotArea(from.plotArea, to.plotArea, t),\n legend: lerpLegendConfig(from.legend, to.legend, t),\n region: lerpRegionConfig(from.region, to.region, t),\n }\n}\n\n// ---- Scales --------------------------------------------------------------\n\n/**\n * Resolve one series' `[x, y]` points out of the chart's rows: each row's\n * position on the {@link XScale} paired with its own `field` column, sorted\n * along x so the polyline traces left-to-right whatever order the rows arrived\n * in.\n *\n * A row missing its y column contributes a 0 rather than a gap — these charts\n * draw continuous lines, so dropping the point would silently bridge the hole.\n *\n * Takes the resolved scale rather than the column *name*, because what a row's x\n * is depends on what kind of column it came out of: a number, an instant, or a\n * place in the file. See `x-scale.ts`.\n */\nexport function seriesPoints(\n data: DataRecord[],\n x: XScale,\n series: LineSeries\n): [x: number, y: number][] {\n return data\n .map((row, i): [number, number] => [x.at(i), num(row, series.field)])\n .sort((a, b) => a[0] - b[0])\n}\n\n/** Min/max across every series' column, the fallback when unset. */\nexport function yExtent(data: DataRecord[], series: LineSeries[]): Range {\n let lo = Infinity\n let hi = -Infinity\n for (const row of data)\n for (const s of series) {\n const v = num(row, s.field)\n if (v < lo) lo = v\n if (v > hi) hi = v\n }\n return lo <= hi ? [lo, hi] : [0, 1]\n}\n\n/**\n * Effective vertical scale: the explicit `range`, else the data's y extent\n * nudged out to tidy step bounds so markers read as round numbers.\n *\n * `divisions` is how many segments the auto-range is stepped into (one fewer\n * than a marker count) — it only shapes the tidy step when `range` is omitted.\n */\nexport function resolveYScale(\n data: DataRecord[],\n series: LineSeries[],\n range: Range | undefined,\n divisions: number\n): Range {\n if (range) return range\n const [lo, hi] = yExtent(data, series)\n const segments = Math.max(1, Math.round(divisions))\n if (hi === lo) return constantScale(lo, segments)\n const step = niceStep((hi - lo) / segments)\n return [Math.floor(lo / step) * step, Math.ceil(hi / step) * step]\n}\n\n/**\n * The scale for data that never moves. A zero-width scale puts every tick on the\n * same value and the line on the plot's floor, so it is widened to a few steps\n * either side of the value (the step sized to its magnitude, so `5` gets\n * `[3, 7]` and `5000` gets `[3000, 7000]`), or to `[0, 1]` for all zeros, as for\n * no data at all.\n */\nfunction constantScale(value: number, segments: number): Range {\n if (value === 0) return [0, 1]\n const step = niceStep(Math.abs(value) / segments)\n return [value - step, value + step]\n}\n", "import {\n Graphics2D,\n Types,\n component2d,\n fillOps,\n strokeOps,\n type Fill,\n type FillResolved,\n type NodeOf,\n // Named so this component's emitted type reaches `Node2D` through the sdk, not core.\n type Node2D as _Node2D,\n type Stroke,\n type StrokeResolved,\n} from \"@motionscript/sdk\"\n\nimport { fresh } from \"../kit/entries\"\nimport { loadPaints } from \"../kit/paints\"\nimport {\n DEFAULT_CHART_THEME,\n EMPTY_REGION_BOX,\n hasRegion,\n type RegionBox,\n} from \"./shared\"\n\n/**\n * The **region call-out** of a {@link LineChart}: a washed rectangle over the\n * plot with a dashed rule down each edge the author actually bounded. Sized and\n * positioned entirely from the pre-mapped `box` rather\n * than its own box — the region already carries the plot geometry.\n *\n * It draws only the *marking*; fading the series outside the region is\n * {@link ChartLine}'s job (it clips its own polyline against the same box), so\n * the two can't disagree about where the boundary is.\n *\n * Only bounded edges get a rule: a region given just an `x` range spans the full\n * plot height, so its top and bottom edges are the plot's own border, not a data\n * bound — drawing dashes there would assert a limit the author didn't set.\n */\nexport const ChartRegion = component2d({\n key: \"chartRegion\",\n state: {\n /** The highlighted slice, already resolved into this node's local pixel space (origin at centre, y-up) by the parent {@link LineChart}. A degenerate box draws nothing. */\n box: fresh<RegionBox>(() => EMPTY_REGION_BOX),\n /** Dashed rule marking each *bounded* edge of the box. */\n stroke: Types.custom<Stroke, StrokeResolved[]>(DEFAULT_CHART_THEME.region.stroke, { resolve: strokeOps.resolve, lerp: strokeOps.lerp }),\n /** Wash filling the box, under the series lines. */\n fill: Types.custom<Fill, FillResolved[]>(DEFAULT_CHART_THEME.region.fill, { resolve: fillOps.resolve, lerp: fillOps.lerp }),\n /** Reveal of the annotation in `[0, 1]`, driven by the chart's own `regionReveal` so the rules and the series' dimming move as one. */\n reveal: 0,\n },\n load: (loader, state) => loadPaints(loader, { fills: [state.fill], strokes: [state.stroke] }),\n render: (ctx, state) => {\n const box = state.box\n if (!hasRegion(box)) return\n\n const r = Math.max(0, Math.min(1, state.reveal))\n if (r <= 0) return\n\n const width = box.right - box.left\n const height = box.top - box.bottom\n const cx = (box.left + box.right) / 2\n const cy = (box.bottom + box.top) / 2\n\n // Wash first, so the rules sit over their own tint.\n if (state.fill.length > 0) {\n ctx.draw(new Graphics2D().rect({ x: cx, y: cy, width, height }).fill(state.fill).opacity(r))\n }\n\n // One rule per bounded edge, growing from the region's centre outward as\n // `reveal` climbs — the same \"trace it in\" beat as the series lines.\n if (state.stroke.length === 0) return\n\n // One `draw` per rule rather than four line ops under a single paint:\n // shapes chained before a `stroke()` are combined into one surface, and\n // stroking a union of disjoint open paths doesn't paint.\n const rule = (points: { x: number; y: number }[]) =>\n ctx.draw(new Graphics2D().line({ points }).stroke(state.stroke).opacity(r))\n\n if (box.xBounded) {\n const half = (height / 2) * r\n for (const x of [box.left, box.right]) rule([{ x, y: cy - half }, { x, y: cy + half }])\n }\n if (box.yBounded) {\n const half = (width / 2) * r\n for (const y of [box.bottom, box.top]) rule([{ x: cx - half, y }, { x: cx + half, y }])\n }\n },\n})\n\nexport type ChartRegion = NodeOf<typeof ChartRegion>\n", "import {\n Rect,\n Text,\n Types,\n component2d,\n insetsOps,\n textOps,\n type NodeOf,\n type Node2D,\n} from \"@motionscript/sdk\";\n\nimport { asParts, fresh } from \"../kit/entries\";\nimport { armed, arrival, arrivalOf, arrivalsEntry, beats, frameEase, type Arrival } from \"../kit/motion\";\nimport {\n DEFAULT_X_AXIS,\n enterBudget,\n type AxisConfig,\n type Range,\n} from \"../kit/shared\";\nimport { markerCaption } from \"../kit/vertical-axis\";\n\n/** How far a marker starts above its slot when it enters, in px. */\nconst SLIDE = 24;\n\n/**\n * The horizontal axis of a {@link LineChart}: a **row of {@link Text} nodes**,\n * one per marker value, running left-to-right. Pure layout — the mirror of the\n * kit's `VerticalAxis`, including its bound (not snapshotted) marker text and\n * positions, and its landed resting state.\n *\n * Markers are always placed **by value** across the range, whatever they say:\n * `2026-03` sits three months along, not three slots along. That is what keeps\n * it distinct from a bar chart's bottom axis, which centres a caption on each\n * slot and has no scale at all — the two only look alike. What the values are\n * captioned *as* is the chart's to decide, in `captions`.\n *\n * `enter` staggers the markers in **left-to-right**: each starts lifted a\n * little above its slot and transparent, then fades and slides down into place.\n */\nexport const HorizontalAxis = component2d({\n key: \"horizontalAxis\",\n state: {\n /** `[min, max]` of the horizontal scale the markers are positioned within. */\n range: fresh<Range>(() => [0, 1]),\n /** Tick values (in data units) to draw along the axis. */\n markers: fresh<number[]>(() => []),\n /**\n * How each marker is captioned, one per entry of `markers` and in its order.\n * Empty (the default), or short, captions a marker with its bare numeric\n * reading; a chart whose x column names months or categories writes those.\n */\n captions: fresh<string[]>(() => []),\n /** Marker/caption text style, grid stroke, and gutters — see {@link AxisConfig}. */\n axis: Types.custom<AxisConfig>(DEFAULT_X_AXIS),\n /** Caption drawn centred in the gutter beneath the marker row; empty (the default) hides it. */\n label: \"\",\n arrivals: arrivalsEntry(),\n },\n // A plain node filled its parent; one made of its markers would hug them.\n defaults: { width: \"fill\", height: \"fill\" },\n children: (state) => {\n const tickPad = insetsOps.resolve(state.axis.tickPadding).top;\n const titlePad = insetsOps.resolve(state.axis.titlePadding).top;\n\n const column: Node2D[] = [];\n // The column's parent is the axis: its box is what the markers are placed in.\n const box = () => (column[0]?.parent as Node2D | null | undefined)?.layoutBounds ?? { width: 0, height: 0 };\n /** The live tick values, left → right — re-read every frame, not snapshotted. */\n const sorted = () => [...state.markers].sort((a, b) => a - b);\n /** Tick `i`'s fraction across the live `range`, 0 at the left edge. */\n const fracAt = (value: number | undefined) => {\n if (value === undefined) return 0;\n const [min, max] = state.range;\n return max <= min ? 0 : (value - min) / (max - min);\n };\n\n const markers = state.markers.map((_, i) => {\n const marker: Node2D = new Text({\n ...textOps.styleProps(state.axis.labelStyle),\n text: () => {\n const value = sorted()[i];\n return value === undefined ? \"\" : markerCaption(state, value);\n },\n x: () => {\n const width = box().width;\n const centred = (fracAt(sorted()[i]) - 0.5) * width;\n // Held inside the axis's right edge, which is also the *chart's*: the\n // last marker is centred on the last grid line, so half of `2026-06`\n // — or of `3300` — would be drawn outside the node's box and outside\n // the rectangle the editor draws around it. Only the right edge is\n // held, because the left one has the vertical axis's whole gutter\n // beyond it, and a marker overhanging into that is inside the chart.\n const limit = width / 2 - marker.layoutBounds.width / 2;\n return Math.min(centred, limit);\n },\n opacity: () => arrivalOf(state.arrivals, i).opacity,\n y: () => arrivalOf(state.arrivals, i).shift,\n });\n return marker;\n });\n\n const children: Node2D[] = [\n new Rect({ width: \"fill\", height: \"hug\", padding: { top: tickPad }, children: markers }),\n ];\n if (state.label !== \"\") {\n children.push(\n new Text({\n ...textOps.styleProps(state.axis.titleStyle),\n text: state.label,\n opacity: () => arrivalOf(state.arrivals, \"label\").opacity,\n y: () => arrivalOf(state.arrivals, \"label\").shift,\n }),\n );\n }\n\n column.push(new Rect({ flow: \"vertical\", gap: titlePad, height: \"hug\", width: \"fill\", children }));\n return asParts(column);\n },\n commands: {\n /**\n * Stagger the markers in left to right, then slide the caption in after\n * them. Its first frame arms them all: lifted by `slide` and transparent.\n *\n * The timeline's duration is the **whole** entrance — see\n * `VerticalAxis.enter` and {@link enterBudget}.\n */\n enter: {\n args: {\n stagger: Types.custom<number | undefined>(undefined),\n slide: Types.number(SLIDE),\n },\n run: (timeline, state) => {\n const { stagger, slide } = timeline.args;\n const count = state.markers.length;\n const captioned = state.label !== \"\";\n const from: Arrival = { opacity: 0, shift: slide };\n const keys = [...state.markers.keys(), ...(captioned ? [\"label\"] : [])];\n const arm = timeline.animate(() => ({ arrivals: armed(keys, from) }));\n if (count === 0) return arm;\n const { step, per, caption } = enterBudget(timeline.duration, count, captioned, stagger);\n const ease = frameEase(timeline);\n // Left marker leads: the row is already left → right.\n const markers = state.markers.map((_, i) => ({ at: i * step, duration: per, piece: arrival(timeline, i, from, ease) }));\n const label = captioned ? [{ at: (count - 1) * step + per, duration: caption, piece: arrival(timeline, \"label\", from, ease) }] : [];\n return timeline.together([arm, beats(timeline, [...markers, ...label])]);\n },\n },\n },\n});\n\nexport type HorizontalAxis = NodeOf<typeof HorizontalAxis>;\n", "/**\n * What a line chart's x column *means* — the one question that has to be\n * answered before a row can be given a position along the bottom of the plot.\n *\n * A y column is always a magnitude. An x column is one of three things, and\n * which one is a property of the data rather than something the author states:\n *\n * - **numeric** — `0, 5, 10` — plot the number.\n * - **time** — `Jan`, `2026-03`, `2026-W12`, `2026-03-14` — plot the instant,\n * so samples a month apart sit a month apart and a gap in the data reads as a\n * gap.\n * - **ordinal** — anything else — plot the row's position, evenly spaced.\n *\n * Before this, every x column went through `num()`, which reads a non-numeric\n * cell as `0`. A `Month` column of `Jan…Dec` therefore stacked all twelve\n * points on one x and drew a vertical line — no error, no empty chart, just a\n * wrong picture. That is the failure this module exists to remove: every column\n * now plots as *something*, and the something is the reading a person would\n * give it.\n *\n * The captions come back off the rows rather than out of a formatter. A tick on\n * a time or ordinal scale is chosen *from the data* (see {@link XScale.ticks}),\n * so the label it wants is the cell the author wrote — `Jan`, not `1970-01-01`,\n * and `2026-W12`, not the Monday it resolves to. Only a tick that is not a\n * sample — an author-set range, or a `zoomTo` mid-flight — needs formatting,\n * and that is the one case {@link formatInstant} covers.\n */\n\nimport type { DataRecord } from \"@motionscript/sdk\"\n\nimport {\n formatMarker,\n markerValues,\n num,\n text,\n type Range,\n} from \"../kit/shared\"\nimport { formatNumber } from \"@motionscript/sdk/component\"\n\n/** How an x column's cells become positions along the axis. */\nexport type XScaleKind = \"numeric\" | \"time\" | \"ordinal\"\n\n/**\n * An x column resolved into positions, captions and ticks.\n *\n * The three kinds differ in all three answers, so they are three closures built\n * once per (data, column) rather than a kind tag every caller switches on.\n */\nexport interface XScale {\n kind: XScaleKind\n /** Position of row `index` along the axis, in this scale's own units. */\n at(index: number): number\n /** `[min, max]` across the rows — the scale when no range is set. */\n extent: Range\n /** The caption for a tick at `value`. */\n label(value: number): string\n /** `divisions + 1` tick values spanning `range`, ends included. */\n ticks(range: Range, divisions: number): number[]\n}\n\n// ---- Time parsing --------------------------------------------------------\n\nconst DAY = 86_400_000\n\nconst MONTHS = [\n \"january\",\n \"february\",\n \"march\",\n \"april\",\n \"may\",\n \"june\",\n \"july\",\n \"august\",\n \"september\",\n \"october\",\n \"november\",\n \"december\",\n]\n\n/**\n * The year a cell that names no year is dated into.\n *\n * Any year would place `Jan…Dec` in the right order at the right spacing; a\n * leap year is the one that also spaces February correctly against a column\n * that *does* carry years, so the two read alike.\n */\nconst NOMINAL_YEAR = 2024\n\n/**\n * Month index for any unambiguous abbreviation of a month name — `jan`, `sept`,\n * `january` — or -1.\n *\n * Three letters minimum, and then a prefix match, which is what lets `sept`\n * through without letting `janitor` through: a longer word has to still *be*\n * the month.\n */\nfunction monthIndex(word: string): number {\n if (word.length < 3) return -1\n return MONTHS.findIndex((month) => month.startsWith(word))\n}\n\n/** The Monday of ISO week `week` in `year`, as an epoch timestamp. */\nfunction isoWeekStart(year: number, week: number): number {\n // ISO week 1 is the week containing 4 January, and weeks start on Monday.\n const jan4 = Date.UTC(year, 0, 4)\n const weekday = (new Date(jan4).getUTCDay() + 6) % 7 // Monday = 0\n return jan4 - weekday * DAY + (week - 1) * 7 * DAY\n}\n\n/**\n * One cell read as an instant, or `null` when it isn't one.\n *\n * Deliberately **not** `Date.parse`. That accepts `\"5\"`, `\"Q3\"` and a good deal\n * else besides, differs between engines on anything non-ISO, and reads a bare\n * `2026-03-14` as UTC but `2026/03/14` as local — three ways for a column to be\n * silently misread as time. Every form below is matched explicitly, and an\n * ambiguous all-numeric date (`03/04/2026` — March or April, depending on where\n * the author lives) is deliberately not one of them.\n */\nexport function parseInstant(raw: unknown): number | null {\n if (typeof raw !== \"string\") return null\n const s = raw.trim().toLowerCase()\n if (s === \"\") return null\n\n // 2026-03-14 · 2026/03/14 · 2026-03 · 2026/03\n const ymd = /^(\\d{4})[-/](\\d{1,2})(?:[-/](\\d{1,2}))?$/.exec(s)\n if (ymd) {\n const month = Number(ymd[2])\n const day = ymd[3] === undefined ? 1 : Number(ymd[3])\n if (month < 1 || month > 12 || day < 1 || day > 31) return null\n return Date.UTC(Number(ymd[1]), month - 1, day)\n }\n\n // 2026-W12 · 2026W12 · w12 · week 12\n const week = /^(?:(\\d{4})[-\\s]?)?w(?:ee)?k?[-\\s]?(\\d{1,2})$/.exec(s)\n if (week) {\n const n = Number(week[2])\n if (n < 1 || n > 53) return null\n return isoWeekStart(\n week[1] === undefined ? NOMINAL_YEAR : Number(week[1]),\n n\n )\n }\n\n // jan · january · jan 2026 · january-2026 · 2026 jan\n const named = /^(?:(\\d{4})[-\\s]+)?([a-z]+)(?:[-\\s]+(\\d{4}))?$/.exec(s)\n if (named) {\n const month = monthIndex(named[2])\n if (month >= 0) {\n const year = named[1] ?? named[3]\n const y = year === undefined ? NOMINAL_YEAR : Number(year)\n return Date.UTC(y, month, 1)\n }\n }\n\n // 14 mar 2026 · mar 14 2026 · mar 14, 2026\n const dayNamed =\n /^(?:(\\d{1,2})[-\\s]+([a-z]+)|([a-z]+)[-\\s]+(\\d{1,2}),?)[-\\s]+(\\d{4})$/.exec(\n s\n )\n if (dayNamed) {\n const month = monthIndex(dayNamed[2] ?? dayNamed[3])\n const day = Number(dayNamed[1] ?? dayNamed[4])\n if (month >= 0 && day >= 1 && day <= 31) {\n return Date.UTC(Number(dayNamed[5]), month, day)\n }\n }\n\n return null\n}\n\n/**\n * An instant captioned at the coarsest granularity the axis's own span can tell\n * apart — a decade of samples reads as years, a year of them as months.\n *\n * Only reached for a tick that is *not* one of the rows (an author-set range, a\n * `zoomTo` part-way through). A tick that is a sample is captioned with the\n * cell the author wrote; see the note at the top of this file.\n */\nexport function formatInstant(value: number, span: number): string {\n const date = new Date(value)\n const year = date.getUTCFullYear()\n const month = MONTHS[date.getUTCMonth()].slice(0, 3)\n const label = month.charAt(0).toUpperCase() + month.slice(1)\n if (span > 4 * 365 * DAY) return String(year)\n if (span > 60 * DAY) return `${label} ${year}`\n return `${label} ${date.getUTCDate()}`\n}\n\n// ---- Column reading ------------------------------------------------------\n\n/**\n * Whether every non-empty cell of `field` holds a number.\n *\n * The same rule `numericFields` applies, asked of one column: a single text\n * cell in an otherwise numeric column takes the whole column off the numeric\n * scale, because plotting that row at 0 is the failure this module is here to\n * stop.\n */\nfunction isNumericColumn(data: DataRecord[], field: string): boolean {\n let sawValue = false\n for (const row of data) {\n const v = row[field]\n if (v === undefined || v === null || v === \"\") continue\n if (typeof v !== \"number\") return false\n sawValue = true\n }\n return sawValue\n}\n\n/**\n * Every non-empty cell of `field` parsed as an instant, or `null` if any one of\n * them isn't one — in which case the column isn't a time column at all.\n *\n * A blank cell keeps its slot, so the returned list stays index-aligned with\n * the rows, and takes the nearest known instant: a hole in an x column is a row\n * whose date the file didn't say, and stacking it on the epoch would drag the\n * whole scale back fifty years to make room for it.\n */\nfunction instantColumn(data: DataRecord[], field: string): number[] | null {\n const parsed: (number | null)[] = []\n let sawValue = false\n for (const row of data) {\n const raw = row[field]\n if (raw === undefined || raw === null || raw === \"\") {\n parsed.push(null)\n continue\n }\n const t = parseInstant(raw)\n if (t === null) return null\n parsed.push(t)\n sawValue = true\n }\n if (!sawValue) return null\n\n const first = parsed.find((t): t is number => t !== null) as number\n let last = first\n return parsed.map((t) => (t === null ? last : (last = t)))\n}\n\n/** `[min, max]` of `values`, or `[0, 1]` when there is nothing to span. */\nfunction extentOf(values: number[]): Range {\n let lo = Infinity\n let hi = -Infinity\n for (const v of values) {\n if (v < lo) lo = v\n if (v > hi) hi = v\n }\n return lo <= hi ? [lo, hi] : [0, 1]\n}\n\n/**\n * Ticks spanning `range`, each one **a sample** — a row the author can point at\n * in their file — placed as near as it can be to where an evenly spaced tick\n * would have gone.\n *\n * Two things have to hold at once, and picking either alone gets it wrong.\n * Ticks must be *samples*, because a caption between two of them has nothing to\n * say (two-thirds of the way through March is not a month, and on a category\n * column it is not anything at all). And they must be *spread*, because the\n * labels sit side by side in a row: taking every nth sample instead puts five\n * dates in the two weeks a burst of rows happens to cover and prints them on\n * top of each other, which is what a column with a gap in it does.\n *\n * Walking the targets rather than the samples is what satisfies both. A run of\n * rows too close together to caption collapses to one tick — consecutive\n * targets pick the same sample and the duplicate is dropped — so a chart asked\n * for five ticks across a gappy column draws four honest ones instead of five\n * overlapping.\n */\nfunction sampledTicks(\n positions: number[],\n [min, max]: Range,\n divisions: number\n): number[] {\n const inside = positions.filter((v) => v >= min && v <= max)\n const unique = [...new Set(inside)].sort((a, b) => a - b)\n if (unique.length === 0) return markerValues([min, max], divisions)\n\n const nearest = (target: number) =>\n unique.reduce((best, v) =>\n Math.abs(v - target) < Math.abs(best - target) ? v : best\n )\n\n const out: number[] = []\n for (const target of markerValues([min, max], divisions)) {\n const tick = nearest(target)\n if (out[out.length - 1] !== tick) out.push(tick)\n }\n return out\n}\n\n/**\n * The caption a tick carries on a sample-based scale: the cell of the row it\n * came from, or `fallback` when the tick isn't a sample after all.\n */\nfunction labelBySample(\n positions: number[],\n captions: string[],\n value: number,\n fallback: (value: number) => string\n): string {\n const i = positions.indexOf(value)\n return i >= 0 ? captions[i] : fallback(value)\n}\n\n/** Scales already resolved, by data array, then by field and pattern. */\nconst scales = new WeakMap<DataRecord[], Map<string, XScale>>()\n\n/**\n * How `field` positions the rows: the number it holds, the instant it names, or\n * failing both, the row's own place in the file.\n *\n * Kept per `data` array: a chart's bound props ask a dozen times a frame, and\n * parsing every row's instant each time was most of a frame.\n */\nexport function resolveXScale(\n data: DataRecord[],\n field: string,\n pattern?: string\n): XScale {\n let byKey = scales.get(data)\n if (!byKey) scales.set(data, (byKey = new Map()))\n const key = JSON.stringify([field, pattern ?? null])\n let scale = byKey.get(key)\n if (!scale) byKey.set(key, (scale = xScaleOf(data, field, pattern)))\n return scale\n}\n\nfunction xScaleOf(\n data: DataRecord[],\n field: string,\n /**\n * The number-format pattern for a *numeric* column. Ignored by the other two\n * kinds, and correctly so: a tick on those is captioned with the cell the\n * author wrote, and `Jan` has no decimal places.\n */\n pattern?: string\n): XScale {\n if (field !== \"\" && isNumericColumn(data, field)) {\n const positions = data.map((row) => num(row, field))\n return {\n kind: \"numeric\",\n at: (i) => positions[i] ?? 0,\n extent: extentOf(positions),\n label: (value) =>\n pattern === undefined ? formatMarker(value) : formatNumber(value, pattern),\n // Round numbers, evenly spaced — a continuous axis is read by its scale\n // rather than by which samples happen to be on it.\n ticks: markerValues,\n }\n }\n\n const captions = data.map((row) => text(row, field))\n const instants = field === \"\" ? null : instantColumn(data, field)\n\n if (instants) {\n const extent = extentOf(instants)\n const span = extent[1] - extent[0]\n return {\n kind: \"time\",\n at: (i) => instants[i] ?? 0,\n extent,\n label: (value) =>\n labelBySample(instants, captions, value, (v) =>\n formatInstant(v, span)\n ),\n ticks: (range, divisions) => sampledTicks(instants, range, divisions),\n }\n }\n\n const positions = data.map((_, i) => i)\n return {\n kind: \"ordinal\",\n at: (i) => i,\n // `[0, n-1]` rather than the tidy `[0, n]`: the first and last rows sit on\n // the plot's edges, which is where a reader looks for them.\n extent: positions.length > 0 ? [0, positions.length - 1] : [0, 1],\n label: (value) => captions[Math.round(value)] ?? \"\",\n ticks: (range, divisions) => sampledTicks(positions, range, divisions),\n }\n}\n", "import {\n Rect,\n Types,\n component2d,\n easeOut,\n type ChildView,\n type DataRecord,\n type EntryOptions,\n type NodeOf,\n // Named so this component's emitted type reaches `Node2D` through the sdk, not core.\n type Node2D,\n type Node2DProps,\n type Size2D,\n type Timeline,\n} from \"@motionscript/sdk\"\nimport { formatNumber } from \"@motionscript/sdk/component\"\n\nimport { cartesianFrame, cartesianViews, laidOutSize } from \"../kit/cartesian\"\nimport { ChartBorder } from \"../kit/chart-border\"\nimport { ChartCanvas } from \"../kit/chart-canvas\"\nimport { ChartLegend } from \"../kit/chart-legend\"\nimport { asParts, fresh } from \"../kit/entries\"\nimport { beats, type Beat } from \"../kit/motion\"\nimport { VerticalAxis } from \"../kit/vertical-axis\"\nimport {\n clampUnit,\n formatMarker,\n lerpAxisDomain,\n lerpAxisEntry,\n markerStep,\n markerValues,\n num,\n resolveAxisDomain,\n settleAxisDomain,\n type AxisDomainConfig,\n type Range,\n} from \"../kit/shared\"\nimport { CategoryAxis } from \"./category-axis\"\nimport { ChartBars, type BarGeometry } from \"./chart-bars\"\nimport {\n DEFAULT_BAR_CHART_THEME,\n barCategories,\n barSpans,\n lerpBarChartTheme,\n resolveBarChartTheme,\n resolveBarFill,\n resolveBarScale,\n stackCapIndex,\n type BarChartConfig,\n type BarChartTheme,\n type BarSeries,\n} from \"./shared\"\n\nexport interface BarChartProps extends Node2DProps {\n /** One row per category — a plain record of field name to value. */\n data: DataRecord[]\n /**\n * Key into each row for its category caption, drawn beneath the baseline.\n * Named `categoryField` rather than `category` for the same reason the line\n * chart's is `xField`: it names a *column*, not a value.\n */\n categoryField: string\n /** One entry per bar series: which column it plots, its `label`, its `fill`. */\n series: BarSeries[]\n /**\n * When `true` every category's series stack into one column; when `false`\n * (the default) each series gets its own bar, grouped side by side within the\n * category's slot.\n */\n stacked?: boolean\n /** The value axis's domain: caption, scale range, and tick placement. */\n valueAxis?: AxisDomainConfig\n /** The category axis's domain. Only its `label` is read — the slots are the rows. */\n categoryAxis?: AxisDomainConfig\n /**\n * The chart's visual theme — bar palette and geometry, axis furniture, plot\n * panel paint, legend, value figures. Every branch is optional and deep-merged\n * onto {@link DEFAULT_BAR_CHART_THEME}.\n */\n theme?: BarChartTheme\n /** Whether the legend row below the plot is drawn at all. */\n showLegend?: boolean\n /** Whether each column prints the figure it plots. */\n showValues?: boolean\n}\n\n/** What a bar chart plots, as its entries hold it. */\nexport interface BarChartValues {\n readonly data: DataRecord[]\n readonly series: BarSeries[]\n readonly stacked: boolean\n readonly valueAxis: AxisDomainConfig\n}\n\n/** The value axis's domain with its range and ticks resolved. */\nexport type BarChartDomain = AxisDomainConfig & { readonly range: Range; readonly ticks: number[] }\n\n/** The argument every per-series command takes: an index into `series`, offered by name. */\nconst SERIES_ARG = Types.custom<number, number, EntryOptions & { readonly min: number; readonly step: number }>(0, { kind: \"series\", min: 0, step: 1 })\n\n/** How far the legend starts below its slot when it enters, in px. */\nconst LEGEND_SLIDE = 20\n\n/** Fallback marker count when the value axis states no ticks. */\nconst DEFAULT_MARKERS = 5\n\n/**\n * How `enter` divides its duration between its three beats: the axes arriving,\n * the columns rising, and the legend coming up. Shares of the whole, so they\n * must sum to 1.\n */\nconst AXES_SHARE = 0.35\nconst BARS_SHARE = 0.5\nconst LEGEND_SHARE = 0.15\n\n/** What the geometry reads: what the chart plots, its theme and the signals its commands move. */\ninterface BarState extends BarChartValues {\n readonly theme: BarChartConfig\n readonly weights: number[]\n readonly spotlight: number | null\n readonly spotlightReveal: number\n}\n\n/** The value axis with its range and ticks resolved: the explicit ones, else five across the scale. */\nfunction drawnValue(s: BarChartValues, domain: AxisDomainConfig): AxisDomainConfig {\n const divisions = (domain.ticks?.length ?? DEFAULT_MARKERS) - 1\n const range = resolveBarScale(s.data, s.series, s.stacked, domain.range, divisions)\n return { ...domain, range, ticks: domain.ticks ?? markerValues(range, DEFAULT_MARKERS - 1) }\n}\n\n/** The last domain worked out from each data array, and what else it was read from. */\nconst domainsDrawn = new WeakMap<DataRecord[], Omit<BarChartValues, \"data\"> & { readonly domain: BarChartDomain }>()\n\n/** The value axis as a bar chart draws it now, an auto range or tick row read back as itself. */\nexport function barChartDomain(s: BarChartValues): BarChartDomain {\n const { data, series, stacked, valueAxis } = s\n const kept = domainsDrawn.get(data)\n if (kept && kept.series === series && kept.stacked === stacked && kept.valueAxis === valueAxis) return kept.domain\n const domain = settleAxisDomain(valueAxis, (d) => drawnValue(s, d)) as BarChartDomain\n domainsDrawn.set(data, { series, stacked, valueAxis, domain })\n return domain\n}\n\n/**\n * A value written down as this chart's value axis says: the ticks and the\n * figures printed on the columns both read through here, so the two cannot\n * disagree about what a number looks like.\n */\nfunction valueLabel(s: BarChartValues, domain: BarChartDomain, value: number): string {\n const pattern = s.valueAxis.format\n return pattern === undefined ? formatMarker(value, markerStep(domain.ticks)) : formatNumber(value, pattern)\n}\n\n/** A value on the plot's **centred** pixel space (origin at its centre, y-up). */\nfunction valueToY(domain: BarChartDomain, plot: Size2D, value: number): number {\n const [min, max] = domain.range\n if (max <= min) return -plot.height / 2\n return ((value - min) / (max - min) - 0.5) * plot.height\n}\n\n/**\n * Pixel y of the line every column grows from: zero clamped onto the scale, so\n * a cropped axis grows its columns from the plot floor.\n */\nfunction baselinePx(domain: BarChartDomain, plot: Size2D): number {\n const [min, max] = domain.range\n return valueToY(domain, plot, Math.min(Math.max(0, min), max))\n}\n\n/** Series `i`'s live share of the slot; 0 once it has been hidden. */\nfunction weightAt(s: BarState, index: number): number {\n return Math.max(0, s.weights[index] ?? 1)\n}\n\n/** `row` with each series' value scaled by its live weight: what the stacked spans are measured from. */\nfunction weightedRow(s: BarState, row: DataRecord): DataRecord {\n const scaled: DataRecord = { ...row }\n s.series.forEach((series, i) => {\n const value = row[series.field]\n if (typeof value === \"number\") scaled[series.field] = value * weightAt(s, i)\n })\n return scaled\n}\n\n/**\n * Series `index`'s columns, mapped into the plot's centred pixel space. A\n * grouped series divides the slot with its siblings by their live weights, a\n * stacked one takes the whole slot and runs from the running total below it,\n * so hiding a series reflows rather than punching a hole.\n */\nfunction seriesBars(s: BarState, index: number, plot: Size2D, captioned: boolean): BarGeometry[] {\n const series = s.series[index]\n if (!series) return []\n const domain = barChartDomain(s)\n const n = s.data.length\n const slotWidth = n > 0 ? plot.width / n : 0\n const slotCenter = (i: number) => -plot.width / 2 + slotWidth * (i + 0.5)\n const inner = slotWidth * clampUnit(s.theme.bar.slotFill)\n const gap = s.theme.bar.groupGap\n let total = 0\n for (let i = 0; i < s.series.length; i++) total += weightAt(s, i)\n const unit = inner / (total > 0 ? total : 1)\n const weight = weightAt(s, index)\n let before = 0\n for (let i = 0; i < index; i++) before += weightAt(s, i)\n\n return s.data.map((row, rowIndex) => {\n // The stack is built from the *live* contributions, so a series fading out\n // lowers the segments above it as it goes.\n const spans = barSpans(s.stacked ? weightedRow(s, row) : row, s.series, s.stacked)\n const span = spans[index] ?? { from: 0, to: 0, value: 0 }\n const cap = s.stacked ? stackCapIndex(row, s.series) : index\n const slotLeft = slotCenter(rowIndex) - inner / 2\n const x = s.stacked ? slotCenter(rowIndex) : slotLeft + unit * before + (unit * weight) / 2\n const width = s.stacked ? inner : Math.max(0, unit * weight - (s.series.length > 1 ? gap : 0))\n // The figure printed is the row's own value, not the weighted one.\n const value = num(row, series.field)\n return {\n x,\n width,\n from: valueToY(domain, plot, span.from),\n to: valueToY(domain, plot, span.to),\n round: cap === index,\n value,\n ...(captioned && { caption: valueLabel(s, domain, value) }),\n }\n })\n}\n\n/**\n * How far each category is dimmed by the spotlight, one entry per row: 1 while\n * nothing is spotlit, falling to the theme's `dimOpacity` for every category but\n * the chosen one as the reveal climbs.\n */\nfunction categoryDims(s: BarState): number[] {\n const focus = clampUnit(s.spotlightReveal)\n const dim = s.theme.spotlight.dimOpacity\n const chosen = s.spotlight\n return s.data.map((_, i) => (focus <= 0 || chosen === i ? 1 : 1 - (1 - dim) * focus))\n}\n\ntype AnyTimeline = Timeline<any, any>\n\n/** The parts a bar chart's commands drive, found through its children. */\nfunction partsOf(children: readonly ChildView[]) {\n const frame = cartesianViews(children)\n if (!frame) return null\n const cAxis = frame.below.find(CategoryAxis.is)\n if (!VerticalAxis.is(frame.axis) || !cAxis) return null\n return {\n vAxis: frame.axis,\n cAxis,\n bars: frame.plot.filter(ChartBars.is),\n legend: frame.below.find(ChartLegend.is) ?? null,\n }\n}\n\n/**\n * Move series `index`'s slot share to `target` with its legend entry, the group\n * reflowing. Its columns are drawn for every frame the move covers, and switch\n * off only once a hide has landed, so arriving at a time backwards shows what\n * arriving forwards does.\n */\nfunction toggling(timeline: AnyTimeline, state: BarState & { readonly showing: boolean[] }, children: readonly ChildView[], index: number, target: number) {\n const parts = partsOf(children)\n if (!parts || !parts.bars[index]) return timeline.wait(timeline.duration)\n // Read through `weightAt`, so a chart whose stored weights are short still\n // tweens from the 1 it is drawing at.\n const from = state.series.map((_, i) => weightAt(state, i))\n const start = from[index] ?? 1\n const showing = state.series.map((s, i) => state.showing[i] ?? (s.enabled ?? true))\n return timeline.together([\n timeline.animate((t) => {\n const weights = [...from]\n weights[index] = start + (target - start) * timeline.ease(t)\n const shown = [...showing]\n shown[index] = target > 0 || t < 1\n return { weights, showing: shown }\n }, { eased: false }),\n ...(parts.legend ? [parts.legend.run(target > 0 ? \"showSeries\" : \"hideSeries\", { index })] : []),\n ])\n}\n\n/**\n * A grouped or stacked bar chart, the sibling of the line chart and built the\n * same way, out of several of the same pieces:\n *\n * - `ChartCanvas` — the plot panel with a horizontal grid, under everything.\n * - `VerticalAxis` — the numeric value markers, staggering in bottom-to-top.\n * - {@link CategoryAxis} — the row of names beneath the baseline, one per row.\n * - {@link ChartBars} (one per series) — that series' column in every category,\n * plus the figures printed on them.\n * - `ChartLegend` — a row of entries below the chart, one per series.\n *\n * Every part reads the chart's entries through bindings, and the columns map\n * across the plot panel's own laid-out box, so a column's end lands exactly on\n * its marker. **Zero is always on the scale**, and the columns grow about it, so\n * a negative value draws downward out of the same `growth` signal.\n *\n * **Resting state is the finished chart**; `enter` arms every part on its first\n * frame. `showSeries`/`hideSeries`/`spotlightSeries` pick out a *series* (hiding\n * one reflows the group), `spotlightCategory`/`clearCategory` a *column of the\n * table*. A `to` of the value axis's `range` or `ticks` tweens from the domain\n * as drawn, so an auto axis rescales the columns continuously.\n */\nexport const BarChart = component2d({\n key: \"barChart\",\n state: {\n data: fresh<DataRecord[]>(() => []),\n categoryField: \"\",\n series: fresh<BarSeries[]>(() => []),\n stacked: false,\n valueAxis: Types.custom<AxisDomainConfig>({}, { resolve: resolveAxisDomain, lerp: lerpAxisEntry }),\n categoryAxis: Types.custom<AxisDomainConfig>({}, { resolve: resolveAxisDomain, lerp: lerpAxisDomain }),\n theme: Types.custom<BarChartTheme, BarChartConfig>(DEFAULT_BAR_CHART_THEME, { resolve: resolveBarChartTheme, lerp: lerpBarChartTheme }),\n /**\n * Global rise of every column in `[0, 1]`, measured about the baseline: 0\n * collapses them all onto it, 1 draws them at full length.\n */\n growth: 1,\n /**\n * How much of the slot each series claims, one entry per series: a weight\n * rather than a switch, so hiding one is the others widening into the space.\n * Seeded from each series' `enabled`.\n */\n weights: fresh<number[]>(() => []),\n /** The category spotlit, or `null` for none. The choice snaps; `spotlightReveal` is the animation. */\n spotlight: Types.custom<number | null>(null),\n /** How far the category spotlight is drawn in, `[0, 1]`. */\n spotlightReveal: 0,\n /** Read as the chart is built: the legend is a part the chart builds or doesn't. */\n showLegend: Types.boolean(true, { animatable: false }),\n /** Read as the chart is built: whether each column prints the figure it plots. */\n showValues: Types.boolean(false, { animatable: false }),\n /** Whether each series' columns are drawn: on for every frame a show or hide covers, off once a hide lands. */\n showing: Types.custom<boolean[]>([], { private: true }),\n },\n defaults: {\n // Its parts fill it; a node made of them would hug them.\n width: \"fill\",\n height: \"fill\",\n weights: (state) => state.series.map((s) => ((s.enabled ?? true) ? 1 : 0)),\n showing: (state) => state.series.map((s) => s.enabled ?? true),\n },\n allowedChildren: [],\n children: (state) => {\n const theme = () => state.theme\n const domain = () => barChartDomain(state)\n let plot: Node2D | undefined\n // Before the panel is built or laid out it has no size, and the columns map onto nothing.\n const plotSize = () => laidOutSize(plot)\n const captioned = state.showValues === true\n\n const vAxis = new VerticalAxis({\n range: () => domain().range,\n markers: () => domain().ticks,\n captions: () => {\n const d = domain()\n return d.ticks.map((value) => valueLabel(state, d, value))\n },\n axis: () => theme().yAxis,\n label: state.valueAxis.label ?? \"\",\n width: \"hug\",\n height: \"fill\",\n })\n\n const cAxis = new CategoryAxis({\n categories: barCategories(state.data, state.categoryField),\n axis: () => theme().xAxis,\n label: state.categoryAxis.label ?? \"\",\n dims: () => categoryDims(state),\n width: \"fill\",\n height: \"hug\",\n }) as unknown as Node2D\n\n const barSets = state.series.map(\n (s, i) =>\n new ChartBars({\n bars: () => seriesBars(state, i, plotSize(), captioned),\n baseline: () => baselinePx(domain(), plotSize()),\n fill: () => resolveBarFill(theme().seriesFills, s, i),\n barStroke: () => theme().bar.stroke,\n barShadow: () => theme().bar.shadow,\n cornerRadius: () => theme().bar.cornerRadius,\n growth: () => state.growth,\n dims: () => categoryDims(state),\n showValues: captioned,\n valueStyle: state.theme.valueStyle,\n enabled: () => state.showing[i] ?? (s.enabled ?? true),\n }) as unknown as Node2D\n )\n\n const below: Node2D[] = [cAxis]\n if (state.showLegend !== false) {\n below.push(\n new ChartLegend({\n series: state.series,\n swatches: () => state.series.map((s, i) => resolveBarFill(theme().seriesFills, s, i)),\n legend: () => theme().legend,\n // The row's spacing is the theme's as the chart was built, like the\n // entries' type and chips: a theme tween re-paints the swatches only.\n gap: state.theme.legend.itemGap,\n padding: state.theme.legend.padding,\n }) as unknown as Node2D\n )\n }\n\n plot = new Rect({\n clip: true,\n children: [\n new ChartCanvas({\n fill: () => theme().plotArea.fill,\n shadow: () => theme().plotArea.shadow,\n xGridStroke: () => theme().xAxis.lineStyle,\n yGridStroke: () => theme().yAxis.lineStyle,\n // No vertical rules: the horizontal axis is categorical, so there is no\n // numeric position along it for a grid line to mark.\n xMarkers: [],\n yMarkers: () => domain().ticks,\n xExtent: () => [0, 1] as Range,\n yExtent: () => domain().range,\n }),\n ...barSets,\n new ChartBorder({ stroke: () => theme().plotArea.stroke }),\n ],\n }) as unknown as Node2D\n\n const gutter = new VerticalAxis({\n opacity: 0,\n range: () => domain().range,\n markers: () => domain().ticks,\n axis: () => theme().yAxis,\n label: state.valueAxis.label ?? \"\",\n width: \"hug\",\n height: 0,\n }) as unknown as Node2D\n\n return asParts([cartesianFrame({ axis: vAxis as unknown as Node2D, plot, gutter, below })])\n },\n commands: {\n /**\n * Animate the whole chart in: the two axes stagger their markers and\n * captions in, the columns rise from the baseline, and the legend fades up\n * last. Its first frame arms every part: markers and captions shifted and\n * transparent, columns collapsed, the legend dropped and transparent.\n * `duration` is the **whole** entrance; the beats are shares of it.\n */\n enter: {\n args: {},\n // Held from the chart's arrival, so it never shows finished ahead of its draw.\n entrance: true,\n easing: easeOut(),\n run: (timeline, _state, children) => {\n const parts = partsOf(children)\n if (!parts) return timeline.wait(timeline.duration)\n const duration = timeline.duration\n const axes = duration * AXES_SHARE\n const rise = duration * BARS_SHARE\n const { legend } = parts\n const at0 = (piece: Beat[\"piece\"]): Beat => ({ at: 0, duration: 0, piece })\n // Without a legend the beat is still its share of the whole.\n return beats(timeline, [\n at0(timeline.animate(() => ({ growth: 0 }))),\n ...(legend ? [at0(legend.to({ opacity: 0, y: -LEGEND_SLIDE }))] : []),\n { at: 0, duration: axes, piece: parts.vAxis.run(\"enter\", { slide: 24 }) },\n { at: 0, duration: axes, piece: parts.cAxis.run(\"enter\", { slide: 24 }) },\n // No priming to 0 first: the value at `t` is a function of `t`.\n { at: axes, duration: rise, piece: timeline.animate((t) => ({ growth: t })) },\n ...(legend ? [{ at: axes + rise, duration: duration * LEGEND_SHARE, piece: legend.to({ opacity: 1, y: 0 }) }] : []),\n ])\n },\n },\n /** Show series `index`: its columns come back, the group reflowing to make room, with its legend entry. */\n showSeries: {\n args: { seriesIndex: SERIES_ARG },\n easing: easeOut(),\n run: (timeline, state, children) => toggling(timeline, state, children, timeline.args.seriesIndex, 1),\n },\n /**\n * Hide series `index`: its columns narrow away (or, stacked, sink out of the\n * stack) while the rest of the group widens into the space — the mirror of\n * `showSeries`.\n */\n hideSeries: {\n args: { seriesIndex: SERIES_ARG },\n easing: easeOut(),\n run: (timeline, state, children) => toggling(timeline, state, children, timeline.args.seriesIndex, 0),\n },\n /**\n * Fade every other series — its columns **and its legend entry** — to focus\n * on `index`, which comes back to full. Nothing moves, so the group doesn't\n * reflow.\n */\n spotlightSeries: {\n args: { seriesIndex: SERIES_ARG },\n easing: easeOut(),\n run: (timeline, state, children) => {\n const parts = partsOf(children)\n if (!parts) return timeline.wait(timeline.duration)\n const index = timeline.args.seriesIndex\n const dim = state.theme.spotlight.dimOpacity\n return timeline.together([\n ...parts.bars.map((bars, i) => bars.to({ opacity: i === index ? 1 : dim })),\n ...(parts.legend ? [parts.legend.run(\"spotlightSeries\", { index })] : []),\n ])\n },\n },\n /**\n * Spotlight one **category**: every other category's columns and caption\n * fade to the theme's `dimOpacity`. Called while another is lit, the focus\n * moves directly: only the dimming changes, so nothing retracts first. A\n * category that is not there takes its slot and changes nothing.\n */\n spotlightCategory: {\n args: { categoryIndex: Types.number(0, { min: 0, step: 1 }) },\n easing: easeOut(),\n run: (timeline, state) => {\n const index = timeline.args.categoryIndex\n if (index < 0 || index >= state.data.length) return timeline.wait(timeline.duration)\n const from = state.spotlightReveal\n return timeline.animate((t) => ({ spotlight: index, spotlightReveal: from + (1 - from) * t }))\n },\n },\n /**\n * Drop the category spotlight, every category back to full. The lit\n * category is held until the fade lands; a no-op when nothing is lit, which\n * still takes its slot.\n */\n clearCategory: {\n args: {},\n easing: easeOut(),\n run: (timeline, state) => {\n const from = state.spotlightReveal\n if (from === 0) return timeline.wait(timeline.duration)\n const lit = state.spotlight\n return timeline.animate((t) => (t >= 1 ? { spotlight: null, spotlightReveal: 0 } : { spotlight: lit, spotlightReveal: from * (1 - t) }))\n },\n },\n },\n})\n\nexport type BarChart = NodeOf<typeof BarChart>\n", "import {\n Rect,\n Text,\n Types,\n component2d,\n insetsOps,\n textOps,\n type NodeOf,\n type Node2D,\n type Node2DProps,\n} from \"@motionscript/sdk\"\n\nimport { asParts, fresh } from \"../kit/entries\"\nimport { armed, arrival, arrivalOf, arrivalsEntry, beats, frameEase, type Arrival } from \"../kit/motion\"\nimport {\n DEFAULT_X_AXIS,\n clampUnit,\n enterBudget,\n type AxisConfig,\n} from \"../kit/shared\"\n\nexport interface CategoryAxisProps extends Node2DProps {\n /** One caption per category slot, left to right — the chart's rows, in order. */\n categories: string[]\n /** Caption/title text style and gutters — see {@link AxisConfig}. */\n axis: AxisConfig\n /**\n * Caption drawn centred in the gutter beneath the category row. Empty string\n * (the default) hides it.\n */\n label: string\n /**\n * Per-category opacity multiplier — how the chart's category spotlight reaches\n * the captions, so the name under a dimmed column dims with it. Missing\n * entries read as 1.\n */\n dims: number[]\n}\n\n/** How far a caption starts above its slot when it enters, in px. */\nconst SLIDE = 24\n\n/**\n * The **categorical** horizontal axis of a {@link BarChart}: a row of\n * {@link Text} captions, one per row of the data, each centred on its slot\n * beneath the baseline.\n *\n * Not a variant of the line chart's `HorizontalAxis` and deliberately not\n * folded into it: that axis places markers by *value* across a numeric range,\n * this one divides its width into equal slots and centres a name in each. The\n * two look alike on screen and share nothing underneath — one reads a scale,\n * the other counts rows.\n *\n * The slot arithmetic is the same as the plot's, because this node is laid out\n * to exactly the plot's width (the chart reserves the axis gutter with an\n * invisible twin of the value axis, as the line chart does), so a caption lands\n * under the columns it names without either side being told about the other.\n *\n * `enter` staggers the captions in left-to-right, each lifted a little above\n * its slot and transparent; the resting state is the landed one, so a chart\n * that is never told to enter still reads as finished.\n */\nexport const CategoryAxis = component2d({\n key: \"categoryAxis\",\n state: {\n categories: fresh<string[]>(() => []),\n axis: Types.custom<AxisConfig>(DEFAULT_X_AXIS),\n label: \"\",\n dims: fresh<number[]>(() => []),\n arrivals: arrivalsEntry(),\n },\n // A plain node filled its parent; one made of its captions would hug them.\n defaults: { width: \"fill\", height: \"fill\" },\n children: (state) => {\n const tickPad = insetsOps.resolve(state.axis.tickPadding).top\n const titlePad = insetsOps.resolve(state.axis.titlePadding).top\n\n const column: Node2D[] = []\n // The column's parent is the axis: its box is what the slots divide.\n const width = () => (column[0]?.parent as Node2D | null | undefined)?.layoutBounds.width ?? 0\n /** Centre x of slot `i`, in the node's centred space. */\n const slotCenter = (index: number) => {\n const n = state.categories.length\n if (n <= 0) return 0\n return -width() / 2 + (width() / n) * (index + 0.5)\n }\n\n // Each caption sits in a hug box carrying the spotlight dim, so the\n // entrance can animate the text's own opacity without the two fighting over\n // one value — opacity multiplies down the tree, which is exactly the\n // composition wanted here.\n const slots = state.categories.map((caption, i) =>\n new Rect({\n width: \"hug\",\n height: \"hug\",\n flow: \"freeform\",\n opacity: () => clampUnit(state.dims[i] ?? 1),\n x: () => slotCenter(i),\n children: [\n new Text({\n ...textOps.styleProps(state.axis.labelStyle),\n text: caption,\n opacity: () => arrivalOf(state.arrivals, i).opacity,\n y: () => arrivalOf(state.arrivals, i).shift,\n }),\n ],\n })\n )\n\n const children: Node2D[] = [new Rect({ width: \"fill\", height: \"hug\", padding: { top: tickPad }, children: slots })]\n if (state.label !== \"\") {\n children.push(\n new Text({\n ...textOps.styleProps(state.axis.titleStyle),\n text: state.label,\n opacity: () => arrivalOf(state.arrivals, \"label\").opacity,\n y: () => arrivalOf(state.arrivals, \"label\").shift,\n })\n )\n }\n\n column.push(new Rect({ flow: \"vertical\", gap: titlePad, height: \"hug\", width: \"fill\", children }))\n return asParts(column)\n },\n commands: {\n /**\n * Slide + fade every caption in from above, staggered left-to-right, then\n * bring the title down behind them. Its first frame arms them all: lifted\n * by `slide` and transparent.\n *\n * The timeline's duration is the **whole** entrance — the stagger and the\n * title are carved out of it rather than added on top. See {@link enterBudget}.\n *\n * `stagger` is seconds between successive captions (derived when omitted);\n * `slide` is the starting upward offset in px (node space is y-up, so a\n * positive start slides *down* into place).\n */\n enter: {\n args: {\n stagger: Types.custom<number | undefined>(undefined),\n slide: Types.number(SLIDE),\n },\n run: (timeline, state) => {\n const { stagger, slide } = timeline.args\n const count = state.categories.length\n const captioned = state.label !== \"\"\n const from: Arrival = { opacity: 0, shift: slide }\n const keys = [...state.categories.keys(), ...(captioned ? [\"label\"] : [])]\n const arm = timeline.animate(() => ({ arrivals: armed(keys, from) }))\n if (count === 0) return arm\n const { step, per, caption } = enterBudget(timeline.duration, count, captioned, stagger)\n const ease = frameEase(timeline)\n const lastCaptionEnds = (count - 1) * step + per\n const captions = state.categories.map((_, i) => ({ at: i * step, duration: per, piece: arrival(timeline, i, from, ease) }))\n const label = captioned ? [{ at: lastCaptionEnds, duration: caption, piece: arrival(timeline, \"label\", from, ease) }] : []\n return timeline.together([arm, beats(timeline, [...captions, ...label])])\n },\n },\n },\n})\n\nexport type CategoryAxis = NodeOf<typeof CategoryAxis>\n", "import {\n Graphics2D,\n Text,\n Types,\n component2d,\n fillOps,\n shadowOps,\n strokeOps,\n textOps,\n type Fill,\n type FillResolved,\n type NodeOf,\n // Named so this component's emitted type reaches `Node2D` through the sdk, not core.\n type Node2D as _Node2D,\n type Node2DProps,\n type Shadow,\n type ShadowResolved,\n type Stroke,\n type StrokeResolved,\n type TextStyle,\n} from \"@motionscript/sdk\"\n\nimport { fresh } from \"../kit/entries\"\nimport { frameEase } from \"../kit/motion\"\nimport { loadPaints } from \"../kit/paints\"\nimport { clampUnit, formatMarker } from \"../kit/shared\"\nimport { DEFAULT_BAR_CHART_THEME } from \"./shared\"\n\n/**\n * One drawn column, already mapped into the plot's **centred pixel space**\n * (origin at the plot centre, y-up) by the parent {@link BarChart}.\n *\n * `from`/`to` are the two ends of the column rather than a base and a height,\n * for the same reason {@link BarSpan} is a span: it is the one shape that fits a\n * grouped bar, a stacked segment and a negative value without a special case.\n */\nexport interface BarGeometry {\n /** Centre x of the column. */\n x: number\n /** Column width in px. */\n width: number\n /** Pixel y of the column's base — the baseline when grouped, the running stack top when stacked. */\n from: number\n /** Pixel y of its far end, at full {@link ChartBarsProps.growth}. */\n to: number\n /** Whether the far end takes the theme's corner radius (the stack's cap). */\n round: boolean\n /** The value this column plots, for the figure printed on it. */\n value: number\n /**\n * The figure printed on it, written as the chart's value axis writes its\n * ticks, so the two cannot disagree. Absent, the bare marker reading of\n * {@link value}.\n */\n caption?: string\n}\n\n/** Gap between a grouped column's end and the figure printed over it, in px. */\nconst VALUE_GAP = 10\n/** A segment shorter than this many multiples of the type size prints no figure. */\nconst VALUE_FIT = 1.6\n/** Fallback type size when the theme's value style leaves it unset or autofit. */\nconst VALUE_SIZE = 24\n\nexport interface ChartBarsProps extends Node2DProps {\n /** This series' columns, one per row of the chart's data. */\n bars: BarGeometry[]\n /** Pixel y of value 0 — where every column grows *from*. */\n baseline: number\n /** Column paint — this series' own, or the theme palette's entry for its index. */\n fill: Fill\n /** Optional outline on every column. */\n barStroke: Stroke\n /** Optional drop shadow on every column. */\n barShadow: Shadow\n /** Radius of the far-end corners, in px. */\n cornerRadius: number\n /**\n * Rise of every column in `[0, 1]`, measured **about the baseline**: 0\n * collapses them all onto it, 1 draws them at full length.\n *\n * Scaling about the baseline rather than about each column's own base is what\n * keeps a stack flush mid-animation — every segment's base and end travel by\n * the same factor, so the segments stay stacked instead of the upper ones\n * floating above the still-growing lower ones. It also means a negative\n * column grows *down* out of the same signal.\n */\n growth: number\n /**\n * Per-column opacity multiplier, by row index — how the chart's category\n * spotlight reaches a single column without touching the series it belongs\n * to. Missing entries read as 1.\n */\n dims: number[]\n /** Whether the figure each column plots is printed on it. */\n showValues: boolean\n /** Type style for those figures. */\n valueStyle: TextStyle\n /**\n * Whether this series draws at all — the wholesale switch behind\n * `BarChart.showSeries`/`hideSeries`, alongside the slot weight that animates\n * the reflow. Off skips the columns *and* their figures.\n */\n enabled: boolean\n}\n\n/** What the column geometry reads. */\ninterface Columns {\n readonly bars: readonly BarGeometry[]\n readonly baseline: number\n readonly growth: number\n readonly dims: readonly number[]\n readonly enabled: boolean\n readonly valueStyle: TextStyle\n}\n\n/**\n * How far column `i` is dimmed — the chart's category spotlight, and the series\n * switch on top of it.\n *\n * The captions read this rather than being hidden by `render`'s guard: they are\n * real child nodes, so a `return` in the draw would leave them on screen over a\n * series that isn't there.\n */\nfunction dimAt(s: Columns, index: number): number {\n if (!s.enabled) return 0\n return clampUnit(s.dims[index] ?? 1)\n}\n\n/** The two ends of column `i` at the current growth. */\nfunction endsAt(s: Columns, index: number): { base: number; end: number } | null {\n const bar = s.bars[index]\n if (!bar) return null\n const g = clampUnit(s.growth)\n return { base: s.baseline + (bar.from - s.baseline) * g, end: s.baseline + (bar.to - s.baseline) * g }\n}\n\n/** The type size the figures are set at, guarding the autofit/unset cases. */\nfunction valueSize(s: Columns): number {\n const size = s.valueStyle.fontSize\n return typeof size === \"number\" ? size : VALUE_SIZE\n}\n\n/** Whether column `i`'s figure sits inside the column (a stacked segment) or past its end. */\nfunction insideAt(s: Columns, index: number): boolean {\n const bar = s.bars[index]\n return bar !== undefined && Math.abs(bar.from - s.baseline) > 0.5\n}\n\n/**\n * The figure printed on column `i`, or `\"\"` where there is no room for it.\n *\n * Blanking the text rather than dropping the node keeps the caption list\n * index-aligned with the columns, so a chart that rescales into more room simply\n * starts printing again.\n */\nfunction captionAt(s: Columns, index: number): string {\n const bar = s.bars[index]\n if (!bar || bar.value === 0) return \"\"\n // An interior segment prints inside itself, so it has to be tall enough to\n // hold a line of type; a grouped column prints in the headroom above its end,\n // where the only constraint is the plot's own top.\n if (insideAt(s, index)) {\n const ends = endsAt(s, index)\n if (!ends) return \"\"\n if (Math.abs(ends.end - ends.base) < valueSize(s) * VALUE_FIT) return \"\"\n }\n return bar.caption ?? formatMarker(bar.value)\n}\n\n/** Where column `i`'s figure sits, in the node's centred y-up space. */\nfunction captionY(s: Columns, index: number): number {\n const ends = endsAt(s, index)\n if (!ends) return 0\n if (insideAt(s, index)) return (ends.base + ends.end) / 2\n // Text is centred on its `y`, so clear the column's end by the gap plus half a\n // line — on whichever side of the baseline the column actually reached.\n const direction = ends.end >= ends.base ? 1 : -1\n return ends.end + direction * (VALUE_GAP + valueSize(s) / 2)\n}\n\n/**\n * One **series** of a {@link BarChart}, drawn as one command list: its column in\n * every category, plus the figures printed on them.\n *\n * A node per series rather than a node per bar, deliberately — it is the same\n * unit `ChartLine` is, which is what lets `showSeries`/`hideSeries`/\n * `spotlightSeries` mean exactly the same thing on a bar chart as on a line\n * chart: enable the node, or tween its opacity. The columns' *positions* come\n * from the parent (it owns the slot arithmetic both modes share), so this part\n * only has to know how to draw a span and grow it.\n *\n * The figures are real {@link Text} children rather than glyphs in the command\n * list, so they take the theme's type style, are measured by the layout engine,\n * and ride the node's own opacity — which is why hiding the series takes its\n * figures with it for free.\n */\nexport const ChartBars = component2d({\n key: \"chartBars\",\n state: {\n bars: fresh<BarGeometry[]>(() => []),\n baseline: 0,\n fill: Types.custom<Fill, FillResolved[]>([], { resolve: fillOps.resolve, lerp: fillOps.lerp }),\n barStroke: Types.custom<Stroke, StrokeResolved[]>(DEFAULT_BAR_CHART_THEME.bar.stroke, { resolve: strokeOps.resolve, lerp: strokeOps.lerp }),\n barShadow: Types.custom<Shadow, ShadowResolved[]>(DEFAULT_BAR_CHART_THEME.bar.shadow, { resolve: shadowOps.resolve, lerp: shadowOps.lerp }),\n cornerRadius: DEFAULT_BAR_CHART_THEME.bar.cornerRadius,\n growth: 1,\n dims: fresh<number[]>(() => []),\n showValues: false,\n valueStyle: Types.custom<TextStyle>(DEFAULT_BAR_CHART_THEME.valueStyle),\n enabled: true,\n },\n // A plain node filled its parent; one made of its figures would hug them.\n defaults: { width: \"fill\", height: \"fill\" },\n // One caption per column, built once (the row count is fixed at construction,\n // like an axis's marker count) and bound to the live geometry so it travels\n // with its column as the chart grows, rescales or reflows.\n children: (state) => {\n if (!state.showValues) return []\n return state.bars.map((_, i) =>\n Text({\n ...textOps.styleProps(state.valueStyle),\n text: () => captionAt(state, i),\n x: () => state.bars[i]?.x ?? 0,\n y: () => captionY(state, i),\n opacity: () => dimAt(state, i),\n })\n )\n },\n // The column stroke and shadow are named past the conventional slots, so they are named here.\n load: (loader, state) =>\n loadPaints(loader, { fills: [state.fill], shadows: [state.barShadow], strokes: [state.barStroke] }),\n commands: {\n /**\n * Raise every column from the baseline by tweening `growth` `0 → 1`.\n *\n * The mirror of `ChartLine.enter`: same contract that the resting state is\n * the finished one, so the chart arms this back to 0 before animating rather\n * than the node hiding itself at construction. No priming to 0 first: the\n * value at `t` is a function of `t`, so a repeat call re-raises by definition.\n */\n enter: (timeline) => {\n const ease = frameEase(timeline)\n return timeline.animate((t) => ({ growth: ease(t) }))\n },\n },\n render: (ctx, state) => {\n if (state.enabled) {\n for (let i = 0; i < state.bars.length; i++) {\n const bar = state.bars[i]\n const ends = endsAt(state, i)\n if (!ends || bar.width <= 0) continue\n const height = Math.abs(ends.end - ends.base)\n if (height <= 0) continue\n\n const radius = bar.round ? Math.min(state.cornerRadius, height) : 0\n // A column per `Graphics2D` rather than one list for the series: the\n // category spotlight dims individual columns, and opacity is a property\n // of the command list rather than of a shape within it.\n const g = new Graphics2D()\n const rect = g.rect({\n x: bar.x,\n y: (ends.base + ends.end) / 2,\n width: bar.width,\n height,\n // Corner keys are visual (the renderer keeps them in y-up author space),\n // so a column reaching *up* rounds its `top*` corners and one reaching\n // down its `bottom*` ones. Only the far end rounds, which is what makes\n // a stack read as a single capped column.\n cornerRadius:\n radius > 0\n ? ends.end >= ends.base\n ? { topLeft: radius, topRight: radius, bottomLeft: 0, bottomRight: 0 }\n : { bottomLeft: radius, bottomRight: radius, topLeft: 0, topRight: 0 }\n : 0,\n })\n rect.shadow(state.barShadow)\n rect.fill(state.fill)\n rect.stroke(state.barStroke)\n ctx.draw(g.opacity(dimAt(state, i)))\n }\n }\n ctx.children()\n },\n})\n\nexport type ChartBars = NodeOf<typeof ChartBars>\n", "/**\n * Types and pure helpers specific to the {@link BarChart} family: how a row's\n * values become the spans its columns cover, how the value axis is scaled around\n * them, and the theme the bars add to the shared chart vocabulary.\n *\n * Everything a line or a pie chart would want too — what a series is, the axis\n * domain model, the theme leaf resolvers, the legend and plot-panel branches —\n * lives in `../kit/shared.ts` and is imported here.\n *\n * Kept renderer-agnostic (no `Node` imports) so a host's inspector mapping\n * can build a partial {@link BarChartTheme} without\n * pulling the components in.\n */\n\nimport {\n fillOps,\n lerpNumber,\n shadowOps,\n strokeOps,\n textOps,\n type DataRecord,\n type Fill,\n type Shadow,\n type ShadowResolved,\n type Stroke,\n type StrokeResolved,\n type TextStyle,\n} from \"@motionscript/sdk\"\n\nimport {\n CHART_PALETTE,\n DEFAULT_LEGEND,\n DEFAULT_PLOT_AREA,\n DEFAULT_SERIES_FILLS,\n DEFAULT_X_AXIS,\n DEFAULT_Y_AXIS,\n SPOTLIGHT_DIM,\n lerpAxisConfig,\n lerpLegendConfig,\n lerpPlotArea,\n lerpSeriesFills,\n niceStep,\n num,\n resolveAxisConfig,\n resolveLegendConfig,\n resolvePlotArea,\n resolveSeriesFills,\n text,\n type AxisConfig,\n type ChartSeries,\n type LegendConfig,\n type LegendTheme,\n type PlotAreaConfig,\n type Range,\n} from \"../kit/shared\"\n\n/**\n * One series in a {@link BarChart}: a column of the table drawn as one bar per\n * row — side by side with its siblings within each category's slot, or stacked\n * on top of them.\n *\n * The family-wide {@link ChartSeries} under a name that says what it draws here.\n */\nexport type BarSeries = ChartSeries\n\n/**\n * One drawn bar's span in **data units**: the value its base sits at and the\n * value its far end reaches.\n *\n * A span rather than a height because that is the one shape both modes fit. A\n * grouped bar runs from zero to its value; a stacked segment runs from the\n * running total below it to that total plus its own value. Negative values fall\n * out of the same arithmetic — the span simply runs the other way — so a chart\n * with a loss in it needs no special case, only a baseline that isn't the floor.\n */\nexport interface BarSpan {\n /** Value the bar's base sits at — 0 when grouped, the running total when stacked. */\n from: number\n /** Value its far end reaches. */\n to: number\n /** The value this bar plots — `to - from`, kept for the figure printed on it. */\n value: number\n}\n\n/**\n * The spans one row's bars cover, one per series, in series order.\n *\n * Stacking is a running sum in the order the series are listed, which is also\n * the order they're drawn: series 0 sits on the baseline and each later one\n * rides on what's below it. A zero-valued series still yields a (degenerate)\n * span so the returned list stays index-aligned with the series list.\n */\nexport function barSpans(\n row: DataRecord,\n series: BarSeries[],\n stacked: boolean\n): BarSpan[] {\n let running = 0\n return series.map((s) => {\n const value = num(row, s.field)\n if (!stacked) return { from: 0, to: value, value }\n const from = running\n running += value\n return { from, to: running, value }\n })\n}\n\n/**\n * The index of the series whose segment ends a row's stack — the one that gets\n * the rounded cap, so a stack reads as one capped column rather than a pile of\n * separate boxes. `-1` when every value in the row is zero.\n *\n * Read off the *last non-zero* series rather than the last one listed, so a\n * series that happens to be 0 in this row doesn't take the cap and leave the\n * segment below it square.\n */\nexport function stackCapIndex(row: DataRecord, series: BarSeries[]): number {\n let last = -1\n for (let i = 0; i < series.length; i++) {\n if (num(row, series[i].field) !== 0) last = i\n }\n return last\n}\n\n/** The captions beneath the baseline: one per row, read off the category column. */\nexport function barCategories(data: DataRecord[], field: string): string[] {\n return data.map((row) => text(row, field))\n}\n\n/**\n * The `[min, max]` of everything the chart draws, **zero always included**.\n *\n * Zero is in the range whatever the data says, because a bar chart's baseline is\n * zero: a scale starting at 40 draws a column three times its neighbour for a\n * 10% difference, which is the classic way to lie with one. A chart that really\n * does want a cropped axis says so with an explicit range.\n */\nexport function barValueExtent(\n data: DataRecord[],\n series: BarSeries[],\n stacked: boolean\n): Range {\n let lo = 0\n let hi = 0\n for (const row of data) {\n for (const span of barSpans(row, series, stacked)) {\n lo = Math.min(lo, span.from, span.to)\n hi = Math.max(hi, span.from, span.to)\n }\n }\n return lo < hi ? [lo, hi] : [0, 1]\n}\n\n/**\n * Effective value scale: the explicit `range`, else the data's extent nudged out\n * to tidy step bounds so the markers read as round numbers.\n *\n * `divisions` is how many segments the auto-range is stepped into (one fewer\n * than the marker count), so the ticks the axis draws land on the bounds this\n * picks.\n */\nexport function resolveBarScale(\n data: DataRecord[],\n series: BarSeries[],\n stacked: boolean,\n range: Range | undefined,\n divisions: number\n): Range {\n if (range) return range\n const [lo, hi] = barValueExtent(data, series, stacked)\n const segments = Math.max(1, Math.round(divisions))\n const step = niceStep((hi - lo) / segments || 1)\n return [Math.floor(lo / step) * step, Math.ceil(hi / step) * step]\n}\n\n// ---- Theme ---------------------------------------------------------------\n\n/** How the columns themselves are drawn and how the slot is divided up. */\nexport interface BarStyleConfig {\n /**\n * Radius of a column's far-end corners, in px. Only the outer end rounds — an\n * interior stacked segment stays square, so the stack reads as one column.\n */\n cornerRadius: number\n /**\n * Fraction of its category's slot the bars fill, `[0, 1]`. The remainder is\n * the channel between one category and the next, which is what makes a bar\n * chart read as groups rather than as a solid block.\n */\n slotFill: number\n /** Gap between the grouped bars *within* one slot, in px. Ignored when stacked. */\n groupGap: number\n /** Optional outline on every column. */\n stroke: Stroke\n /** Optional drop shadow on every column. */\n shadow: Shadow\n}\n\n/** A {@link BarChart}'s full, resolved visual theme. */\nexport interface BarChartConfig {\n /**\n * Default per-series bar paint, cycled by series index (`i % length`). A\n * series' own `fill`, when set, replaces its entry outright.\n */\n seriesFills: Fill[]\n bar: BarStyleConfig\n /** The category axis beneath the baseline. */\n xAxis: AxisConfig\n /** The numeric value axis up the left. */\n yAxis: AxisConfig\n plotArea: PlotAreaConfig\n legend: LegendConfig\n /** The figure printed on each column, when the chart prints them. */\n valueStyle: TextStyle\n /** How far everything *outside* a spotlit series or category fades. */\n spotlight: { dimOpacity: number }\n}\n\n/** Author-facing partial theme — every branch optional and deep-merged. */\nexport type BarChartTheme = {\n seriesFills?: Fill[]\n bar?: Partial<BarStyleConfig>\n xAxis?: Partial<AxisConfig>\n yAxis?: Partial<AxisConfig>\n plotArea?: Partial<PlotAreaConfig>\n legend?: LegendTheme\n valueStyle?: TextStyle\n spotlight?: Partial<BarChartConfig[\"spotlight\"]>\n}\n\n/** The bar chart's out-of-the-box look. */\nexport const DEFAULT_BAR_CHART_THEME: BarChartConfig = {\n seriesFills: DEFAULT_SERIES_FILLS,\n bar: {\n cornerRadius: 6,\n slotFill: 0.72,\n groupGap: 6,\n stroke: [],\n shadow: [],\n },\n // A category axis names its slots, so its captions are set like a caption\n // rather than like a numeric marker: same size, less weight than the line\n // chart's ticks, and centred on the slot.\n xAxis: {\n ...DEFAULT_X_AXIS,\n labelStyle: { ...DEFAULT_X_AXIS.labelStyle, fontWeight: 600 },\n tickPadding: 16,\n },\n yAxis: DEFAULT_Y_AXIS,\n plotArea: DEFAULT_PLOT_AREA,\n legend: DEFAULT_LEGEND,\n valueStyle: {\n fontSize: 24,\n fontWeight: 700,\n textAlign: \"center\",\n fill: CHART_PALETTE.markerColor,\n },\n spotlight: { dimOpacity: SPOTLIGHT_DIM },\n}\n\nfunction resolveBarStyle(\n bar: Partial<BarStyleConfig> | undefined,\n previous: BarStyleConfig\n): BarStyleConfig {\n return {\n cornerRadius: bar?.cornerRadius ?? previous.cornerRadius,\n slotFill: bar?.slotFill ?? previous.slotFill,\n groupGap: bar?.groupGap ?? previous.groupGap,\n stroke: strokeOps.resolve(bar?.stroke ?? previous.stroke),\n shadow: shadowOps.resolve(bar?.shadow ?? previous.shadow),\n }\n}\n\nfunction lerpBarStyle(\n from: BarStyleConfig,\n to: BarStyleConfig,\n t: number\n): BarStyleConfig {\n return {\n cornerRadius: lerpNumber(from.cornerRadius, to.cornerRadius, t),\n slotFill: lerpNumber(from.slotFill, to.slotFill, t),\n groupGap: lerpNumber(from.groupGap, to.groupGap, t),\n stroke: strokeOps.lerp(\n from.stroke as StrokeResolved[],\n to.stroke as StrokeResolved[],\n t\n ),\n shadow: shadowOps.lerp(\n from.shadow as ShadowResolved[],\n to.shadow as ShadowResolved[],\n t\n ),\n }\n}\n\n/**\n * {@link BarChart}'s `theme` entry's resolve: merges the author's partial\n * {@link BarChartTheme} onto `previous` (falling back to\n * {@link DEFAULT_BAR_CHART_THEME} on first resolution), fully resolving every\n * leaf so the tween below always has resolved data to interpolate.\n */\nexport function resolveBarChartTheme(\n theme: BarChartTheme | undefined,\n previous?: BarChartConfig\n): BarChartConfig {\n const base = previous ?? DEFAULT_BAR_CHART_THEME\n return {\n seriesFills: resolveSeriesFills(theme?.seriesFills, base.seriesFills),\n bar: resolveBarStyle(theme?.bar, base.bar),\n xAxis: resolveAxisConfig(theme?.xAxis, base.xAxis),\n yAxis: resolveAxisConfig(theme?.yAxis, base.yAxis),\n plotArea: resolvePlotArea(theme?.plotArea, base.plotArea),\n legend: resolveLegendConfig(theme?.legend, base.legend),\n valueStyle: textOps.resolveStyle(theme?.valueStyle, base.valueStyle),\n spotlight: {\n dimOpacity: theme?.spotlight?.dimOpacity ?? base.spotlight.dimOpacity,\n },\n }\n}\n\n/** {@link BarChart}'s `theme` entry's lerp — leaf by leaf. */\nexport function lerpBarChartTheme(\n from: BarChartConfig,\n to: BarChartConfig,\n t: number\n): BarChartConfig {\n return {\n seriesFills: lerpSeriesFills(from.seriesFills, to.seriesFills, t),\n bar: lerpBarStyle(from.bar, to.bar, t),\n xAxis: lerpAxisConfig(from.xAxis, to.xAxis, t),\n yAxis: lerpAxisConfig(from.yAxis, to.yAxis, t),\n plotArea: lerpPlotArea(from.plotArea, to.plotArea, t),\n legend: lerpLegendConfig(from.legend, to.legend, t),\n valueStyle: textOps.lerpStyle(from.valueStyle, to.valueStyle, t),\n spotlight: {\n dimOpacity: lerpNumber(\n from.spotlight.dimOpacity,\n to.spotlight.dimOpacity,\n t\n ),\n },\n }\n}\n\n/** The paint series `index` is drawn in: its own override, else the palette's entry. */\nexport function resolveBarFill(\n palette: Fill[],\n series: BarSeries,\n index: number\n): Fill {\n if (series.fill != null) return fillOps.resolve(series.fill)\n if (palette.length === 0) return \"white\"\n return palette[index % palette.length]\n}\n", "import {\n Rect,\n Types,\n component2d,\n easeOut,\n type ChildView,\n type DataRecord,\n type NodeOf,\n // Named so this component's emitted type reaches `Node2D` through the sdk, not core.\n type Node2D,\n type Node2DProps,\n type Timeline,\n} from \"@motionscript/sdk\"\n\nimport { asParts, fresh } from \"../kit/entries\"\nimport { beats, type Beat } from \"../kit/motion\"\nimport { clampUnit } from \"../kit/shared\"\nimport { PieLegend } from \"./pie-legend\"\nimport { PieRing } from \"./pie-ring\"\nimport {\n DEFAULT_PIE_CHART_THEME,\n lerpPieChartTheme,\n resolvePieChartTheme,\n resolvePieSlices,\n type PieChartConfig,\n type PieChartTheme,\n type PieLabelMode,\n type PieLabelPlacement,\n type PieSlice,\n} from \"./shared\"\n\nexport interface PieChartProps extends Node2DProps {\n /** One row per slice — a plain record of field name to value. */\n data: DataRecord[]\n /** Key into each row for its slice caption, used on the labels and the legend. */\n categoryField: string\n /** Key into each row for its slice magnitude — the wedge's share of the ring. */\n valueField: string\n /**\n * The chart's visual theme — slice palette, ring geometry, label and centre\n * type, legend. Every branch is optional and deep-merged onto\n * {@link DEFAULT_PIE_CHART_THEME}.\n */\n theme?: PieChartTheme\n /** What the ring's labels say. */\n labelMode?: PieLabelMode\n /** Where they sit: inside their wedge, outside on a leader, or nowhere. */\n labelPlacement?: PieLabelPlacement\n /** Whether the legend column beside the ring is drawn at all. */\n showLegend?: boolean\n /** Gap between the ring and the legend, in px. */\n gap?: number\n /** Text set in the ring's hole — see `revealCenter`. */\n centerLabel?: string\n /** Smaller second line under {@link centerLabel}. */\n centerCaption?: string\n /**\n * Whether the hole's summary is drawn at rest. `true` (the default) is the\n * plain answer; a scene that wants the conclusion to land on a later beat sets\n * this `false` and runs `revealCenter` when it gets there.\n */\n showCenter?: boolean\n}\n\n/** What the selection reads: the rows, how they are cut, and the pops the commands move. */\nexport interface PieChartValues {\n readonly data: DataRecord[]\n readonly categoryField: string\n readonly valueField: string\n readonly theme: PieChartConfig\n readonly pops: number[]\n}\n\n/**\n * How `enter` divides its duration: the wedges sweeping in, then the labels and\n * the legend arriving together. Shares of the whole, so they sum to 1.\n */\nconst SWEEP_SHARE = 0.65\nconst LABEL_SHARE = 0.35\n\nconst SLICE_ARG = Types.number(0, { min: 0, step: 1 })\n\n/** The slices the ring and the legend draw, resolved from the rows and the palette. */\nfunction slicesOf(s: PieChartValues): PieSlice[] {\n return resolvePieSlices(s.data, s.categoryField, s.valueField, s.theme.sliceFills)\n}\n\n/** How far slice `index` is pushed out, in `[0, 1]`. */\nfunction popAt(s: PieChartValues, index: number): number {\n return clampUnit(s.pops[index] ?? 0)\n}\n\n/** Whether slice `index` is grown out past the ring's rim: past half way, so a slice mid-swell is not yet counted. */\nexport function isSliceSelected(s: PieChartValues, index: number): boolean {\n return popAt(s, index) > 0.5\n}\n\n/**\n * How far each slice is dimmed by the selection, one entry per slice: 1 while\n * nothing is out, and for the selected slice itself; falling toward the theme's\n * `dimOpacity` for the rest in proportion to how far the selection has grown.\n */\nfunction sliceDims(s: PieChartValues): number[] {\n let focus = 0\n for (let i = 0; i < s.data.length; i++) focus = Math.max(focus, popAt(s, i))\n const dim = s.theme.spotlight.dimOpacity\n if (focus <= 0) return s.data.map(() => 1)\n return s.data.map((_, i) => 1 - (1 - clampUnit(dim)) * focus * (1 - popAt(s, i)))\n}\n\ntype AnyTimeline = Timeline<any, any>\n\n/**\n * Every slice's pop eased from where it is to what `pop` asks for. A move that\n * changes nothing still takes its slot, writing nothing.\n */\nfunction popping(timeline: AnyTimeline, s: PieChartValues, pop: (index: number) => number) {\n const from = s.data.map((_, i) => popAt(s, i))\n const targets = s.data.map((_, i) => clampUnit(pop(i)))\n if (from.every((value, i) => value === targets[i])) return timeline.wait(timeline.duration)\n return timeline.animate((t) => ({ pops: from.map((value, i) => value + (targets[i] - value) * t) }))\n}\n\n/** The ring and the legend, found through the chart's children: the last row of them is the one it built. */\nfunction partsOf(children: readonly ChildView[]) {\n for (let i = children.length - 1; i >= 0; i--) {\n const row = children[i]\n const ring = Rect.is(row) ? row.children.find(PieRing.is) : undefined\n if (ring) return { ring, legend: row.children.find(PieLegend.is) ?? null }\n }\n return null\n}\n\n/**\n * A pie or donut chart, composed from two parts laid out by a horizontal-flow\n * {@link Rect} — the ring filling whatever the hugging legend leaves, both\n * centred:\n *\n * - {@link PieRing} — the wedges, each sized by its share of the total, with its\n * label inside its own band or pinned outside on a leader line, and the\n * summary in the hole.\n * - {@link PieLegend} — a column of swatch-and-caption rows naming each slice.\n *\n * **One mark per row, not per column**: a pie's palette is indexed by row and\n * there is nothing to hide, since dropping a slice would re-cut every other\n * wedge. `selectSlice` and its siblings are what a pie has instead — a slice\n * swells out of the ring and everything else dims behind it.\n *\n * **Resting state is the finished chart**; `enter` arms every part on its first\n * frame. The one exception is the hole's summary, which a scene can hold back\n * with `showCenter: false`, because it is usually the chart's *conclusion*.\n */\nexport const PieChart = component2d({\n key: \"pieChart\",\n state: {\n data: fresh<DataRecord[]>(() => []),\n categoryField: \"\",\n valueField: \"\",\n theme: Types.custom<PieChartTheme, PieChartConfig>(DEFAULT_PIE_CHART_THEME, { resolve: resolvePieChartTheme, lerp: lerpPieChartTheme }),\n labelMode: Types.custom<PieLabelMode>(\"percent\"),\n labelPlacement: Types.custom<PieLabelPlacement>(\"leader\"),\n gap: 40,\n centerLabel: \"\",\n centerCaption: \"\",\n /**\n * How far each slice is pushed out of the ring, `0`–`1` per index. Held here\n * rather than on the ring because two parts read it: the wedges grow out by\n * it, and the legend entries dim by it. Seeded flush, one to a row.\n */\n pops: fresh<number[]>(() => []),\n /** Read as the chart is built: the legend is a part the chart builds or doesn't. */\n showLegend: Types.boolean(true, { animatable: false }),\n /** Read as the chart is built: whether the hole's summary is drawn at rest. */\n showCenter: Types.boolean(true, { animatable: false }),\n },\n defaults: {\n // Its parts fill it; a node made of them would hug them.\n width: \"fill\",\n height: \"fill\",\n pops: (state) => slicesOf(state).map(() => 0),\n },\n allowedChildren: [],\n // The slice *structure* (one label and one legend row per row) is read once;\n // the slices themselves, their paint and the selection are bound, so a theme\n // tween re-paints and a `set` of `data` re-cuts the wedges.\n children: (state) => {\n const theme = () => state.theme\n const slices = () => slicesOf(state)\n const parts: Node2D[] = [\n new PieRing({\n width: \"fill\",\n height: \"fill\",\n slices,\n ring: () => theme().ring,\n label: () => theme().label,\n centerStyle: () => theme().center,\n labelMode: state.labelMode,\n labelPlacement: state.labelPlacement,\n pops: () => state.pops,\n dimOpacity: () => theme().spotlight.dimOpacity,\n centerLabel: state.centerLabel,\n centerCaption: state.centerCaption,\n centerReveal: state.showCenter === false ? 0 : 1,\n }) as unknown as Node2D,\n ]\n if (state.showLegend !== false) {\n parts.push(\n new PieLegend({\n slices,\n legend: () => theme().legend,\n dims: () => sliceDims(state),\n // The column's spacing is the theme's as the chart was built, like the\n // entries' type and chips.\n gap: state.theme.legend.itemGap / 3,\n padding: state.theme.legend.padding,\n }) as unknown as Node2D\n )\n }\n const row = new Rect({ flow: \"horizontal\", width: \"fill\", height: \"fill\", gap: state.gap, align: \"center\", children: parts })\n return asParts([row as unknown as Node2D])\n },\n commands: {\n /**\n * Animate the whole chart in: the wedges sweep in clockwise from the theme's\n * `startAngle`, then the labels fade up and the legend staggers in together.\n * Its first frame arms the wedges, the labels and the legend; the hole's\n * summary keeps its own signal. `duration` is the **whole** entrance.\n */\n enter: {\n args: {},\n // Held from the chart's arrival, so it never shows finished ahead of its draw.\n entrance: true,\n easing: easeOut(),\n run: (timeline, _state, children) => {\n const parts = partsOf(children)\n if (!parts) return timeline.wait(timeline.duration)\n const { ring, legend } = parts\n const sweep = timeline.duration * SWEEP_SHARE\n const labels = timeline.duration * LABEL_SHARE\n const at0 = (piece: Beat[\"piece\"]): Beat => ({ at: 0, duration: 0, piece })\n return beats(timeline, [\n at0(ring.to({ growth: 0, labelReveal: 0 })),\n ...(legend ? [at0(legend.run(\"arm\"))] : []),\n { at: 0, duration: sweep, piece: ring.run(\"sweep\") },\n { at: sweep, duration: labels, piece: ring.run(\"revealLabels\") },\n ...(legend ? [{ at: sweep, duration: labels, piece: legend.run(\"enter\") }] : []),\n ])\n },\n },\n /**\n * Fade the hole's summary up. Held out of `enter` on purpose: what sits in a\n * donut's hole is usually the chart's conclusion, so the scene lands it on\n * its own beat. Pair it with `showCenter: false`.\n */\n revealCenter: {\n args: {},\n easing: easeOut(),\n run: (timeline, _state, children) => partsOf(children)?.ring.run(\"revealCenter\") ?? timeline.wait(timeline.duration),\n },\n /** Grow slice `index` out past the ring's rim and dim the rest, leaving any other selected slice where it is. */\n selectSlice: {\n args: { sliceIndex: SLICE_ARG },\n easing: easeOut(),\n run: (timeline, state) => {\n const index = timeline.args.sliceIndex\n return popping(timeline, state, (i) => (i === index ? 1 : popAt(state, i)))\n },\n },\n /** Settle slice `index` back flush with the ring. */\n deselectSlice: {\n args: { sliceIndex: SLICE_ARG },\n easing: easeOut(),\n run: (timeline, state) => {\n const index = timeline.args.sliceIndex\n return popping(timeline, state, (i) => (i === index ? 0 : popAt(state, i)))\n },\n },\n /** Move the focus to slice `index` alone, settling whatever else was out. */\n selectOnlySlice: {\n args: { sliceIndex: SLICE_ARG },\n easing: easeOut(),\n run: (timeline, state) => {\n const index = timeline.args.sliceIndex\n return popping(timeline, state, (i) => (i === index ? 1 : 0))\n },\n },\n /** Settle every slice flush and bring the whole chart back to full. */\n clearSelection: {\n args: {},\n easing: easeOut(),\n run: (timeline, state) => popping(timeline, state, () => 0),\n },\n },\n})\n\nexport type PieChart = NodeOf<typeof PieChart>\n", "import {\n Flow,\n Paint,\n Rect,\n Shapes,\n Text,\n Types,\n component2d,\n textOps,\n type NodeOf,\n type RectProps,\n // Named so the column's emitted type reaches the Rect surface it spreads through the sdk, not core.\n type Alignment as _Alignment,\n type CornerRadiusResolved as _CornerRadiusResolved,\n type CornerStyleResolved as _CornerStyleResolved,\n type Fill as _Fill,\n type FillResolved as _FillResolved,\n type FlowMode as _FlowMode,\n type FlowResolved as _FlowResolved,\n type GapSize as _GapSize,\n type Node2D as _Node2D,\n type Node2DProps as _Node2DProps,\n type RectCornerRadius as _RectCornerRadius,\n type RectCornerStyle as _RectCornerStyle,\n type Shadow as _Shadow,\n type ShadowResolved as _ShadowResolved,\n type Stroke as _Stroke,\n type StrokeResolved as _StrokeResolved,\n type Vector2 as _Vector2,\n} from \"@motionscript/sdk\"\n\nimport { fresh } from \"../kit/entries\"\nimport { armed, arrival, arrivalOf, arrivalsEntry, beats, frameEase, type Arrival } from \"../kit/motion\"\nimport {\n DEFAULT_LEGEND,\n clampUnit,\n type LegendConfig,\n} from \"../kit/shared\"\nimport type { PieSlice } from \"./shared\"\n\nexport interface PieLegendProps extends RectProps {\n /** One entry per slice, in ring order. */\n slices: PieSlice[]\n /** Padding, caption style, gaps, and swatch geometry — see {@link LegendConfig}. */\n legend: LegendConfig\n /**\n * Per-slice opacity multiplier, by index — the same signal that dims the ring,\n * so an entry fades in step with the wedge it names. Missing entries read as 1.\n */\n dims: number[]\n}\n\n/** How far an entry starts to the right of its slot when it enters, in px. */\nconst SLIDE = 16\n\n/**\n * The legend of a {@link PieChart}: a **vertical-flow column** of entries, one per\n * slice, each pairing the slice's caption with its colour swatch.\n *\n * A column rather than the cartesian charts' row, because it sits *beside* the\n * ring rather than beneath the plot — a pie is as tall as it is wide, so the\n * space it leaves over is a vertical strip. It is otherwise the same entry: the\n * swatch carries the colour, the caption reads in the legend's neutral ink, and\n * both take the shared {@link LegendConfig}.\n *\n * There is no show/hide here. A pie's marks are its *rows*, and a row that\n * shouldn't be in the chart shouldn't be in the table — removing one would\n * silently re-cut every other wedge's share, which is a different chart rather\n * than the same chart with something hidden. Selection is what a pie has\n * instead, and it arrives through {@link PieLegendProps.dims}.\n */\nexport const PieLegend = component2d({\n key: \"pieLegend\",\n state: {\n ...Paint.state,\n ...Flow.state,\n ...Shapes.corners,\n slices: fresh<PieSlice[]>(() => []),\n legend: Types.custom<LegendConfig>(DEFAULT_LEGEND),\n dims: fresh<number[]>(() => []),\n arrivals: arrivalsEntry(),\n },\n // A column, for the reason `ChartLegend` gives for its row: a `Rect` given no\n // flow lays its children out `freeform`, every caption on top of the last.\n defaults: {\n flow: \"vertical\",\n align: \"centerLeft\",\n width: \"hug\",\n height: \"hug\",\n gap: (state) => state.legend.itemGap / 3,\n padding: (state) => state.legend.padding,\n },\n // One entry per slice, ordered top → bottom. The selection dim rides a wrapper\n // rather than the entry itself, so the entrance can animate the entry's own\n // opacity without the two fighting over one value — opacity multiplies down\n // the tree, which is exactly the composition wanted here.\n children: (state) => {\n const marker = state.legend.markerStyle\n return state.slices.map((_, index) =>\n Rect({\n width: \"hug\",\n height: \"hug\",\n flow: \"freeform\",\n opacity: () => clampUnit(state.dims[index] ?? 1),\n children: [\n Rect({\n flow: \"horizontal\",\n gap: 12,\n align: \"center\",\n width: \"hug\",\n height: \"hug\",\n opacity: () => arrivalOf(state.arrivals, index).opacity,\n x: () => arrivalOf(state.arrivals, index).shift,\n children: [\n Rect({\n width: marker.height,\n height: marker.height,\n cornerRadius: marker.borderRadius,\n fill: () => state.slices[index]?.fill ?? \"white\",\n stroke: marker.stroke,\n shadow: marker.shadow,\n }),\n Text({\n ...textOps.styleProps(state.legend.textStyle),\n text: () => state.slices[index]?.category ?? \"\",\n }),\n ],\n }),\n ],\n })\n )\n },\n commands: {\n ...Paint.commands,\n /**\n * Every entry at its pre-entrance state, shifted right and transparent: what\n * a chart shows ahead of this legend's own beat in its entrance, since\n * `enter` arms the entries only on its first frame.\n */\n arm: (timeline, state) =>\n timeline.animate(() => ({ arrivals: armed([...state.slices.keys()], { opacity: 0, shift: SLIDE }) })),\n /**\n * Slide + fade every entry in from the right, staggered top-to-bottom. Its\n * first frame arms them all: shifted right and transparent.\n *\n * The timeline's duration is the **whole** entrance: the stagger is carved\n * out of it rather than added on top, so the command takes exactly the time\n * the timeline laid out for it.\n */\n enter: (timeline, state) => {\n const count = state.slices.length\n if (count === 0) return timeline.wait(timeline.duration)\n const from: Arrival = { opacity: 0, shift: SLIDE }\n const arm = timeline.animate(() => ({ arrivals: armed([...state.slices.keys()], from) }))\n // Each gap is a fifth of an entry's own move, the rhythm the axes use;\n // solved for from the budget rather than added to it.\n const per = timeline.duration / (1 + Math.max(0, count - 1) / 5)\n const step = per / 5\n const ease = frameEase(timeline)\n const entries = state.slices.map((_, i) => ({ at: i * step, duration: per, piece: arrival(timeline, i, from, ease) }))\n return timeline.together([arm, beats(timeline, entries)])\n },\n },\n measure: (measurer, state, children) => Flow.measure(measurer, state, children, Paint.inset(state)),\n layout: (box, state, children) => Flow.layout(box, state, children, Paint.inset(state)),\n render: (ctx, state) => Paint.draw(ctx, state, Shapes.rect(ctx.size, state)),\n})\n\nexport type PieLegend = NodeOf<typeof PieLegend>\n", "import {\n Graphics2D,\n PathBuilder,\n Rect,\n Text,\n Types,\n component2d,\n fillOps,\n shadowOps,\n strokeOps,\n textOps,\n type Fill,\n type FillResolved,\n type NodeOf,\n type Node2D,\n type Node2DProps,\n type TextStyle,\n type Vector2,\n} from \"@motionscript/sdk\"\n\nimport { asParts, fresh } from \"../kit/entries\"\nimport { frameEase } from \"../kit/motion\"\nimport { loadPaints } from \"../kit/paints\"\nimport { clampUnit, formatPercent } from \"../kit/shared\"\nimport {\n DEFAULT_PIE_CHART_THEME,\n pieArcs,\n relaxLabels,\n ringPoint,\n type CenterConfig,\n type LabelConfig,\n type PieArc,\n type PieLabelMode,\n type PieLabelPlacement,\n type PieSlice,\n type RingConfig,\n} from \"./shared\"\n\nexport interface PieRingProps extends Node2DProps {\n /** One entry per slice: its `category`, `value` and `fill`. */\n slices: PieSlice[]\n /** Ring geometry and paint — gap, hole, start angle, selection growth. */\n ring: RingConfig\n /** How the labels are set, and the leader lines that point at them. */\n label: LabelConfig\n /**\n * How the hole's summary is set.\n *\n * Named `centerStyle` rather than `center` because every node already has a\n * `center` — the anchor its position is measured from.\n */\n centerStyle: CenterConfig\n /** What the labels say, or `\"none\"` to drop them entirely. */\n labelMode: PieLabelMode\n /** Where the labels sit: inside their wedge, outside on a leader, or nowhere. */\n labelPlacement: PieLabelPlacement\n /**\n * Sweep of the ring in `[0, 1]`: 0 draws nothing, 1 the full circle. The\n * wedges grow around clockwise from the ring's `startAngle` as this rises.\n */\n growth: number\n /**\n * Fade of the leader lines and labels in `[0, 1]`, held separate from\n * {@link growth} so they arrive *after* the ring has swept in.\n */\n labelReveal: number\n /**\n * How far each slice is currently pushed out of the ring, `0`–`1` per index.\n * Owned by the chart, so the ring and the legend read one signal.\n */\n pops: number[]\n /** Opacity everything *unselected* falls back to while any slice is selected. */\n dimOpacity: number\n /**\n * Text set in the middle of the ring's hole — the largest uninterrupted area\n * the chart owns, and on a donut otherwise empty. A one-line summary there\n * routinely does more work than the whole ring around it. Empty (the default)\n * leaves the hole clear; ignored when there is no hole.\n */\n centerLabel: string\n /** Smaller second line under {@link centerLabel}. Empty (the default) drops it. */\n centerCaption: string\n /**\n * Fade of the hole's summary in `[0, 1]`. Its own signal, untouched by the\n * ring's entrance — a hole label is usually a *conclusion*, so a scene that\n * wants it to land on a later beat sets this to 0 and calls\n * `PieChart.revealCenter` when it gets there.\n */\n centerReveal: number\n}\n\n/** Vertical breathing room between one label block and the next, in px. */\nconst LABEL_GAP = 12\n/** Breathing room past the outermost label, so it never touches the node's edge, in px. */\nconst LABEL_PAD = 14\n/** Rough width of one glyph as a fraction of the font size, for gutter sizing. */\nconst GLYPH_RATIO = 0.62\n/** Gap between a wedge's outer edge and where its leader lifts off, in px. */\nconst LEADER_LIFTOFF = 6\n/** How far past the ring's outer edge a leader runs before it elbows, in px. */\nconst LEADER_LENGTH = 34\n/** Horizontal run from the elbow to the label, in px. */\nconst LABEL_STUB = 26\n/** Gap between a leader's end and the label text it points at, in px. */\nconst TEXT_INSET = 8\n/**\n * Floor on the elbow's horizontal distance from the ring's axis, as a fraction\n * of the leader radius — so a label pushed clear above (or below) the ring still\n * elbows out sideways rather than running straight down the 12 o'clock line.\n */\nconst MIN_ELBOW = 0.18\n/** Segments used to tessellate a full turn of arc; a wedge takes its share. */\nconst ARC_STEPS = 240\n/** Rounding applied to the leader's elbow, in px. */\nconst ELBOW_RADIUS = 8\n/** Side padding used when nothing is leadered out — no gutter to reserve. */\nconst BARE_PAD = 2\n/** An inside label's caption size, as a fraction of its leading line. */\nconst INSIDE_CAPTION_RATIO = 0.44\n/**\n * Where an inside label sits on a **solid pie** (no hole to split the band\n * with), as a fraction of the wedge's radius — far enough out that the label\n * clears the point every wedge converges on.\n */\nconst PIE_LABEL_RADIUS = 0.62\n/** Fraction of the hole's diameter its summary is allowed to run to. */\nconst CENTER_LABEL_FIT = 0.78\n/** Fraction of the room inside a wedge that its label is allowed to run to. */\nconst INSIDE_LABEL_FIT = 0.9\n/** Below this, a wedge's bisector counts as axis-aligned and that term stops binding. */\nconst AXIS_EPSILON = 1e-3\n/** Fallback type size when a theme style leaves it unset or on autofit. */\nconst FALLBACK_SIZE = 26\n\n/** A style's numeric size, guarding the autofit and unset cases. */\nfunction sizeOf(style: TextStyle, fallback = FALLBACK_SIZE): number {\n const size = style.fontSize\n return typeof size === \"number\" ? size : fallback\n}\n\n/** Where one slice's label sits and the polyline that points at it. */\ninterface LabelPlacement {\n /** Index of the slice. */\n index: number\n /** Which side of the ring the label sits on: +1 right, −1 left. */\n side: 1 | -1\n /** Leader polyline: wedge edge → elbow → label. */\n leader: Vector2[]\n /** Point the label's inner edge is pinned to. */\n text: Vector2\n}\n\n/**\n * A ring-band wedge over `a0`→`a1` (radians, clockwise from 12 o'clock) spanning\n * `rInner`→`rOuter`, centred on `cx`. Ring wedges trace the outer arc, step in,\n * and trace the inner arc back; full pie wedges (`rInner <= 0`) close through the\n * centre.\n *\n * `halfGap` is **half the gap to the neighbouring slice, in px**, shaved off both\n * ends of the wedge. It's converted to an angle *per radius* (`halfGap / r` — a\n * wider angle near the hole than out at the rim), which is what keeps the channel\n * between two slices the same width all the way across the band. A fixed angular\n * gap instead reads as a thin pizza slice: right at the rim, pinched shut at the\n * hole. The inset is capped at the wedge's own half-span so a sliver of a slice\n * narrows to nothing rather than turning inside out.\n */\nfunction wedgePath(\n a0: number,\n a1: number,\n rInner: number,\n rOuter: number,\n halfGap: number,\n cx: number\n): PathBuilder {\n const path = new PathBuilder()\n const span = a1 - a0\n const steps = Math.max(\n 2,\n Math.ceil((Math.abs(span) / (Math.PI * 2)) * ARC_STEPS)\n )\n /** Angular inset at radius `r` that puts the wedge's edge `halfGap` px off the divide. */\n const insetAt = (r: number): number =>\n r <= 0 ? 0 : Math.min(halfGap / r, span / 2.2)\n const arcTo = (r: number, i: number): Vector2 => {\n const inset = insetAt(r)\n const from = a0 + inset\n const to = a1 - inset\n const point = ringPoint(r, from + ((to - from) * i) / steps)\n return { x: point.x + cx, y: point.y }\n }\n\n if (rInner <= 0) {\n // Solid pie wedge: the sides converge on the centre, so only the outer rim\n // carries the gap.\n path.moveTo(cx, 0)\n for (let i = 0; i <= steps; i++) {\n const p = arcTo(rOuter, i)\n path.lineTo(p.x, p.y)\n }\n path.close()\n return path\n }\n\n for (let i = 0; i <= steps; i++) {\n const p = arcTo(rOuter, i)\n if (i === 0) path.moveTo(p.x, p.y)\n else path.lineTo(p.x, p.y)\n }\n // Back along the inner arc — its own (wider) inset makes each side edge a\n // straight chord sitting `halfGap` px off the divide at both ends.\n for (let i = steps; i >= 0; i--) {\n const p = arcTo(rInner, i)\n path.lineTo(p.x, p.y)\n }\n path.close()\n return path\n}\n\n/** What the ring's geometry reads off its entries. */\ninterface RingState {\n readonly slices: PieSlice[]\n readonly ring: RingConfig\n readonly label: LabelConfig\n readonly centerStyle: CenterConfig\n readonly labelMode: PieLabelMode\n readonly labelPlacement: PieLabelPlacement\n readonly pops: readonly number[]\n readonly dimOpacity: number\n readonly centerLabel: string\n readonly centerCaption: string\n}\n\n/**\n * The ring's geometry for one box: every radius, gutter and label position,\n * derived from the entries and the size the ring was laid out at, and asked\n * afresh wherever it is read — the drawing, and the labels' bound positions\n * and sizes alike, so the two cannot disagree.\n */\nexport class RingGeometry {\n constructor(\n private readonly s: RingState,\n private readonly box: { readonly width: number; readonly height: number }\n ) { }\n\n /** The drawn slices' angular spans, re-read every frame so a value tween re-cuts them. */\n get arcs(): PieArc[] {\n return pieArcs(this.s.slices, this.s.ring.startAngle)\n }\n\n /** Whether any slice is labelled on a leader — the only thing that costs a gutter. */\n get hasLeaderLabels(): boolean {\n return this.s.labelPlacement === \"leader\" && this.s.labelMode !== \"none\" && this.arcs.length > 0\n }\n\n // ---- Label sizing ------------------------------------------------------\n\n /**\n * How wide a horizontal label centred in slice `arc`'s band can run before it\n * leaves the wedge, in px.\n *\n * Two things bound it, and which one binds depends entirely on where the wedge\n * sits. Stepping sideways from the label's centre moves you *radially* by\n * `dx·sin(mid)` and *tangentially* by `dx·cos(mid)`, so a wedge at 3 o'clock is\n * limited by the band's thickness (its label runs straight across the band)\n * while a wedge at 12 o'clock is limited by its own arc (its label runs along\n * the band, which is much roomier). Taking the smaller of the two is what stops\n * a label overflowing the ring on one chart and being pointlessly shrunk on the\n * next.\n *\n * Measured off the *unpopped* radius, so a slice growing out of the ring\n * doesn't resize its own text mid-animation.\n */\n insideRoom(arc: PieArc): number {\n const outer = this.outerRadius\n const hole = this.holeRadius\n const band = hole > 0 ? outer - hole : outer * (1 - PIE_LABEL_RADIUS) * 2\n const mid = hole > 0 ? (hole + outer) / 2 : outer * PIE_LABEL_RADIUS\n const sin = Math.abs(Math.sin(arc.mid))\n const cos = Math.abs(Math.cos(arc.mid))\n const acrossBand = sin > AXIS_EPSILON ? band / sin : Infinity\n const alongArc = cos > AXIS_EPSILON ? ((arc.a1 - arc.a0) * mid) / cos : Infinity\n return Math.max(0, Math.min(acrossBand, alongArc))\n }\n\n /** Widest line, in glyphs at the leading line's size, an inside label runs to. */\n insideGlyphs(arc: PieArc): number {\n const mode = this.s.labelMode\n const category = this.s.slices[arc.index]?.category.length ?? 0\n const percent = mode === \"both\" || mode === \"percent\" ? formatPercent(arc.fraction).length : 0\n // The caption is set at a fraction of the leading line, except when it *is*\n // the leading line.\n const caption = mode === \"category\" ? category : mode === \"both\" ? category * INSIDE_CAPTION_RATIO : 0\n return Math.max(1, percent, caption)\n }\n\n /**\n * Font size of an inside label's leading line: what the theme asked for,\n * shrunk if that wouldn't fit inside its wedge. **One size across every inside\n * label** — the smallest any of them needs — because labels at mismatched\n * sizes read as a mistake rather than as emphasis.\n *\n * So the theme's `insideStyle.fontSize` is an upper bound, not a promise: ask\n * for the size the chart wants and the ring keeps it legal.\n */\n get insideFont(): number {\n let size = sizeOf(this.s.label.insideStyle, FALLBACK_SIZE * 1.9)\n for (const arc of this.arcs) {\n const fits = (this.insideRoom(arc) * INSIDE_LABEL_FIT) / (this.insideGlyphs(arc) * GLYPH_RATIO)\n size = Math.min(size, fits)\n }\n return Math.max(1, size)\n }\n\n /**\n * Font size of the hole's summary: the configured size, capped so it still\n * fits across the hole. The cap matters because the hole's radius derives from\n * the box, so a chart that reflows smaller would otherwise push its summary out\n * through the ring.\n */\n get centerFont(): number {\n const { centerStyle, centerLabel, centerCaption } = this.s\n const wanted = sizeOf(centerStyle.labelStyle, 46)\n const hole = this.holeRadius\n if (hole <= 0) return wanted\n const captionRatio = sizeOf(centerStyle.captionStyle, 22) / Math.max(1, wanted)\n // Both lines have to clear the hole, and the caption is set at a fraction of\n // this size — so what binds is whichever is wider once drawn, not whichever\n // has more characters.\n const glyphs = Math.max(1, centerLabel.length, centerCaption.length * captionRatio)\n return Math.min(wanted, (hole * 2 * CENTER_LABEL_FIT) / (glyphs * GLYPH_RATIO))\n }\n\n // ---- Ring geometry, all derived from the box ---------------------------\n\n /** Rim radius of a fully selected slice as a multiple of the ring's, clamped sane. */\n get growFactor(): number {\n return Math.max(1, Math.min(1.6, this.s.ring.selectedGrow))\n }\n\n /** How far past the ring's rim a fully selected slice reaches, in px. */\n get maxPop(): number {\n return this.outerRadius * (this.growFactor - 1)\n }\n\n /** Height of one **leader** label block, in px — the unit those columns are spaced by. */\n get labelRowHeight(): number {\n // Inside labels are stacked in the band, not in a column beside the ring, so\n // they cost no vertical reserve.\n if (!this.hasLeaderLabels) return 0\n const caption = sizeOf(this.s.label.textStyle) * 1.25\n const percent = sizeOf(this.s.label.percentStyle) * 1.25\n if (this.s.labelMode === \"both\") return caption + percent + 2\n return this.s.labelMode === \"category\" ? caption : percent\n }\n\n /** Widest line, in glyphs, that a leader label for slice `index` can run to. */\n captionGlyphs(index: number): number {\n const mode = this.s.labelMode\n // \"100%\" is the widest a percentage gets; captions are their own length.\n const percent = mode === \"percent\" || mode === \"both\" ? 4 : 0\n const category = mode === \"category\" || mode === \"both\" ? (this.s.slices[index]?.category.length ?? 0) : 0\n return Math.max(percent, category)\n }\n\n /**\n * Horizontal room reserved on each side for the leaders and their labels, in\n * px — measured **per side**, from the slices that actually leader out that\n * way. A ring whose labels are all inside reserves nothing and draws bigger.\n *\n * Excludes the ring's own selection-growth allowance, which scales with the\n * radius and so is divided out in {@link outerRadius} instead.\n */\n get labelGutters(): { left: number; right: number } {\n const gutters = { left: BARE_PAD, right: BARE_PAD }\n if (!this.hasLeaderLabels) return gutters\n const glyph = sizeOf(this.s.label.textStyle) * GLYPH_RATIO\n for (const arc of this.arcs) {\n const room = LEADER_LENGTH + LABEL_STUB + TEXT_INSET + this.captionGlyphs(arc.index) * glyph + LABEL_PAD\n const key = sideOf(arc.mid) > 0 ? \"right\" : \"left\"\n gutters[key] = Math.max(gutters[key], room)\n }\n return gutters\n }\n\n /**\n * How far the ring's centre sits from the node's, in px. Zero while the\n * gutters match; when the labels all fall one way it slides the ring into the\n * empty half so the ring-plus-labels group stays centred in the box instead of\n * the ring sitting dead centre with a bald patch beside it.\n */\n get centerX(): number {\n const { left, right } = this.labelGutters\n return (left - right) / 2\n }\n\n /**\n * Radius of the ring's outer edge, fitted to whatever the label gutters leave.\n * A fully selected slice reaches `radius × growFactor`, so the room has to\n * cover that — hence the divisor, which keeps even the grown slice inside the\n * box (and holds the ring the same size whether or not anything is selected).\n */\n get outerRadius(): number {\n const { left, right } = this.labelGutters\n const room = Math.min((this.box.width - left - right) / 2, this.box.height / 2 - this.labelRowHeight - 4)\n return Math.max(0, room / this.growFactor)\n }\n\n /** Radius of the hole in the middle: a fraction of the outer radius, or explicit px. */\n get holeRadius(): number {\n const configured = this.s.ring.holeRadius\n if (configured <= 0) return 0\n const px = configured < 1 ? configured * this.outerRadius : configured\n return Math.min(px, this.outerRadius * 0.95)\n }\n\n /** Radius of the circle every leader elbows on, clear of even a popped-out slice. */\n get elbowRadius(): number {\n return this.outerRadius + this.maxPop + LEADER_LENGTH\n }\n\n /** How far slice `index` is currently pushed out, in `[0, 1]`. */\n popOf(index: number): number {\n return clampUnit(this.s.pops[index] ?? 0)\n }\n\n /** Radius of slice `index`'s outer edge, its selection growth included. */\n anchorRadius(index: number): number {\n return this.outerRadius + this.maxPop * this.popOf(index)\n }\n\n /**\n * Radius an inside label sits at on slice `index`: halfway across the band on a\n * donut, and further out than halfway on a solid pie, where \"halfway\" would\n * crowd the point every wedge converges on. Rides the slice's own pop, so an\n * inside label travels out with the wedge it belongs to.\n */\n bandMidRadius(index: number): number {\n const rim = this.anchorRadius(index)\n const hole = this.holeRadius\n return hole > 0 ? (hole + rim) / 2 : rim * PIE_LABEL_RADIUS\n }\n\n /** The point slice `index`'s inside label is centred on. */\n insideAnchor(index: number): Vector2 {\n const arc = this.arcs.find((a) => a.index === index)\n if (arc === undefined) return { x: this.centerX, y: 0 }\n const point = ringPoint(this.bandMidRadius(index), arc.mid)\n return { x: point.x + this.centerX, y: point.y }\n }\n\n /**\n * How selected the ring is overall, in `[0, 1]` — the most-grown slice. Rides\n * the same tween the pops do, so the dim below arrives with the growth rather\n * than needing an animation of its own.\n */\n get selectionStrength(): number {\n let strongest = 0\n for (let i = 0; i < this.s.slices.length; i++) strongest = Math.max(strongest, this.popOf(i))\n return strongest\n }\n\n /**\n * Opacity slice `index` (and its leader and label) is drawn at: full while\n * nothing is selected, falling toward `dimOpacity` in proportion to how\n * selected the *rest* of the ring is. A slice that's selected itself stays at\n * full — the two factors cancel.\n */\n sliceOpacity(index: number): number {\n const focus = this.selectionStrength\n if (focus <= 0) return 1\n return 1 - (1 - clampUnit(this.s.dimOpacity)) * focus * (1 - this.popOf(index))\n }\n\n /** Slice `index`'s share of the ring as its label reads it, off the live values. */\n percentText(index: number): string {\n const arc = this.arcs.find((a) => a.index === index)\n return formatPercent(arc?.fraction ?? 0)\n }\n\n // ---- Label placement ---------------------------------------------------\n\n /**\n * Where every leader label sits and how its leader gets there. Labels are split\n * into a right and a left column by their slice's bisector, each column is\n * spread so nothing overlaps (and nothing leaves the box), and each leader is\n * then built as wedge edge → elbow → label. The elbow rides the leader circle at\n * the label's *settled* y, which keeps the final run horizontal and the whole\n * polyline outside the ring however far the label was nudged.\n */\n placements(): LabelPlacement[] {\n if (!this.hasLeaderLabels) return []\n const arcs = this.arcs\n if (arcs.length === 0 || this.outerRadius <= 0) return []\n\n const radius = this.elbowRadius\n const rowHeight = this.labelRowHeight\n const top = this.box.height / 2 - rowHeight / 2\n const bottom = -top\n const cx = this.centerX\n\n interface Slot {\n index: number\n side: 1 | -1\n mid: number\n ideal: number\n y: number\n }\n const right: Slot[] = []\n const left: Slot[] = []\n for (const arc of arcs) {\n const side = sideOf(arc.mid)\n const ideal = ringPoint(radius, arc.mid).y\n ;(side > 0 ? right : left).push({ index: arc.index, side, mid: arc.mid, ideal, y: ideal })\n }\n relaxLabels(right, rowHeight + LABEL_GAP, top, bottom)\n relaxLabels(left, rowHeight + LABEL_GAP, top, bottom)\n\n return [...right, ...left].map((slot) => {\n const start = ringPoint(this.anchorRadius(slot.index) + LEADER_LIFTOFF, slot.mid)\n // The elbow sits where the label's y crosses the leader circle. Solving\n // x² + y² = radius² for x keeps it *on* that circle (never inside the\n // ring); a label pushed past the circle's top or bottom falls back to the\n // sideways floor so its leader still reads as pointing outward.\n const onCircle = Math.sqrt(Math.max(0, radius * radius - slot.y * slot.y))\n const elbowX = cx + slot.side * Math.max(onCircle, radius * MIN_ELBOW)\n const endX = cx + slot.side * (radius + LABEL_STUB)\n\n return {\n index: slot.index,\n side: slot.side,\n leader: [\n { x: start.x + cx, y: start.y },\n { x: elbowX, y: slot.y },\n { x: endX, y: slot.y },\n ],\n text: { x: endX + slot.side * TEXT_INSET, y: slot.y },\n }\n })\n }\n\n /** This frame's placement for slice `index`, or `undefined` if it isn't leadered. */\n placementFor(index: number): LabelPlacement | undefined {\n return this.placements().find((placement) => placement.index === index)\n }\n}\n\n/** Which side a leader off bisector `mid` runs to: +1 right, −1 left. */\nfunction sideOf(mid: number): 1 | -1 {\n return Math.sin(mid) >= 0 ? 1 : -1\n}\n\nconst NO_BOX = { width: 0, height: 0 }\n\n/**\n * Each slice's fill, resolved once per paint rather than every frame: the slices\n * are bound to the chart's rows and palette, so a theme tween hands over new\n * paint and the drawing must see it, but a frame that moves nothing must not\n * re-resolve. Keyed by the paint itself, so it is shared and needs no node.\n */\nconst resolvedFills = new WeakMap<object, FillResolved[]>()\n\nfunction resolvedFill(fill: Fill): FillResolved[] {\n if (typeof fill !== \"object\" || fill === null) return fillOps.resolve(fill)\n let resolved = resolvedFills.get(fill)\n if (resolved === undefined) resolvedFills.set(fill, resolved = fillOps.resolve(fill))\n return resolved\n}\n\n/** Each slice's fill, resolved. */\nexport function ringFills(slices: readonly PieSlice[]): FillResolved[][] {\n return slices.map((slice) => resolvedFill(slice.fill))\n}\n\n/**\n * The labels a ring was built with: one per *drawn* slice (a zero-valued slice\n * gets no wedge, so no label either), in whichever of the two forms the chart\n * asked for, then the hole's summary. Both are real Text stacked in a hug\n * vertical flow — the difference is only what they are pinned to and which line\n * leads. The point each is pinned to stays reactive and follows the ring as the\n * box resizes or its slice pops out; only the choice of form, which depends on\n * entries alone, is settled here.\n */\nfunction ringLabels(s: RingState & { readonly labelReveal: number; readonly centerReveal: number }) {\n const blocks: Node2D[] = []\n // A label block's parent is the ring: its box is what the ring was laid out at.\n const geometry = () => new RingGeometry(s, (blocks[0]?.parent as Node2D | null | undefined)?.layoutBounds ?? NO_BOX)\n\n const label = (arc: PieArc): Node2D => {\n // Fades in with the ring's labels, then dims along with its own slice\n // whenever another slice takes the focus.\n const opacity = () => clampUnit(s.labelReveal) * geometry().sliceOpacity(arc.index)\n const wantsCategory = s.labelMode === \"both\" || s.labelMode === \"category\"\n const wantsPercent = s.labelMode === \"both\" || s.labelMode === \"percent\"\n\n if (s.labelPlacement === \"inside\") {\n // The number leads and the caption sits under it: inside a wedge the\n // reader already knows *which* wedge they are looking at, so the magnitude\n // is what the label is for. Both sizes are bound rather than read once —\n // the fitted size derives from the ring's radius, which isn't known until\n // the node has been laid out.\n const inside: Node2D[] = []\n if (wantsPercent) {\n inside.push(new Text({\n ...textOps.styleProps(s.label.insideStyle),\n text: () => geometry().percentText(arc.index),\n fontSize: () => geometry().insideFont,\n }))\n }\n if (wantsCategory) {\n inside.push(new Text({\n ...textOps.styleProps(s.label.insideStyle),\n text: () => s.slices[arc.index]?.category ?? \"\",\n fontSize: () => (s.labelMode === \"category\" ? geometry().insideFont : geometry().insideFont * INSIDE_CAPTION_RATIO),\n }))\n }\n return new Rect({\n flow: \"vertical\",\n width: \"hug\",\n height: \"hug\",\n gap: 0,\n align: \"center\",\n opacity,\n center: () => geometry().insideAnchor(arc.index),\n children: inside,\n })\n }\n\n const side = sideOf(arc.mid)\n const at = () => geometry().placementFor(arc.index)?.text ?? { x: 0, y: 0 }\n const children: Node2D[] = []\n if (wantsCategory) {\n children.push(new Text({\n ...textOps.styleProps(s.label.textStyle),\n text: () => s.slices[arc.index]?.category ?? \"\",\n textAlign: side > 0 ? \"left\" : \"right\",\n }))\n }\n if (wantsPercent) {\n children.push(new Text({\n ...textOps.styleProps(s.label.percentStyle),\n text: () => geometry().percentText(arc.index),\n textAlign: side > 0 ? \"left\" : \"right\",\n }))\n }\n return new Rect({\n flow: \"vertical\",\n width: \"hug\",\n height: \"hug\",\n gap: 2,\n align: side > 0 ? \"centerLeft\" : \"centerRight\",\n opacity,\n // Exactly one of these is set; `undefined` anchors are ignored.\n centerLeft: side > 0 ? at : undefined,\n centerRight: side > 0 ? undefined : at,\n children,\n })\n }\n\n if (s.labelPlacement !== \"none\" && s.labelMode !== \"none\") {\n for (const arc of pieArcs(s.slices, s.ring.startAngle)) blocks.push(label(arc))\n }\n\n // The hole's summary, pinned to the ring's centre so it tracks the sideways\n // shift a one-sided label column puts on the ring.\n if (s.centerLabel !== \"\" || s.centerCaption !== \"\") {\n const children: Node2D[] = []\n if (s.centerLabel !== \"\") {\n children.push(new Text({\n ...textOps.styleProps(s.centerStyle.labelStyle),\n text: s.centerLabel,\n fontSize: () => geometry().centerFont,\n }))\n }\n if (s.centerCaption !== \"\") {\n children.push(new Text({\n ...textOps.styleProps(s.centerStyle.captionStyle),\n text: s.centerCaption,\n fontSize: () =>\n geometry().centerFont * (sizeOf(s.centerStyle.captionStyle, 22) / sizeOf(s.centerStyle.labelStyle, 46)),\n }))\n }\n blocks.push(new Rect({\n flow: \"vertical\",\n width: \"hug\",\n height: \"hug\",\n gap: 2,\n align: \"center\",\n opacity: () => clampUnit(s.centerReveal),\n center: () => ({ x: geometry().centerX, y: 0 }),\n children,\n }))\n }\n return asParts(blocks)\n}\n\n/**\n * The ring itself: one wedge per {@link PieSlice}, sized by its share of the\n * total, swept clockwise from the theme's `startAngle` with a `gap` of clear\n * space between neighbours — a *constant-width* channel, the same at the hole as\n * at the rim (see {@link wedgePath}). A **selected** slice reaches further out\n * while its hole edge stays put — so it reads as that band swelling out of the\n * ring rather than breaking off it — and the rest of the ring dims behind it,\n * leaders and labels included.\n *\n * This is the only part of the chart family that draws a path: the wedges are\n * filled {@link PathBuilder} polygons approximating each arc, and the leaders are\n * stroked polylines — both in the node's centred, y-up space, so neither depends\n * on SVG arc-sweep conventions. The **labels are real {@link Text} nodes**\n * (stacked in a hug vertical flow and pinned by their inner edge), so no\n * glyph-width guesswork decides where a caption starts.\n *\n * ### Label placement\n * Each label sits either **inside** its wedge or **outside** on a leader.\n *\n * An inside label is centred on its slice's band and leads with the number, the\n * caption set small underneath — inside a wedge the reader already knows which\n * slice they're on, so the magnitude is the thing being read. It costs no gutter\n * and no eye round-trip, so a ring labelled that way draws considerably bigger in\n * the same box.\n *\n * A leader label sits outside on the end of a polyline that lifts off its wedge,\n * elbows, and runs horizontally into the text. Labels are split into a left and a\n * right column by which way their slice's bisector points, and each column is\n * de-conflicted independently (see {@link relaxLabels}): a label takes the spot\n * nearest its wedge that still clears its neighbour, and the column is squeezed\n * to fit the node's height rather than spilling out of it. The elbow then rides on\n * a circle just outside the ring **at the label's settled y**, so the run into the\n * text is always horizontal and a nudged label never drags its leader back across\n * the ring.\n *\n * Only the sides that actually carry a leader reserve a gutter, and the ring's\n * centre slides into whatever the other side left over — so a ring whose slices\n * all leader one way doesn't leave a bald patch on the other.\n */\nexport const PieRing = component2d({\n key: \"pieRing\",\n state: {\n slices: fresh<PieSlice[]>(() => []),\n ring: Types.custom<RingConfig>(DEFAULT_PIE_CHART_THEME.ring),\n label: Types.custom<LabelConfig>(DEFAULT_PIE_CHART_THEME.label),\n centerStyle: Types.custom<CenterConfig>(DEFAULT_PIE_CHART_THEME.center),\n labelMode: Types.custom<PieLabelMode>(\"percent\"),\n labelPlacement: Types.custom<PieLabelPlacement>(\"leader\"),\n growth: 1,\n labelReveal: 1,\n pops: fresh<number[]>(() => []),\n dimOpacity: DEFAULT_PIE_CHART_THEME.spotlight.dimOpacity,\n centerLabel: \"\",\n centerCaption: \"\",\n centerReveal: 1,\n },\n // A plain node filled its parent; one made of its labels would hug them.\n defaults: { width: \"fill\", height: \"fill\" },\n children: (state) => ringLabels(state),\n // The ring's paints live nowhere a paint entry would look: one fill per slice,\n // the wedge outline and drop shadow of `ring`, and the leader line's own stroke.\n load: (loader, state) =>\n loadPaints(loader, {\n fills: ringFills(state.slices),\n shadows: [shadowOps.resolve(state.ring.shadow)],\n strokes: [strokeOps.resolve(state.ring.stroke), strokeOps.resolve(state.label.leaderStroke)],\n }),\n commands: {\n /**\n * Tween `growth` `0 → 1`, wiping the wedges in around the ring. No priming\n * to 0 first — the value at `t` is a function of `t`, so a repeat call\n * re-sweeps by definition rather than by having reset something.\n */\n sweep: (timeline) => {\n const ease = frameEase(timeline)\n return timeline.animate((t) => ({ growth: ease(t) }))\n },\n /** Tween `labelReveal` `0 → 1`, fading the leaders and captions up. */\n revealLabels: (timeline) => {\n const ease = frameEase(timeline)\n return timeline.animate((t) => ({ labelReveal: ease(t) }))\n },\n /**\n * Fade the hole's summary up. Deliberately not part of the ring's entrance: a\n * hole label is usually the chart's conclusion, so the scene decides when it\n * lands.\n */\n revealCenter: (timeline, state) => {\n const ease = frameEase(timeline)\n const from = clampUnit(state.centerReveal)\n return timeline.animate((t) => ({ centerReveal: from + (1 - from) * ease(t) }))\n },\n },\n render: (ctx, state) => {\n const g = new RingGeometry(state, ctx.size)\n const rOuter = g.outerRadius\n const arcs = rOuter > 0 ? g.arcs : []\n if (arcs.length > 0) {\n const sweep = clampUnit(state.growth)\n const revealEnd = state.ring.startAngle * (Math.PI / 180) + Math.PI * 2 * sweep\n const hole = g.holeRadius\n const halfGap = Math.max(0, state.ring.gap) / 2\n\n // One shared frame (the whole ring square, grown slices included) pinned onto\n // every wedge path, so `Graphics2D.path()` doesn't re-centre each wedge on its\n // own bbox and collapse them all onto the origin. It stays symmetric about the\n // *node's* origin — so path coordinates land exactly in node space — and is\n // grown by the ring's sideways offset rather than moved with it, which would\n // undo the very shift it has to contain.\n const cx = g.centerX\n const half = rOuter + g.maxPop + 1 + Math.abs(cx)\n const centerBounds: [number, number, number, number] = [-half, -half, half, half]\n // Explicit width/height alongside centerBounds: a wedge path's command list\n // carries no implied extent, so a slice filled with an image or a video needs\n // this to size its decode.\n const span = half * 2\n\n const fills = ringFills(state.slices)\n for (const arc of arcs) {\n // Clip the wedge to the growth-revealed arc; skip it until the sweep\n // reaches it, so the ring draws in as one clockwise wipe.\n const a1 = Math.min(arc.a1, revealEnd)\n if (a1 <= arc.a0) continue\n\n const path = wedgePath(arc.a0, a1, hole, g.anchorRadius(arc.index), halfGap, cx)\n const wedge = new Graphics2D().path(path.toPathState({ centerBounds, width: span, height: span }))\n wedge.shadow(state.ring.shadow)\n wedge.fill(fills[arc.index] ?? [])\n wedge.stroke(state.ring.stroke)\n ctx.draw(wedge.opacity(g.sliceOpacity(arc.index)))\n }\n\n // Leaders. Each polyline gets its own `Graphics2D`: several open polylines\n // chained into one command list are combined into a single surface before the\n // stroke, which closes each contour back on itself and paints the leaders as\n // thin triangles.\n const reveal = clampUnit(state.labelReveal)\n if (reveal > 0.001) {\n for (const placement of g.placements()) {\n ctx.draw(\n new Graphics2D()\n .line({ points: placement.leader, radius: ELBOW_RADIUS })\n .stroke(state.label.leaderStroke)\n .opacity(reveal * g.sliceOpacity(placement.index))\n )\n }\n }\n }\n ctx.children()\n },\n})\n\nexport type PieRing = NodeOf<typeof PieRing>\n", "/**\n * Types and pure helpers specific to the {@link PieChart} family: how rows\n * become slices, how slices become arcs on a ring, where the labels around it\n * end up, and the theme all of that is drawn from.\n *\n * The one chart in the family that draws **one mark per row** rather than one\n * per column, which is why almost nothing about its geometry is shared: there is\n * no scale, no axis and no baseline, only a circle divided by share. What it\n * *does* share — the legend, the theme leaf resolvers, the palette, the\n * percentage format — comes from `../kit/shared.ts`.\n *\n * Kept renderer-agnostic (no `Node` imports) so a host's inspector mapping\n * can build a partial {@link PieChartTheme} without\n * pulling the node classes in.\n */\n\nimport {\n lerpNumber,\n shadowOps,\n strokeOps,\n textOps,\n type DataRecord,\n type Fill,\n type Shadow,\n type ShadowResolved,\n type Stroke,\n type StrokeResolved,\n type TextStyle,\n type Vector2,\n} from \"@motionscript/sdk\"\n\nimport {\n CHART_PALETTE,\n DEFAULT_LEGEND,\n DEFAULT_SERIES_FILLS,\n lerpLegendConfig,\n lerpSeriesFills,\n num,\n resolveLegendConfig,\n resolveSeriesFills,\n text,\n type LegendConfig,\n type LegendTheme,\n} from \"../kit/shared\"\n\n/**\n * One **resolved** slice of a {@link PieChart}: its caption, its magnitude, and\n * the paint its wedge and legend swatch take.\n *\n * This is what the ring and the legend consume, not what a scene authors: the\n * chart takes tabular `data` plus two field names and resolves it into these, so\n * the parts stay unaware of where a slice's caption, magnitude and paint came\n * from.\n */\nexport interface PieSlice {\n /** Legend caption and label text for this slice. */\n category: string\n /** This slice's magnitude. Its wedge covers `value / (sum of all values)`. */\n value: number\n /** Wedge and swatch paint. */\n fill: Fill\n}\n\n/**\n * Resolve tabular rows into slices: one per row, captioned by the `category`\n * column, sized by the `value` column, painted from `palette` by row order\n * (cycling when the palette is shorter than the data).\n *\n * Paint comes from the palette rather than from the rows because a pie draws one\n * mark per *row* — unlike the line and bar charts, where a colour belongs to a\n * series (a whole column) and rides on the series entry instead. Keeping it out\n * of the rows is what lets a pie be fed straight from a CSV of plain numbers.\n */\nexport function resolvePieSlices(\n data: DataRecord[],\n category: string,\n value: string,\n palette: Fill[]\n): PieSlice[] {\n return data.map((row, i) => ({\n category: text(row, category),\n value: num(row, value),\n fill:\n palette.length > 0 ? palette[i % palette.length] : (\"white\" as Fill),\n }))\n}\n\n/** What a {@link PieChart}'s labels say. */\nexport type PieLabelMode =\n /**\n * The category and its percentage, two lines. Their order flips with the\n * placement: a **leader** label reads category over percentage, an **inside**\n * label reads the percentage big over a small category caption — inside a\n * wedge the reader already knows which slice they're on, so the magnitude is\n * the thing being read.\n */\n | \"both\"\n /** The category only. */\n | \"category\"\n /** The percentage only — the pick when a legend already names the categories. */\n | \"percent\"\n /** No labels and no leader lines at all. */\n | \"none\"\n\n/**\n * Where a slice's label sits.\n *\n * `\"inside\"` drops the leader line entirely and centres the label in the wedge's\n * own band, which costs no gutter — so the ring draws bigger — and costs the eye\n * no round trip. `\"leader\"` is the classic treatment, and what a sliver too\n * narrow to hold text needs.\n */\nexport type PieLabelPlacement = \"inside\" | \"leader\" | \"none\"\n\n/** Degrees → radians. */\nexport const DEG = Math.PI / 180\n\n/** A slice's plotted value: negatives and non-finite values count as 0. */\nexport function sliceValue(slice: PieSlice): number {\n return slice.value > 0 && Number.isFinite(slice.value) ? slice.value : 0\n}\n\n/** Sum of every slice's plotted value — the denominator every share is taken against. */\nexport function sliceTotal(slices: PieSlice[]): number {\n let sum = 0\n for (const slice of slices) sum += sliceValue(slice)\n return sum\n}\n\n/**\n * The point at radius `r` and angle `a` on a ring centred on the origin, in the\n * node's centred, **y-up** space. Angles run **clockwise from 12 o'clock**:\n * `a = 0` points straight up (+y), and increasing `a` sweeps toward +x then −y.\n */\nexport function ringPoint(r: number, a: number): Vector2 {\n return { x: r * Math.sin(a), y: r * Math.cos(a) }\n}\n\n/** One slice's resolved angular span on the ring. */\nexport interface PieArc {\n /** Index of the slice in the source list. */\n index: number\n /** Start angle, radians clockwise from 12 o'clock. */\n a0: number\n /** End angle, radians clockwise from 12 o'clock. */\n a1: number\n /** Bisector angle — where this slice's label and pop-out point. */\n mid: number\n /** This slice's share of the total, in `[0, 1]`. */\n fraction: number\n}\n\n/**\n * Resolve slices into per-slice arcs: each covers exactly its share of the\n * circle, walking clockwise from `startDegrees` (0 = 12 o'clock), with the arcs\n * abutting. Zero/negative slices are skipped entirely; an all-zero list yields\n * no arcs.\n *\n * Arcs are the *undivided* shares — the gap between neighbours is not carved out\n * here, because a fixed angular gap reads as a wedge: wide at the rim, pinched\n * shut at the hole. It's applied when the wedge is drawn instead, as a\n * constant-width inset off both ends of every arc, so a slice still covers its\n * true percentage of the circle.\n */\nexport function pieArcs(slices: PieSlice[], startDegrees: number): PieArc[] {\n const total = sliceTotal(slices)\n if (total <= 0) return []\n\n const arcs: PieArc[] = []\n let cursor = startDegrees * DEG\n slices.forEach((slice, index) => {\n const value = sliceValue(slice)\n if (value <= 0) return\n const fraction = value / total\n const a0 = cursor\n const a1 = a0 + fraction * Math.PI * 2\n cursor = a1\n arcs.push({ index, a0, a1, mid: (a0 + a1) / 2, fraction })\n })\n return arcs\n}\n\n/** A label competing for vertical space in one of the two label columns. */\nexport interface LabelSlot {\n /** Where the label would sit if nothing else were in the way. */\n ideal: number\n /** Where it ends up once the column has been de-conflicted. Written by {@link relaxLabels}. */\n y: number\n}\n\n/**\n * Spread one column of labels so no two overlap, keeping each as close to its\n * `ideal` y as possible and the whole column inside `[bottom, top]`.\n *\n * Two passes, the standard treatment. Walking top-to-bottom, each label takes\n * the highest slot at or below its ideal that still clears the label above it by\n * `minGap`; if that pushes the tail past `bottom`, a reverse pass lifts it back\n * up. When there are more labels than the column can hold, the reverse pass wins\n * and the crowding is shared evenly rather than dumped off the bottom edge.\n *\n * Mutates each slot's `y` and returns `slots` re-ordered top-to-bottom.\n */\nexport function relaxLabels<T extends LabelSlot>(\n slots: T[],\n minGap: number,\n top: number,\n bottom: number\n): T[] {\n slots.sort((a, b) => b.ideal - a.ideal)\n\n let ceiling = top\n for (const slot of slots) {\n slot.y = Math.min(slot.ideal, ceiling)\n ceiling = slot.y - minGap\n }\n\n let floor = bottom\n for (let i = slots.length - 1; i >= 0; i--) {\n slots[i].y = Math.max(slots[i].y, floor)\n floor = slots[i].y + minGap\n }\n\n return slots\n}\n\n// ---- Theme ---------------------------------------------------------------\n\n/** The ring's geometry and paint — everything about the wedges but their colour. */\nexport interface RingConfig {\n /**\n * Width of the channel between adjacent slices, in px, held constant all the\n * way across the band — the same clearance at the hole as at the rim. (An\n * angular gap would splay open toward the rim instead.)\n */\n gap: number\n /**\n * Radius of the hole punched through the middle. Below 1 it reads as a\n * fraction of the ring's outer radius (`0.6` leaves a band 40% of the radius\n * thick); 1 and up as an explicit pixel radius; `0` fills the middle in,\n * drawing solid pie wedges.\n */\n holeRadius: number\n /** Angle in degrees where the first slice begins, clockwise from 12 o'clock. */\n startAngle: number\n /**\n * How far out a selected slice's rim reaches, as a **multiple of the ring's\n * outer radius**. Being relative, the emphasis scales with the chart instead\n * of shrinking on a big one and swamping a small one. Clamped to `[1, 1.6]`.\n */\n selectedGrow: number\n /** Optional outline on every wedge. */\n stroke: Stroke\n /** Optional drop shadow on every wedge. */\n shadow: Shadow\n}\n\n/** How the ring's labels are set, and the leader lines that point at them. */\nexport interface LabelConfig {\n /** The category caption on a label. */\n textStyle: TextStyle\n /** The percentage on a label. */\n percentStyle: TextStyle\n /**\n * Ink for a label set **inside** its wedge, both lines. Separate because an\n * inside label sits on the wedge's own paint rather than on the background, so\n * it usually wants the opposite end of the value range from everything else.\n */\n insideStyle: TextStyle\n /** The leader line running from a wedge out to its label. */\n leaderStroke: Stroke\n}\n\n/** The summary set in the ring's hole. */\nexport interface CenterConfig {\n labelStyle: TextStyle\n captionStyle: TextStyle\n}\n\n/** A {@link PieChart}'s full, resolved visual theme. */\nexport interface PieChartConfig {\n /** Wedge and swatch paint, cycled by **row** index (`i % length`). */\n sliceFills: Fill[]\n ring: RingConfig\n label: LabelConfig\n center: CenterConfig\n legend: LegendConfig\n /** How far everything outside the selection fades while any slice is selected. */\n spotlight: { dimOpacity: number }\n}\n\n/** Author-facing partial theme — every branch optional and deep-merged. */\nexport type PieChartTheme = {\n sliceFills?: Fill[]\n ring?: Partial<RingConfig>\n label?: Partial<LabelConfig>\n center?: Partial<CenterConfig>\n legend?: LegendTheme\n spotlight?: Partial<PieChartConfig[\"spotlight\"]>\n}\n\nconst LABEL_TEXT_STYLE: TextStyle = {\n fontSize: 26,\n fontWeight: 600,\n textAlign: \"center\",\n fill: CHART_PALETTE.markerColor,\n}\n\n/**\n * Opacity the other slices fade to under a selection. Fainter than the line and\n * bar charts' `SPOTLIGHT_DIM`: a ring's slices touch, so at that a selected\n * slice barely separates from its neighbours.\n */\nexport const PIE_SPOTLIGHT_DIM = 0.1\n\n/** The pie chart's out-of-the-box look. */\nexport const DEFAULT_PIE_CHART_THEME: PieChartConfig = {\n sliceFills: DEFAULT_SERIES_FILLS,\n ring: {\n gap: 6,\n // A donut rather than a solid pie by default: the hole is the largest\n // uninterrupted area the chart owns and the one place a conclusion can be\n // set. `0` here would give that away before anyone had asked for it.\n holeRadius: 0.6,\n startAngle: 0,\n selectedGrow: 1.08,\n stroke: [],\n shadow: [],\n },\n label: {\n textStyle: LABEL_TEXT_STYLE,\n percentStyle: { ...LABEL_TEXT_STYLE, fontWeight: 700 },\n // Inside labels sit on the wedge's paint, which is a mid-tone from the\n // palette — so they lead with the panel colour rather than the axis ink.\n insideStyle: { ...LABEL_TEXT_STYLE, fontWeight: 800, fill: \"#15130e\" },\n leaderStroke: { weight: 2, fill: CHART_PALETTE.markerColor },\n },\n center: {\n labelStyle: {\n fontSize: 46,\n fontWeight: 800,\n textAlign: \"center\",\n fill: CHART_PALETTE.markerColor,\n },\n captionStyle: {\n fontSize: 22,\n fontWeight: 500,\n textAlign: \"center\",\n fill: CHART_PALETTE.markerColor,\n },\n },\n legend: DEFAULT_LEGEND,\n spotlight: { dimOpacity: PIE_SPOTLIGHT_DIM },\n}\n\nfunction resolveRingConfig(\n ring: Partial<RingConfig> | undefined,\n previous: RingConfig\n): RingConfig {\n return {\n gap: ring?.gap ?? previous.gap,\n holeRadius: ring?.holeRadius ?? previous.holeRadius,\n startAngle: ring?.startAngle ?? previous.startAngle,\n selectedGrow: ring?.selectedGrow ?? previous.selectedGrow,\n stroke: strokeOps.resolve(ring?.stroke ?? previous.stroke),\n shadow: shadowOps.resolve(ring?.shadow ?? previous.shadow),\n }\n}\n\nfunction lerpRingConfig(\n from: RingConfig,\n to: RingConfig,\n t: number\n): RingConfig {\n return {\n gap: lerpNumber(from.gap, to.gap, t),\n holeRadius: lerpNumber(from.holeRadius, to.holeRadius, t),\n startAngle: lerpNumber(from.startAngle, to.startAngle, t),\n selectedGrow: lerpNumber(from.selectedGrow, to.selectedGrow, t),\n stroke: strokeOps.lerp(\n from.stroke as StrokeResolved[],\n to.stroke as StrokeResolved[],\n t\n ),\n shadow: shadowOps.lerp(\n from.shadow as ShadowResolved[],\n to.shadow as ShadowResolved[],\n t\n ),\n }\n}\n\nfunction resolveLabelConfig(\n label: Partial<LabelConfig> | undefined,\n previous: LabelConfig\n): LabelConfig {\n return {\n textStyle: textOps.resolveStyle(label?.textStyle, previous.textStyle),\n percentStyle: textOps.resolveStyle(\n label?.percentStyle,\n previous.percentStyle\n ),\n insideStyle: textOps.resolveStyle(label?.insideStyle, previous.insideStyle),\n leaderStroke: strokeOps.resolve(\n label?.leaderStroke ?? previous.leaderStroke\n ),\n }\n}\n\nfunction lerpLabelConfig(\n from: LabelConfig,\n to: LabelConfig,\n t: number\n): LabelConfig {\n return {\n textStyle: textOps.lerpStyle(from.textStyle, to.textStyle, t),\n percentStyle: textOps.lerpStyle(from.percentStyle, to.percentStyle, t),\n insideStyle: textOps.lerpStyle(from.insideStyle, to.insideStyle, t),\n leaderStroke: strokeOps.lerp(\n from.leaderStroke as StrokeResolved[],\n to.leaderStroke as StrokeResolved[],\n t\n ),\n }\n}\n\nfunction resolveCenterConfig(\n center: Partial<CenterConfig> | undefined,\n previous: CenterConfig\n): CenterConfig {\n return {\n labelStyle: textOps.resolveStyle(center?.labelStyle, previous.labelStyle),\n captionStyle: textOps.resolveStyle(\n center?.captionStyle,\n previous.captionStyle\n ),\n }\n}\n\nfunction lerpCenterConfig(\n from: CenterConfig,\n to: CenterConfig,\n t: number\n): CenterConfig {\n return {\n labelStyle: textOps.lerpStyle(from.labelStyle, to.labelStyle, t),\n captionStyle: textOps.lerpStyle(from.captionStyle, to.captionStyle, t),\n }\n}\n\n/**\n * {@link PieChart}'s `theme` property mapper: merges the author's partial\n * {@link PieChartTheme} onto `previous` (falling back to\n * {@link DEFAULT_PIE_CHART_THEME} on first resolution), fully resolving every\n * leaf so the tween below always has resolved data to interpolate.\n */\nexport function resolvePieChartTheme(\n theme: PieChartTheme | undefined,\n previous?: PieChartConfig\n): PieChartConfig {\n const base = previous ?? DEFAULT_PIE_CHART_THEME\n return {\n sliceFills: resolveSeriesFills(theme?.sliceFills, base.sliceFills),\n ring: resolveRingConfig(theme?.ring, base.ring),\n label: resolveLabelConfig(theme?.label, base.label),\n center: resolveCenterConfig(theme?.center, base.center),\n legend: resolveLegendConfig(theme?.legend, base.legend),\n spotlight: {\n dimOpacity: theme?.spotlight?.dimOpacity ?? base.spotlight.dimOpacity,\n },\n }\n}\n\n/** {@link PieChart}'s `theme` property tween — leaf by leaf. */\nexport function lerpPieChartTheme(\n from: PieChartConfig,\n to: PieChartConfig,\n t: number\n): PieChartConfig {\n return {\n // A palette is a list of independent paints rather than one value, so it\n // interpolates pairwise by index; one that grows or shrinks holds the longer\n // side's tail, exactly as the cartesian charts' palettes do.\n sliceFills: lerpSeriesFills(from.sliceFills, to.sliceFills, t),\n ring: lerpRingConfig(from.ring, to.ring, t),\n label: lerpLabelConfig(from.label, to.label, t),\n center: lerpCenterConfig(from.center, to.center, t),\n legend: lerpLegendConfig(from.legend, to.legend, t),\n spotlight: {\n dimOpacity: lerpNumber(\n from.spotlight.dimOpacity,\n to.spotlight.dimOpacity,\n t\n ),\n },\n }\n}\n", "import { LineChart } from \"./line-chart\";\nimport { BarChart } from \"./bar-chart\";\nimport { PieChart } from \"./pie-chart\";\n\n/**\n * Every node type this package publishes.\n *\n * A host registers these by handing the array to an engine —\n * `new Engine(platform, { nodes: NODES })` — which reads each component's key.\n * Defining a component only *declares* that key: nothing is registered by\n * importing a module, so a registry holds exactly what its owner asked for.\n *\n * Listing the component **values** is also what survives bundling: this package\n * declares `sideEffects: false`, and a document names a node type by string, so\n * an import kept only for its side effect is exactly what gets shaken out.\n */\n// Typed by name, so the declaration names each component rather than spelling out its type through core.\nexport const NODES: (typeof LineChart | typeof BarChart | typeof PieChart)[] = [LineChart, BarChart, PieChart];\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["Rect", "Types", "component2d", "easeOut", "formatNumber", "Graphics2D", "Types", "component2d", "strokeOps", "Fills", "fillOps", "lerpNumber", "strokeOps", "EMPTY_REGION_BOX", "hasRegion", "box", "resolveRegionBox", "region", "plot", "xScale", "yScale", "span", "range", "min", "max", "lo", "hi", "a", "b", "toPx", "v", "left", "right", "xBounded", "bottom", "top", "yBounded", "clipPolylineToBox", "points", "runs", "run", "i", "seg", "clipSegment", "samePoint", "dx", "dy", "t0", "t1", "edges", "p", "q", "r", "LINE_CHART_LEGEND", "DEFAULT_LEGEND", "DEFAULT_CHART_THEME", "DEFAULT_SERIES_STROKES", "DEFAULT_X_AXIS", "DEFAULT_Y_AXIS", "DEFAULT_PLOT_AREA", "CHART_PALETTE", "Fills", "SPOTLIGHT_DIM", "resolveRegionConfig", "previous", "strokeOps", "fillOps", "lerpRegionConfig", "from", "to", "t", "lerpNumber", "resolveChartTheme", "theme", "base", "resolveSeriesStrokes", "resolveAxisConfig", "resolvePlotArea", "resolveLegendConfig", "lerpChartTheme", "lerpSeriesStrokes", "lerpAxisConfig", "lerpPlotArea", "lerpLegendConfig", "seriesPoints", "data", "x", "series", "row", "num", "yExtent", "s", "resolveYScale", "divisions", "segments", "constantScale", "step", "niceStep", "value", "tracePoints", "pts", "p", "segLen", "total", "i", "d", "target", "out", "t", "ChartLine", "component2d", "fresh", "Types", "strokeOps", "EMPTY_REGION_BOX", "SPOTLIGHT_DIM", "loader", "state", "loadPaints", "timeline", "from", "to", "ease", "frameEase", "progress", "ctx", "drawn", "stroke", "radius", "box", "hasRegion", "Graphics2D", "run", "clipPolylineToBox", "Graphics2D", "Types", "component2d", "fillOps", "strokeOps", "ChartRegion", "component2d", "fresh", "EMPTY_REGION_BOX", "Types", "DEFAULT_CHART_THEME", "strokeOps", "fillOps", "loader", "state", "loadPaints", "ctx", "box", "hasRegion", "r", "width", "height", "cx", "cy", "Graphics2D", "rule", "points", "half", "x", "y", "Rect", "Text", "Types", "component2d", "insetsOps", "textOps", "SLIDE", "HorizontalAxis", "component2d", "fresh", "Types", "DEFAULT_X_AXIS", "arrivalsEntry", "state", "tickPad", "insetsOps", "titlePad", "column", "box", "sorted", "a", "b", "fracAt", "value", "min", "max", "markers", "_", "i", "marker", "Text", "textOps", "markerCaption", "width", "centred", "limit", "arrivalOf", "children", "Rect", "timeline", "stagger", "slide", "count", "captioned", "from", "keys", "arm", "armed", "step", "per", "caption", "enterBudget", "ease", "frameEase", "arrival", "label", "beats", "formatNumber", "DAY", "MONTHS", "NOMINAL_YEAR", "monthIndex", "word", "month", "isoWeekStart", "year", "week", "jan4", "weekday", "parseInstant", "raw", "s", "ymd", "day", "n", "named", "y", "dayNamed", "formatInstant", "value", "span", "date", "label", "isNumericColumn", "data", "field", "sawValue", "row", "v", "instantColumn", "parsed", "t", "last", "extentOf", "values", "lo", "hi", "sampledTicks", "positions", "min", "max", "divisions", "inside", "unique", "a", "b", "markerValues", "nearest", "target", "best", "out", "tick", "labelBySample", "captions", "fallback", "i", "scales", "resolveXScale", "pattern", "byKey", "key", "scale", "xScaleOf", "num", "formatMarker", "text", "instants", "extent", "range", "_", "SERIES_ARG", "Types", "REGION_ARG", "LEGEND_SLIDE", "DEFAULT_MARKERS", "AXES_SHARE", "LINES_SHARE", "LEGEND_SHARE", "columnOf", "s", "format", "resolveXScale", "drawnX", "domain", "column", "range", "drawnY", "divisions", "resolveYScale", "markerValues", "domainsDrawn", "lineChartDomains", "data", "xField", "xAxis", "yAxis", "series", "kept", "domains", "settleAxisDomain", "d", "seriesPixels", "index", "plot", "points", "seriesPoints", "w", "h", "xMin", "xMax", "yMin", "yMax", "xToPx", "x", "yToPx", "y", "regionBox", "resolveRegionBox", "hasCallout", "region", "regionArg", "span", "a", "b", "rescale", "ticks", "to", "axesAt", "from", "t", "lerpAxisDomain", "partsOf", "children", "frame", "cartesianViews", "hAxis", "HorizontalAxis", "VerticalAxis", "ChartLine", "ChartLegend", "clearing", "timeline", "state", "toggling", "show", "parts", "line", "LineChart", "component2d", "fresh", "resolveAxisDomain", "lerpAxisEntry", "DEFAULT_CHART_THEME", "resolveChartTheme", "lerpChartTheme", "theme", "plotSize", "laidOutSize", "enabled", "vAxis", "markers", "value", "formatMarker", "markerStep", "formatNumber", "lines", "i", "resolveSeriesStroke", "below", "resolveSeriesFill", "Rect", "ChartCanvas", "ChartRegion", "ChartBorder", "gutter", "cartesianFrame", "easeOut", "duration", "revealing", "body", "axes", "traced", "legend", "at0", "piece", "beats", "previous", "home", "_state", "SPOTLIGHT_DIM", "Rect", "Types", "component2d", "easeOut", "formatNumber", "Rect", "Text", "Types", "component2d", "insetsOps", "textOps", "SLIDE", "CategoryAxis", "component2d", "fresh", "Types", "DEFAULT_X_AXIS", "arrivalsEntry", "state", "tickPad", "insetsOps", "titlePad", "column", "width", "slotCenter", "index", "n", "slots", "caption", "i", "Rect", "clampUnit", "Text", "textOps", "arrivalOf", "children", "timeline", "stagger", "slide", "count", "captioned", "from", "keys", "arm", "armed", "step", "per", "enterBudget", "ease", "frameEase", "lastCaptionEnds", "captions", "_", "arrival", "label", "beats", "Graphics2D", "Text", "Types", "component2d", "fillOps", "shadowOps", "strokeOps", "textOps", "fillOps", "lerpNumber", "shadowOps", "strokeOps", "textOps", "barSpans", "row", "series", "stacked", "running", "s", "value", "num", "from", "stackCapIndex", "last", "i", "barCategories", "data", "field", "text", "barValueExtent", "lo", "hi", "span", "resolveBarScale", "range", "divisions", "segments", "step", "niceStep", "DEFAULT_BAR_CHART_THEME", "DEFAULT_SERIES_FILLS", "DEFAULT_X_AXIS", "DEFAULT_Y_AXIS", "DEFAULT_PLOT_AREA", "DEFAULT_LEGEND", "CHART_PALETTE", "SPOTLIGHT_DIM", "resolveBarStyle", "bar", "previous", "strokeOps", "shadowOps", "lerpBarStyle", "to", "t", "lerpNumber", "resolveBarChartTheme", "theme", "base", "resolveSeriesFills", "resolveAxisConfig", "resolvePlotArea", "resolveLegendConfig", "textOps", "lerpBarChartTheme", "lerpSeriesFills", "lerpAxisConfig", "lerpPlotArea", "lerpLegendConfig", "resolveBarFill", "palette", "index", "fillOps", "VALUE_GAP", "VALUE_FIT", "VALUE_SIZE", "dimAt", "s", "index", "clampUnit", "endsAt", "bar", "g", "valueSize", "size", "insideAt", "captionAt", "ends", "formatMarker", "captionY", "direction", "ChartBars", "component2d", "fresh", "Types", "fillOps", "DEFAULT_BAR_CHART_THEME", "strokeOps", "shadowOps", "state", "_", "i", "Text", "textOps", "loader", "loadPaints", "timeline", "ease", "frameEase", "t", "ctx", "height", "radius", "Graphics2D", "rect", "SERIES_ARG", "Types", "LEGEND_SLIDE", "DEFAULT_MARKERS", "AXES_SHARE", "BARS_SHARE", "LEGEND_SHARE", "drawnValue", "s", "domain", "divisions", "range", "resolveBarScale", "markerValues", "domainsDrawn", "barChartDomain", "data", "series", "stacked", "valueAxis", "kept", "settleAxisDomain", "d", "valueLabel", "value", "pattern", "formatMarker", "markerStep", "formatNumber", "valueToY", "plot", "min", "max", "baselinePx", "weightAt", "index", "weightedRow", "row", "scaled", "i", "seriesBars", "captioned", "n", "slotWidth", "slotCenter", "inner", "clampUnit", "gap", "total", "unit", "weight", "before", "rowIndex", "span", "barSpans", "cap", "stackCapIndex", "slotLeft", "x", "width", "num", "categoryDims", "focus", "dim", "chosen", "_", "partsOf", "children", "frame", "cartesianViews", "cAxis", "CategoryAxis", "VerticalAxis", "ChartBars", "ChartLegend", "toggling", "timeline", "state", "target", "parts", "from", "start", "showing", "t", "weights", "shown", "BarChart", "component2d", "fresh", "resolveAxisDomain", "lerpAxisEntry", "lerpAxisDomain", "DEFAULT_BAR_CHART_THEME", "resolveBarChartTheme", "lerpBarChartTheme", "theme", "plotSize", "laidOutSize", "vAxis", "barCategories", "barSets", "resolveBarFill", "below", "Rect", "ChartCanvas", "ChartBorder", "gutter", "cartesianFrame", "easeOut", "_state", "duration", "axes", "rise", "legend", "at0", "piece", "beats", "bars", "lit", "Rect", "Types", "component2d", "easeOut", "Flow", "Paint", "Rect", "Shapes", "Text", "Types", "component2d", "textOps", "SLIDE", "PieLegend", "component2d", "Paint", "Flow", "Shapes", "fresh", "Types", "DEFAULT_LEGEND", "arrivalsEntry", "state", "marker", "_", "index", "Rect", "clampUnit", "arrivalOf", "Text", "textOps", "timeline", "armed", "count", "from", "arm", "per", "step", "ease", "frameEase", "entries", "i", "arrival", "beats", "measurer", "children", "box", "ctx", "Graphics2D", "PathBuilder", "Rect", "Text", "Types", "component2d", "fillOps", "shadowOps", "strokeOps", "textOps", "lerpNumber", "shadowOps", "strokeOps", "textOps", "resolvePieSlices", "data", "category", "value", "palette", "row", "text", "num", "DEG", "sliceValue", "slice", "sliceTotal", "slices", "sum", "ringPoint", "r", "a", "pieArcs", "startDegrees", "total", "arcs", "cursor", "index", "fraction", "a0", "a1", "relaxLabels", "slots", "minGap", "top", "bottom", "b", "ceiling", "slot", "floor", "i", "LABEL_TEXT_STYLE", "CHART_PALETTE", "PIE_SPOTLIGHT_DIM", "DEFAULT_PIE_CHART_THEME", "DEFAULT_SERIES_FILLS", "DEFAULT_LEGEND", "resolveRingConfig", "ring", "previous", "strokeOps", "shadowOps", "lerpRingConfig", "from", "to", "t", "lerpNumber", "resolveLabelConfig", "label", "textOps", "lerpLabelConfig", "resolveCenterConfig", "center", "lerpCenterConfig", "resolvePieChartTheme", "theme", "base", "resolveSeriesFills", "resolveLegendConfig", "lerpPieChartTheme", "lerpSeriesFills", "lerpLegendConfig", "LABEL_GAP", "LABEL_PAD", "GLYPH_RATIO", "LEADER_LIFTOFF", "LEADER_LENGTH", "LABEL_STUB", "TEXT_INSET", "MIN_ELBOW", "ARC_STEPS", "ELBOW_RADIUS", "BARE_PAD", "INSIDE_CAPTION_RATIO", "PIE_LABEL_RADIUS", "CENTER_LABEL_FIT", "INSIDE_LABEL_FIT", "AXIS_EPSILON", "FALLBACK_SIZE", "sizeOf", "style", "fallback", "size", "wedgePath", "a0", "a1", "rInner", "rOuter", "halfGap", "cx", "path", "PathBuilder", "span", "steps", "insetAt", "r", "arcTo", "i", "inset", "from", "to", "point", "ringPoint", "p", "RingGeometry", "s", "box", "pieArcs", "arc", "outer", "hole", "band", "mid", "sin", "cos", "acrossBand", "alongArc", "mode", "category", "percent", "formatPercent", "caption", "fits", "centerStyle", "centerLabel", "centerCaption", "wanted", "captionRatio", "glyphs", "index", "gutters", "glyph", "room", "key", "sideOf", "left", "right", "configured", "px", "clampUnit", "rim", "a", "strongest", "focus", "arcs", "radius", "rowHeight", "top", "bottom", "side", "ideal", "relaxLabels", "slot", "start", "onCircle", "elbowX", "endX", "placement", "NO_BOX", "resolvedFills", "resolvedFill", "fill", "fillOps", "resolved", "ringFills", "slices", "slice", "ringLabels", "blocks", "geometry", "label", "opacity", "wantsCategory", "wantsPercent", "inside", "Text", "textOps", "Rect", "at", "children", "PieRing", "component2d", "fresh", "Types", "DEFAULT_PIE_CHART_THEME", "state", "loader", "loadPaints", "shadowOps", "strokeOps", "timeline", "ease", "frameEase", "t", "ctx", "g", "sweep", "revealEnd", "half", "centerBounds", "fills", "wedge", "Graphics2D", "reveal", "SWEEP_SHARE", "LABEL_SHARE", "SLICE_ARG", "Types", "slicesOf", "s", "resolvePieSlices", "popAt", "index", "clampUnit", "isSliceSelected", "sliceDims", "focus", "i", "dim", "_", "popping", "timeline", "pop", "from", "targets", "value", "t", "partsOf", "children", "row", "ring", "Rect", "PieRing", "PieLegend", "PieChart", "component2d", "fresh", "DEFAULT_PIE_CHART_THEME", "resolvePieChartTheme", "lerpPieChartTheme", "state", "theme", "slices", "parts", "easeOut", "_state", "legend", "sweep", "labels", "at0", "piece", "beats", "NODES", "LineChart", "BarChart", "PieChart"]
|
|
4
|
+
"sourcesContent": ["import {\n Rect,\n Types,\n component2d,\n easeOut,\n type ChildView,\n type DataRecord,\n type NodeOf,\n // Named so this component's emitted type reaches `Node2D` and its props through the sdk, not core.\n type Node2D,\n type Node2DProps as _Node2DProps,\n type Size2D,\n type Vector2,\n} from \"@motionscript/sdk\"\nimport { formatNumber } from \"@motionscript/sdk/component\"\n\nimport { cartesianFrame, cartesianViews, laidOutSize } from \"../kit/cartesian\"\nimport { ChartBorder } from \"../kit/chart-border\"\nimport { ChartCanvas } from \"../kit/chart-canvas\"\nimport { ChartLegend } from \"../kit/chart-legend\"\nimport { SERIES_ARG, asParts, fresh } from \"../kit/entries\"\nimport { DEFAULT_MARKERS, ENTER_SHARE, LEGEND_SLIDE, beats, type AnyTimeline, type Beat } from \"../kit/motion\"\nimport { VerticalAxis } from \"../kit/vertical-axis\"\nimport {\n SPOTLIGHT_DIM,\n formatMarker,\n lerpAxisDomain,\n lerpAxisEntry,\n markerStep,\n markerValues,\n resolveAxisDomain,\n resolveSeriesFill,\n resolveSeriesStroke,\n settleAxisDomain,\n type AxisDomainConfig,\n type Range,\n type ResolvedAxisDomain,\n} from \"../kit/shared\"\nimport { ChartLine } from \"./chart-line\"\nimport { ChartRegion } from \"./chart-region\"\nimport { HorizontalAxis } from \"./horizontal-axis\"\nimport {\n DEFAULT_CHART_THEME,\n lerpChartTheme,\n resolveChartTheme,\n resolveRegionBox,\n resolveYScale,\n seriesPoints,\n type ChartConfig,\n type ChartTheme,\n type LineSeries,\n type PlotRegion,\n type RegionBox,\n} from \"./shared\"\nimport { resolveXScale, type XScale } from \"./x-scale\"\n\n/** Both axes' domains with their ranges and ticks resolved. */\nexport interface LineChartDomains {\n readonly x: ResolvedAxisDomain\n readonly y: ResolvedAxisDomain\n}\n\n/** What a line chart plots, as its entries hold it. */\nexport interface LineChartValues {\n readonly data: DataRecord[]\n readonly xField: string\n readonly series: LineSeries[]\n readonly xAxis: AxisDomainConfig\n readonly yAxis: AxisDomainConfig\n}\n\n/** One argument, two controls: the commands take a `PlotRegion`, so the x and y spans a host draws are declared inside it. */\nconst REGION_ARG = Types.custom<PlotRegion | undefined>(undefined, {\n kind: \"region\",\n properties: {\n x: { kind: \"range\", default: true, defaultMin: 0, defaultMax: 100 },\n y: { kind: \"range\", default: false, defaultMin: 0, defaultMax: 100 },\n },\n})\n\n/** What the chart's own commands read: its entries, and the private `home` a zoom remembers. */\ninterface LineState extends LineChartValues {\n readonly theme: ChartConfig\n readonly region: PlotRegion | null\n readonly regionReveal: number\n readonly home: LineChartDomains | null\n}\n\n/** What the x column is (a number, an instant, or a place in the file), and so how rows are positioned, ticked and captioned. */\nfunction columnOf(s: LineChartValues, format = s.xAxis.format): XScale {\n return resolveXScale(s.data, s.xField, format)\n}\n\n/** The explicit range and ticks, else the column's extent and ticks: only a numeric column has round numbers between its samples. */\nfunction drawnX(s: LineChartValues, domain: AxisDomainConfig): AxisDomainConfig {\n const column = columnOf(s, domain.format)\n const range = domain.range ?? column.extent\n return { ...domain, range, ticks: domain.ticks ?? column.ticks(range, DEFAULT_MARKERS - 1) }\n}\n\n/** Auto-ranging steps into one fewer segment than the marker count, so the tidy bounds frame the ticks drawn. */\nfunction drawnY(s: LineChartValues, domain: AxisDomainConfig): AxisDomainConfig {\n const divisions = (domain.ticks?.length ?? DEFAULT_MARKERS) - 1\n const range = resolveYScale(s.data, s.series, domain.range, divisions)\n return { ...domain, range, ticks: domain.ticks ?? markerValues(range, DEFAULT_MARKERS - 1) }\n}\n\n/** The last domains worked out from each data array, and what else they were read from. */\nconst domainsDrawn = new WeakMap<\n DataRecord[],\n Pick<LineChartValues, \"xField\" | \"xAxis\" | \"yAxis\" | \"series\"> & { readonly domains: LineChartDomains }\n>()\n\n/** Both axes as drawn now. */\nexport function lineChartDomains(s: LineChartValues): LineChartDomains {\n const { data, xField, xAxis, yAxis, series } = s\n const kept = domainsDrawn.get(data)\n if (kept && kept.xField === xField && kept.xAxis === xAxis && kept.yAxis === yAxis && kept.series === series) {\n return kept.domains\n }\n const domains = {\n x: settleAxisDomain(xAxis, (d) => drawnX(s, d)) as LineChartDomains[\"x\"],\n y: settleAxisDomain(yAxis, (d) => drawnY(s, d)) as LineChartDomains[\"y\"],\n }\n domainsDrawn.set(data, { xField, xAxis, yAxis, series, domains })\n return domains\n}\n\n/** One series' points in the plot's centred pixel space (origin at its centre, y-up). */\nfunction seriesPixels(s: LineChartValues, domains: LineChartDomains, index: number, plot: Size2D): Vector2[] {\n const series = s.series[index]\n if (!series) return []\n const points = seriesPoints(s.data, columnOf(s), series)\n if (points.length === 0) return []\n const { width: w, height: h } = plot\n const [xMin, xMax] = domains.x.range\n const [yMin, yMax] = domains.y.range\n const xToPx = (x: number) => (xMax <= xMin ? 0 : ((x - xMin) / (xMax - xMin) - 0.5) * w)\n const yToPx = (y: number) => (yMax <= yMin ? 0 : ((y - yMin) / (yMax - yMin) - 0.5) * h)\n return points.map(([x, y]) => ({ x: xToPx(x), y: yToPx(y) }))\n}\n\n/** `region` in centred pixel space against the live scales, so it travels with a zoom. */\nfunction regionBox(s: LineChartValues & { readonly region: PlotRegion | null }, domains: LineChartDomains, plot: Size2D): RegionBox {\n const { width: w, height: h } = plot\n return resolveRegionBox(s.region, { left: -w / 2, right: w / 2, bottom: -h / 2, top: h / 2 }, domains.x.range, domains.y.range)\n}\n\n/** Whether a call-out is authored, not whether it resolves to pixels: a command is built before the first layout. A region bounding neither axis is none. */\nfunction hasCallout(s: { readonly region: PlotRegion | null }): boolean {\n const region = s.region\n return region != null && (region.x !== undefined || region.y !== undefined)\n}\n\n/** The `region` argument, or `{}`: a command is added before its arguments are filled in. */\nfunction regionArg(region: PlotRegion | null | undefined): PlotRegion {\n return region != null && typeof region === \"object\" ? region : {}\n}\n\n/** A range ordered low to high. */\nfunction span([a, b]: Range): Range {\n return a <= b ? [a, b] : [b, a]\n}\n\n/** Rescale to `range` keeping the tick count, so each marker slides rather than the row popping. */\nfunction rescale(range: Range, ticks: readonly number[]): AxisDomainConfig {\n const to = span(range)\n return { range: to, ticks: markerValues(to, Math.max(2, ticks.length) - 1) }\n}\n\nfunction axesAt(from: LineChartDomains, to: Partial<Record<\"x\" | \"y\", AxisDomainConfig>>, t: number) {\n return {\n ...(to.x && { xAxis: lerpAxisDomain(from.x, to.x, t) }),\n ...(to.y && { yAxis: lerpAxisDomain(from.y, to.y, t) }),\n }\n}\n\n/** The parts a line chart's commands drive, found through its children. */\nfunction partsOf(children: readonly ChildView[]) {\n const frame = cartesianViews(children)\n if (!frame) return null\n const hAxis = frame.below.find(HorizontalAxis.is)\n if (!VerticalAxis.is(frame.axis) || !hAxis) return null\n return {\n vAxis: frame.axis,\n hAxis,\n lines: frame.plot.filter(ChartLine.is),\n legend: frame.below.find(ChartLegend.is) ?? null,\n }\n}\n\nfunction clearing(timeline: AnyTimeline, state: LineState) {\n const from = state.regionReveal\n const region = state.region\n // Nothing to retract, but the call still takes its slot.\n if (from === 0) return timeline.animate(() => ({ region: null, regionReveal: 0 }), { eased: false })\n // `region` is held until the retraction lands, or there would be nothing left to retract.\n return timeline.animate((t) => (t >= 1 ? { region: null, regionReveal: 0 } : { region, regionReveal: from * (1 - t) }))\n}\n\nfunction toggling(timeline: AnyTimeline, children: readonly ChildView[], index: number, show: boolean) {\n const parts = partsOf(children)\n const line = parts?.lines[index]\n if (!parts || !line) return timeline.wait(timeline.duration)\n return timeline.together([\n line.run(\"enter\", show ? { from: 0, to: 1 } : { from: 1, to: 0 }),\n ...(parts.legend ? [parts.legend.run(show ? \"showSeries\" : \"hideSeries\", { index })] : []),\n ])\n}\n\n/**\n * A multi-series line chart composed of {@link ChartCanvas}, {@link VerticalAxis} /\n * {@link HorizontalAxis}, {@link ChartRegion}, one {@link ChartLine} per series and\n * {@link ChartLegend}. Every part reads the chart's entries through bindings and\n * maps against the plot panel's own laid-out box.\n *\n * Resting state is the finished chart; `enter` arms every part on its first\n * frame. A `to` of an axis `range` or `ticks` tweens from the domain as drawn\n * (see {@link lerpAxisEntry}), so an auto axis rescales continuously.\n */\nexport const LineChart = component2d({\n key: \"lineChart\",\n state: {\n data: fresh<DataRecord[]>(() => [], { kind: \"dataset\" }),\n // Not `x`: every node already has an `x`, its position.\n xField: \"\",\n series: fresh<LineSeries[]>(() => [], { kind: \"series\" }),\n xAxis: Types.custom<AxisDomainConfig>({}, { resolve: resolveAxisDomain, lerp: lerpAxisEntry }),\n yAxis: Types.custom<AxisDomainConfig>({}, { resolve: resolveAxisDomain, lerp: lerpAxisEntry }),\n theme: Types.custom<ChartTheme, ChartConfig>(DEFAULT_CHART_THEME, { resolve: resolveChartTheme, lerp: lerpChartTheme }),\n /** The spotlit slice in data units, or `null`. The box snaps; {@link regionReveal} animates it. */\n region: Types.custom<PlotRegion | null>(null),\n /** How far the `region` call-out is drawn in, `[0, 1]`: rules grow, wash fades up, out-of-region stretches fade to `region.dimOpacity`. */\n regionReveal: 0,\n /** Read as the chart is built: the legend is a part the chart builds or doesn't. */\n showLegend: Types.boolean(true, { animatable: false }),\n /** The domain `zoomTo` first left, so `resetZoom` restores it, auto-derived ticks included. */\n home: Types.custom<LineChartDomains | null>(null, { private: true }),\n },\n // A node made of parts that fill would otherwise hug them.\n defaults: { width: \"fill\", height: \"fill\" },\n allowedChildren: [],\n children: (state) => {\n const theme = () => state.theme\n const domains = () => lineChartDomains(state)\n let plot: Node2D | undefined\n const plotSize = () => laidOutSize(plot)\n const enabled = state.series.map((s) => s.enabled ?? true)\n\n // Bound, not snapshotted, so a rescale re-labels and re-places the row as it\n // tweens. The marker *count* is read once, which is why `zoomTo` keeps it.\n const vAxis = new VerticalAxis({\n range: () => domains().y.range,\n markers: () => domains().y.ticks,\n captions: () => {\n const markers = domains().y.ticks\n const format = state.yAxis.format\n return markers.map((value) => (format === undefined ? formatMarker(value, markerStep(markers)) : formatNumber(value, format)))\n },\n axis: () => theme().yAxis,\n label: state.yAxis.label ?? \"\",\n width: \"hug\",\n height: \"fill\",\n })\n\n const hAxis = new HorizontalAxis({\n range: () => domains().x.range,\n markers: () => domains().x.ticks,\n // A month or category tick is captioned with its cell (`Jan`), not the instant it parsed to.\n captions: () => {\n const column = columnOf(state)\n return domains().x.ticks.map((value) => column.label(value))\n },\n axis: () => theme().xAxis,\n label: state.xAxis.label ?? \"\",\n width: \"fill\",\n height: \"hug\",\n })\n\n const lines = state.series.map(\n (s, i) =>\n new ChartLine({\n points: () => seriesPixels(state, domains(), i, plotSize()),\n stroke: () => resolveSeriesStroke(theme().seriesStyles, s, i),\n enabled: enabled[i],\n // The box the rules are drawn from, so the dim boundary cannot drift from the dashes.\n spotlight: () => regionBox(state, domains(), plotSize()),\n dimOpacity: () => 1 - (1 - theme().region.dimOpacity) * state.regionReveal,\n }) as unknown as Node2D\n )\n\n const below: Node2D[] = [hAxis as unknown as Node2D]\n if (state.showLegend !== false) {\n below.push(\n new ChartLegend({\n series: state.series,\n swatches: () => state.series.map((s, i) => resolveSeriesFill(theme().seriesStyles, s, i)),\n legend: () => theme().legend,\n // Spacing is fixed as built; a theme tween re-paints the swatches only.\n gap: state.theme.legend.itemGap,\n padding: state.theme.legend.padding,\n }) as unknown as Node2D\n )\n }\n\n plot = new Rect({\n clip: true,\n children: [\n new ChartCanvas({\n fill: () => theme().plotArea.fill,\n shadow: () => theme().plotArea.shadow,\n xGridStroke: () => theme().xAxis.lineStyle,\n yGridStroke: () => theme().yAxis.lineStyle,\n xMarkers: () => domains().x.ticks,\n yMarkers: () => domains().y.ticks,\n xExtent: () => domains().x.range,\n yExtent: () => domains().y.range,\n }),\n // Under the lines, so the highlight does not tint the stretch it picks out.\n new ChartRegion({\n box: () => regionBox(state, domains(), plotSize()),\n stroke: () => theme().region.stroke,\n fill: () => theme().region.fill,\n reveal: () => state.regionReveal,\n }),\n ...lines,\n new ChartBorder({ stroke: () => theme().plotArea.stroke }),\n ],\n }) as unknown as Node2D\n\n const gutter = new VerticalAxis({\n opacity: 0,\n range: () => domains().y.range,\n markers: () => domains().y.ticks,\n axis: () => theme().yAxis,\n label: state.yAxis.label ?? \"\",\n width: \"hug\",\n height: 0,\n }) as unknown as Node2D\n\n return asParts([cartesianFrame({ axis: vAxis as unknown as Node2D, plot, gutter, below })])\n },\n commands: {\n /**\n * Axes stagger in, lines trace left to right, the legend fades up last.\n * `duration` is the whole entrance; a chart with a `region` lands its\n * call-out in the final quarter.\n */\n enter: {\n args: {},\n // Held from arrival, so it never shows finished ahead of its draw.\n entrance: true,\n easing: easeOut(),\n run: (timeline, state, children) => {\n const parts = partsOf(children)\n if (!parts) return timeline.wait(timeline.duration)\n const duration = timeline.duration\n const revealing = hasCallout(state) && state.regionReveal === 0\n const body = revealing ? duration * 0.75 : duration\n const axes = body * ENTER_SHARE.axes\n const traced = body * ENTER_SHARE.marks\n const { legend } = parts\n const at0 = (piece: Beat[\"piece\"]): Beat => ({ at: 0, duration: 0, piece })\n // The first frame arms what every later beat moves: a beat ahead of the\n // playhead writes nothing. A beat with nothing to do still takes its share.\n return beats(timeline, [\n ...(revealing ? [at0(timeline.animate(() => ({ regionReveal: 0 })))] : []),\n ...parts.lines.map((line) => at0(line.to({ progress: 0 }))),\n ...(legend ? [at0(legend.to({ opacity: 0, y: -LEGEND_SLIDE }))] : []),\n { at: 0, duration: axes, piece: parts.vAxis.run(\"enter\", { slide: 24 }) },\n { at: 0, duration: axes, piece: parts.hAxis.run(\"enter\", { slide: 24 }) },\n ...parts.lines.map((line) => ({ at: axes, duration: traced, piece: line.run(\"enter\") })),\n ...(legend ? [{ at: axes + traced, duration: body * ENTER_SHARE.legend, piece: legend.to({ opacity: 1, y: 0 }) }] : []),\n ...(revealing ? [{ at: body, duration: duration * 0.25, piece: timeline.animate((t) => ({ regionReveal: t })) }] : []),\n ])\n },\n },\n /**\n * Spotlight a slice of the plot in data units; either axis alone is a band.\n * Over a region already up, the old one retracts in the first half and the\n * new one draws in over the second, each half eased on its own.\n */\n spotlightRegion: {\n args: { region: REGION_ARG },\n easing: easeOut(),\n run: (timeline, state) => {\n const region = regionArg(timeline.args.region)\n const from = state.regionReveal\n const previous = state.region\n // A region bounding neither axis is no call-out: spotlighting it is a clear.\n if (region.x === undefined && region.y === undefined) return clearing(timeline, state)\n // Nothing up yet: the region is set from the first frame and just draws on.\n if (from <= 0 || !hasCallout(state)) return timeline.animate((t) => ({ region, regionReveal: from + (1 - from) * t }))\n return timeline.animate(\n (t) => (t < 0.5\n ? { region: previous, regionReveal: from * (1 - timeline.ease(t * 2)) }\n : { region, regionReveal: timeline.ease(t * 2 - 1) }),\n { eased: false }\n )\n },\n },\n /** Drop the call-out. A no-op when nothing is spotlit, which still takes its slot. */\n clearRegion: {\n args: {},\n easing: easeOut(),\n run: (timeline, state) => clearing(timeline, state),\n },\n /** Rescale the named axes so a slice (data units) fills the plot; an omitted axis is left. The first zoom remembers the domain for `resetZoom`. */\n zoomTo: {\n args: { region: REGION_ARG },\n easing: easeOut(),\n run: (timeline, state) => {\n const region = regionArg(timeline.args.region)\n if (!region.x && !region.y) return timeline.wait(timeline.duration)\n const from = lineChartDomains(state)\n const home = state.home ?? from\n const to = {\n ...(region.x && { x: { ...from.x, ...rescale(region.x, from.x.ticks) } }),\n ...(region.y && { y: { ...from.y, ...rescale(region.y, from.y.ticks) } }),\n }\n return timeline.animate((t) => ({ home, ...axesAt(from, to, t) }))\n },\n },\n /** Back to the domain the first zoom left; a hold if it never zoomed. */\n resetZoom: {\n args: {},\n easing: easeOut(),\n run: (timeline, state) => {\n const home = state.home\n if (!home) return timeline.wait(timeline.duration)\n const from = lineChartDomains(state)\n return timeline.animate((t) => ({ home: null, ...axesAt(from, home, t) }))\n },\n },\n /** Trace series `index` in, with its legend entry growing in. */\n showSeries: {\n args: { seriesIndex: SERIES_ARG },\n easing: easeOut(),\n run: (timeline, _state, children) => toggling(timeline, children, timeline.args.seriesIndex, true),\n },\n /** Retrace series `index` out as its legend entry shrinks away. */\n hideSeries: {\n args: { seriesIndex: SERIES_ARG },\n easing: easeOut(),\n run: (timeline, _state, children) => toggling(timeline, children, timeline.args.seriesIndex, false),\n },\n /** Fade every other series, line and legend entry, to focus on `index`. The legend dims in place, so the row does not reflow. */\n spotlightSeries: {\n args: { seriesIndex: SERIES_ARG },\n easing: easeOut(),\n run: (timeline, _state, children) => {\n const parts = partsOf(children)\n if (!parts) return timeline.wait(timeline.duration)\n const index = timeline.args.seriesIndex\n return timeline.together([\n ...parts.lines.map((line, i) => line.to({ opacity: i === index ? 1 : SPOTLIGHT_DIM })),\n ...(parts.legend ? [parts.legend.run(\"spotlightSeries\", { index })] : []),\n ])\n },\n },\n },\n})\n\nexport type LineChart = NodeOf<typeof LineChart>\n", "import {\n Graphics2D,\n Types,\n component2d,\n strokeOps,\n type NodeOf,\n // Named so this component's emitted type reaches `Node2D` and its props through the sdk, not core.\n type Node2D as _Node2D,\n type Node2DProps as _Node2DProps,\n type Stroke,\n type StrokeResolved,\n type Vector2,\n} from \"@motionscript/sdk\"\n\nimport { fresh } from \"../kit/entries\"\nimport { frameEase } from \"../kit/motion\"\nimport { loadPaints } from \"../kit/paints\"\nimport { SPOTLIGHT_DIM } from \"../kit/shared\"\nimport {\n EMPTY_REGION_BOX,\n clipPolylineToBox,\n hasRegion,\n type RegionBox,\n} from \"./shared\"\n\n/** The prefix of `pts` revealed at fraction `p`, measured by cumulative arc length so the trace keeps a steady pace over uneven spacing. */\nfunction tracePoints(pts: Vector2[], p: number): Vector2[] {\n if (p >= 1 || pts.length < 2) return pts\n if (p <= 0) return []\n\n const segLen: number[] = []\n let total = 0\n for (let i = 1; i < pts.length; i++) {\n const d = Math.hypot(pts[i].x - pts[i - 1].x, pts[i].y - pts[i - 1].y)\n segLen.push(d)\n total += d\n }\n if (total === 0) return pts\n\n let target = total * p\n const out: Vector2[] = [pts[0]]\n for (let i = 0; i < segLen.length; i++) {\n if (target >= segLen[i]) {\n out.push(pts[i + 1])\n target -= segLen[i]\n } else {\n const t = segLen[i] === 0 ? 0 : target / segLen[i]\n out.push({\n x: pts[i].x + (pts[i + 1].x - pts[i].x) * t,\n y: pts[i].y + (pts[i + 1].y - pts[i].y) * t,\n })\n break\n }\n }\n return out\n}\n\n/** One series of a {@link LineChart}: a rounded polyline through pre-mapped pixel `points`, one {@link Graphics2D} command list. `progress` traces it in; `enabled` shows or hides it wholesale. */\nexport const ChartLine = component2d({\n key: \"chartLine\",\n state: {\n /** In this node's pixel space (origin at centre, y-up), mapped by the parent and ordered along x. */\n points: fresh<Vector2[]>(() => []),\n stroke: Types.custom<Stroke, StrokeResolved[]>([], { resolve: strokeOps.resolve, lerp: strokeOps.lerp }),\n /** Trace from the left end, `[0, 1]`. Rests at 1 so a paused editor canvas paints the line; `enter` drives it from 0. */\n progress: 1,\n /** Whether the line is drawn at all, flipped alongside its legend entry. */\n enabled: true,\n /** In this node's pixel space; outside it the line drops to `dimOpacity`. */\n spotlight: fresh<RegionBox>(() => EMPTY_REGION_BOX),\n dimOpacity: SPOTLIGHT_DIM,\n },\n load: (loader, state) => loadPaints(loader, { strokes: [state.stroke] }),\n commands: {\n /**\n * Trace the line from `from` to `to`; `showSeries`/`hideSeries` are this with\n * the ends stated. `enabled` rides along in the same value, so the line is\n * drawable for every `t` the trace covers and arriving backwards matches\n * arriving forwards.\n */\n enter: {\n args: { from: Types.number(0, { min: 0, max: 1 }), to: Types.number(1, { min: 0, max: 1 }) },\n run: (timeline) => {\n const { from, to } = timeline.args\n const ease = frameEase(timeline)\n return timeline.animate((t) => {\n const progress = from + (to - from) * ease(t)\n // Hidden only once fully retraced, so a hide does not vanish a frame early.\n return { progress, enabled: progress > 0 }\n })\n },\n },\n },\n render: (ctx, state) => {\n if (!state.enabled) return\n const pts = state.points\n if (pts.length < 2) return\n\n const p = Math.max(0, Math.min(1, state.progress))\n if (p <= 0) return\n\n const drawn = tracePoints(pts, p)\n if (drawn.length < 2) return\n const stroke = state.stroke\n const radius = (stroke[0]?.weight ?? 0) * 2\n\n const box = state.spotlight\n if (!hasRegion(box)) {\n ctx.draw(new Graphics2D().line({ points: drawn, radius }).stroke(stroke))\n return\n }\n\n // Dim the whole line, then redraw the stretches inside the region at full strength: the bright run keeps continuous geometry and round joins, and the boundary lands on the region edge.\n ctx.draw(\n new Graphics2D()\n .line({ points: drawn, radius })\n .stroke(stroke)\n .opacity(Math.max(0, Math.min(1, state.dimOpacity)))\n )\n\n // One `draw` per run: shapes chained before `stroke()` combine into one surface, and stroking a union of disjoint open paths paints nothing.\n for (const run of clipPolylineToBox(drawn, box)) {\n ctx.draw(new Graphics2D().line({ points: run, radius }).stroke(stroke))\n }\n },\n})\n\nexport type ChartLine = NodeOf<typeof ChartLine>\n", "import {\n Fills,\n fillOps,\n lerpNumber,\n strokeOps,\n type DataRecord,\n type Fill,\n type FillResolved,\n type Stroke,\n type StrokeResolved,\n type Vector2,\n} from \"@motionscript/sdk\"\nimport { niceStep } from \"@motionscript/sdk/component\"\n\nimport {\n CHART_PALETTE,\n DEFAULT_LEGEND,\n DEFAULT_PLOT_AREA,\n DEFAULT_SERIES_STROKES,\n DEFAULT_X_AXIS,\n DEFAULT_Y_AXIS,\n SPOTLIGHT_DIM,\n lerpAxisConfig,\n lerpLegendConfig,\n lerpPlotArea,\n lerpSeriesStrokes,\n num,\n resolveAxisConfig,\n resolveLegendConfig,\n resolvePlotArea,\n resolveSeriesStrokes,\n type AxisConfig,\n type ChartSeries,\n type LegendConfig,\n type LegendTheme,\n type PlotAreaConfig,\n type Range,\n} from \"../kit/shared\"\nimport type { XScale } from \"./x-scale\"\n\n/** One polyline of a {@link LineChart}: {@link ChartSeries} under the name for what it draws here. */\nexport type LineSeries = ChartSeries\n\n/**\n * A rectangular slice of the plot in data units, the input to `spotlightRegion`\n * and `zoomTo`. Either axis may be omitted: `{ x: [60, 100] }` is a full-height\n * band. A reversed range reads the same as a forward one.\n */\nexport interface PlotRegion {\n /** Omit to span the full plot width. */\n x?: Range\n /** Omit to span the full plot height. */\n y?: Range\n}\n\n/**\n * A {@link PlotRegion} in the plot's centred pixel space (origin at the centre,\n * y-up), the frame the series points live in. An axis the author left unbounded\n * has its edges pinned to the plot's own and draws no rule.\n */\nexport interface RegionBox {\n left: number\n right: number\n bottom: number\n top: number\n xBounded: boolean\n yBounded: boolean\n}\n\n/** The \"no region\" resting value. */\nexport const EMPTY_REGION_BOX: RegionBox = {\n left: 0,\n right: 0,\n bottom: 0,\n top: 0,\n xBounded: false,\n yBounded: false,\n}\n\n/** Whether `box` is non-degenerate, so there is something to spotlight. */\nexport function hasRegion(box: RegionBox): boolean {\n return box.right > box.left && box.top > box.bottom\n}\n\n/**\n * Resolve a {@link PlotRegion} against the plot rectangle (centred pixel space)\n * and the data ranges its edges span. An omitted axis takes the plot's own\n * edges; a given one is clamped to the plot.\n */\nexport function resolveRegionBox(\n region: PlotRegion | null | undefined,\n plot: { left: number; right: number; bottom: number; top: number },\n xScale: Range,\n yScale: Range\n): RegionBox {\n if (!region || (region.x === undefined && region.y === undefined)) {\n return EMPTY_REGION_BOX\n }\n\n const span = (\n range: Range | undefined,\n [min, max]: Range,\n lo: number,\n hi: number\n ): [number, number, boolean] => {\n if (range === undefined || max <= min) return [lo, hi, false]\n const a = Math.min(range[0], range[1])\n const b = Math.max(range[0], range[1])\n const toPx = (v: number) => lo + ((v - min) / (max - min)) * (hi - lo)\n return [\n Math.max(lo, Math.min(hi, toPx(a))),\n Math.max(lo, Math.min(hi, toPx(b))),\n true,\n ]\n }\n\n const [left, right, xBounded] = span(region.x, xScale, plot.left, plot.right)\n const [bottom, top, yBounded] = span(region.y, yScale, plot.bottom, plot.top)\n return { left, right, bottom, top, xBounded, yBounded }\n}\n\n/**\n * The parts of `points` inside `box`, as contiguous sub-polylines: a series is\n * drawn dimmed in full, then these runs are re-drawn at full strength. Segments\n * are clipped, not filtered by endpoint, so a line entering mid-segment is cut\n * exactly on the boundary.\n */\nexport function clipPolylineToBox(\n points: Vector2[],\n box: RegionBox\n): Vector2[][] {\n if (points.length < 2 || !hasRegion(box)) return []\n\n const runs: Vector2[][] = []\n let run: Vector2[] = []\n\n for (let i = 1; i < points.length; i++) {\n const seg = clipSegment(points[i - 1], points[i], box)\n if (!seg) {\n // Misses the box: close the open run.\n if (run.length >= 2) runs.push(run)\n run = []\n continue\n }\n const [a, b] = seg\n // Continues the open run if it starts where the last ended; otherwise the line left and came back.\n if (run.length > 0 && samePoint(run[run.length - 1], a)) run.push(b)\n else {\n if (run.length >= 2) runs.push(run)\n run = [a, b]\n }\n }\n if (run.length >= 2) runs.push(run)\n return runs\n}\n\n/** Points within half a pixel are the same point, for run-joining purposes. */\nfunction samePoint(a: Vector2, b: Vector2): boolean {\n return Math.abs(a.x - b.x) < 0.5 && Math.abs(a.y - b.y) < 0.5\n}\n\n/** Liang-Barsky clip of one segment against `box`: the surviving `[start, end]`, or `null` when wholly outside. */\nfunction clipSegment(\n a: Vector2,\n b: Vector2,\n box: RegionBox\n): [Vector2, Vector2] | null {\n const dx = b.x - a.x\n const dy = b.y - a.y\n let t0 = 0\n let t1 = 1\n\n // Each edge as (p, q): the segment survives where p*t <= q.\n const edges: [number, number][] = [\n [-dx, a.x - box.left],\n [dx, box.right - a.x],\n [-dy, a.y - box.bottom],\n [dy, box.top - a.y],\n ]\n for (const [p, q] of edges) {\n if (p === 0) {\n // Parallel to this edge: outside it, the whole segment is out.\n if (q < 0) return null\n continue\n }\n const r = q / p\n if (p < 0) {\n if (r > t1) return null\n if (r > t0) t0 = r\n } else {\n if (r < t0) return null\n if (r < t1) t1 = r\n }\n }\n return [\n { x: a.x + t0 * dx, y: a.y + t0 * dy },\n { x: a.x + t1 * dx, y: a.y + t1 * dy },\n ]\n}\n\n/** How a spotlit {@link PlotRegion} is drawn: rules on its bounded edges, a wash, and how far series fade outside it. */\nexport interface RegionConfig {\n /** Marks each bounded edge. */\n stroke: Stroke\n /** Drawn under the series lines. */\n fill: Fill\n /** Opacity the stretch of every series outside the region fades to. */\n dimOpacity: number\n}\n\n/** A {@link LineChart}'s full, resolved visual theme. */\nexport interface ChartConfig {\n /** Cycled by series index; a series' own `fill` overrides just the colour. */\n seriesStyles: Stroke[]\n xAxis: AxisConfig\n yAxis: AxisConfig\n plotArea: PlotAreaConfig\n legend: LegendConfig\n region: RegionConfig\n}\n\n/** The author's partial theme, deep-merged onto {@link DEFAULT_CHART_THEME}. */\nexport type ChartTheme = {\n seriesStyles?: Stroke[]\n xAxis?: Partial<AxisConfig>\n yAxis?: Partial<AxisConfig>\n plotArea?: Partial<PlotAreaConfig>\n legend?: LegendTheme\n region?: Partial<RegionConfig>\n}\n\n/** The shared legend with a small square swatch: a line's swatch stands for a colour, where a bar's or wedge's stands for an area of paint. */\nconst LINE_CHART_LEGEND: LegendConfig = {\n ...DEFAULT_LEGEND,\n markerStyle: {\n ...DEFAULT_LEGEND.markerStyle,\n width: 20,\n height: 20,\n borderRadius: 6,\n },\n}\n\nexport const DEFAULT_CHART_THEME: ChartConfig = {\n seriesStyles: DEFAULT_SERIES_STROKES,\n xAxis: DEFAULT_X_AXIS,\n yAxis: DEFAULT_Y_AXIS,\n plotArea: DEFAULT_PLOT_AREA,\n legend: LINE_CHART_LEGEND,\n region: {\n // Brighter and dashed so it reads as a call-out, not an axis line; `butt` caps keep dash lengths exact.\n stroke: {\n weight: 3,\n fill: CHART_PALETTE.regionColor,\n dash: [14, 12],\n cap: \"butt\",\n },\n fill: Fills.color(CHART_PALETTE.regionColor, { opacity: 0.08 }),\n dimOpacity: SPOTLIGHT_DIM,\n },\n}\n\nfunction resolveRegionConfig(\n region: Partial<RegionConfig> | undefined,\n previous: RegionConfig\n): RegionConfig {\n return {\n stroke: strokeOps.resolve(region?.stroke ?? previous.stroke),\n fill: fillOps.resolve(region?.fill ?? previous.fill),\n dimOpacity: region?.dimOpacity ?? previous.dimOpacity,\n }\n}\n\nfunction lerpRegionConfig(\n from: RegionConfig,\n to: RegionConfig,\n t: number\n): RegionConfig {\n return {\n stroke: strokeOps.lerp(\n from.stroke as StrokeResolved[],\n to.stroke as StrokeResolved[],\n t\n ),\n fill: fillOps.lerp(\n from.fill as FillResolved[],\n to.fill as FillResolved[],\n t\n ),\n dimOpacity: lerpNumber(from.dimOpacity, to.dimOpacity, t),\n }\n}\n\n/** The `theme` entry's resolve: merges a partial {@link ChartTheme} onto `previous` (first resolution: the default). */\nexport function resolveChartTheme(\n theme: ChartTheme | undefined,\n previous?: ChartConfig\n): ChartConfig {\n const base = previous ?? DEFAULT_CHART_THEME\n return {\n seriesStyles: resolveSeriesStrokes(theme?.seriesStyles, base.seriesStyles),\n xAxis: resolveAxisConfig(theme?.xAxis, base.xAxis),\n yAxis: resolveAxisConfig(theme?.yAxis, base.yAxis),\n plotArea: resolvePlotArea(theme?.plotArea, base.plotArea),\n legend: resolveLegendConfig(theme?.legend, base.legend),\n region: resolveRegionConfig(theme?.region, base.region),\n }\n}\n\nexport function lerpChartTheme(\n from: ChartConfig,\n to: ChartConfig,\n t: number\n): ChartConfig {\n return {\n seriesStyles: lerpSeriesStrokes(from.seriesStyles, to.seriesStyles, t),\n xAxis: lerpAxisConfig(from.xAxis, to.xAxis, t),\n yAxis: lerpAxisConfig(from.yAxis, to.yAxis, t),\n plotArea: lerpPlotArea(from.plotArea, to.plotArea, t),\n legend: lerpLegendConfig(from.legend, to.legend, t),\n region: lerpRegionConfig(from.region, to.region, t),\n }\n}\n\n/**\n * One series' `[x, y]` points, sorted along x so the polyline traces left to\n * right. A row missing its y contributes 0 rather than a gap, since dropping it\n * would silently bridge the hole.\n */\nexport function seriesPoints(\n data: DataRecord[],\n x: XScale,\n series: LineSeries\n): [x: number, y: number][] {\n return data\n .map((row, i): [number, number] => [x.at(i), num(row, series.field)])\n .sort((a, b) => a[0] - b[0])\n}\n\nexport function yExtent(data: DataRecord[], series: LineSeries[]): Range {\n let lo = Infinity\n let hi = -Infinity\n for (const row of data)\n for (const s of series) {\n const v = num(row, s.field)\n if (v < lo) lo = v\n if (v > hi) hi = v\n }\n return lo <= hi ? [lo, hi] : [0, 1]\n}\n\n/**\n * The explicit `range`, else the data's y extent nudged out to tidy step bounds.\n * `divisions` (one fewer than the marker count) only shapes the step when\n * `range` is omitted.\n */\nexport function resolveYScale(\n data: DataRecord[],\n series: LineSeries[],\n range: Range | undefined,\n divisions: number\n): Range {\n if (range) return range\n const [lo, hi] = yExtent(data, series)\n const segments = Math.max(1, Math.round(divisions))\n if (hi === lo) return constantScale(lo, segments)\n const step = niceStep((hi - lo) / segments)\n return [Math.floor(lo / step) * step, Math.ceil(hi / step) * step]\n}\n\n/**\n * The scale for constant data. A zero-width scale would put every tick on one\n * value and the line on the floor, so it widens by a step sized to the\n * magnitude (`5` gets `[3, 7]`), or `[0, 1]` for zero.\n */\nfunction constantScale(value: number, segments: number): Range {\n if (value === 0) return [0, 1]\n const step = niceStep(Math.abs(value) / segments)\n return [value - step, value + step]\n}\n", "import {\n Graphics2D,\n Types,\n component2d,\n fillOps,\n strokeOps,\n type Fill,\n type FillResolved,\n type NodeOf,\n // Named so this component's emitted type reaches `Node2D` and its props through the sdk, not core.\n type Node2D as _Node2D,\n type Node2DProps as _Node2DProps,\n type Stroke,\n type StrokeResolved,\n} from \"@motionscript/sdk\"\n\nimport { fresh } from \"../kit/entries\"\nimport { loadPaints } from \"../kit/paints\"\nimport {\n DEFAULT_CHART_THEME,\n EMPTY_REGION_BOX,\n hasRegion,\n type RegionBox,\n} from \"./shared\"\n\n/**\n * The call-out of a {@link LineChart}: a washed rectangle with a dashed rule on\n * each bounded edge, drawn from the pre-mapped `box`. Dimming the series outside\n * is {@link ChartLine}'s job, clipped against the same box so the two agree on\n * the boundary. An unbounded edge is the plot's own border, and a rule there\n * would assert a limit the author did not set.\n */\nexport const ChartRegion = component2d({\n key: \"chartRegion\",\n state: {\n /** In this node's pixel space (origin at centre, y-up), mapped by the parent. A degenerate box draws nothing. */\n box: fresh<RegionBox>(() => EMPTY_REGION_BOX),\n stroke: Types.custom<Stroke, StrokeResolved[]>(DEFAULT_CHART_THEME.region.stroke, { resolve: strokeOps.resolve, lerp: strokeOps.lerp }),\n fill: Types.custom<Fill, FillResolved[]>(DEFAULT_CHART_THEME.region.fill, { resolve: fillOps.resolve, lerp: fillOps.lerp }),\n /** Driven by the chart's `regionReveal`, so the rules and the series' dimming move as one. */\n reveal: 0,\n },\n load: (loader, state) => loadPaints(loader, { fills: [state.fill], strokes: [state.stroke] }),\n render: (ctx, state) => {\n const box = state.box\n if (!hasRegion(box)) return\n\n const r = Math.max(0, Math.min(1, state.reveal))\n if (r <= 0) return\n\n const width = box.right - box.left\n const height = box.top - box.bottom\n const cx = (box.left + box.right) / 2\n const cy = (box.bottom + box.top) / 2\n\n if (state.fill.length > 0) {\n ctx.draw(new Graphics2D().rect({ x: cx, y: cy, width, height }).fill(state.fill).opacity(r))\n }\n\n if (state.stroke.length === 0) return\n\n // One `draw` per rule: stroking a union of disjoint open paths paints nothing.\n const rule = (points: { x: number; y: number }[]) =>\n ctx.draw(new Graphics2D().line({ points }).stroke(state.stroke).opacity(r))\n\n if (box.xBounded) {\n const half = (height / 2) * r\n for (const x of [box.left, box.right]) rule([{ x, y: cy - half }, { x, y: cy + half }])\n }\n if (box.yBounded) {\n const half = (width / 2) * r\n for (const y of [box.bottom, box.top]) rule([{ x: cx - half, y }, { x: cx + half, y }])\n }\n },\n})\n\nexport type ChartRegion = NodeOf<typeof ChartRegion>\n", "import {\n Rect,\n Text,\n Types,\n component2d,\n insetsOps,\n textOps,\n type NodeOf,\n type Node2D,\n type Node2DProps as _Node2DProps,\n} from \"@motionscript/sdk\";\nimport { enterBudget } from \"@motionscript/sdk/component\";\n\nimport { asParts, fresh } from \"../kit/entries\";\nimport { armed, arrival, arrivalOf, arrivalsEntry, beats, frameEase, type Arrival } from \"../kit/motion\";\nimport {\n DEFAULT_X_AXIS,\n type AxisConfig,\n type Range,\n} from \"../kit/shared\";\nimport { markerCaption } from \"../kit/vertical-axis\";\n\nconst SLIDE = 24;\n\n/**\n * The horizontal axis of a {@link LineChart}: a row of {@link Text} markers, the\n * mirror of `VerticalAxis` (bound text and positions, landed resting state).\n * Markers are placed by value across the range, whatever they say: `2026-03`\n * sits three months along, not three slots along, unlike a bar chart's axis.\n */\nexport const HorizontalAxis = component2d({\n key: \"horizontalAxis\",\n state: {\n range: fresh<Range>(() => [0, 1]),\n /** Tick values in data units. */\n markers: fresh<number[]>(() => []),\n /** One caption per marker in its order; a missing one reads as the bare numeric value. */\n captions: fresh<string[]>(() => []),\n axis: Types.custom<AxisConfig>(DEFAULT_X_AXIS),\n /** Caption centred beneath the markers; empty hides it. */\n label: \"\",\n arrivals: arrivalsEntry(),\n },\n // A node made of markers would otherwise hug them.\n defaults: { width: \"fill\", height: \"fill\" },\n children: (state) => {\n const tickPad = insetsOps.resolve(state.axis.tickPadding).top;\n const titlePad = insetsOps.resolve(state.axis.titlePadding).top;\n\n const column: Node2D[] = [];\n // The column's parent is the axis, whose box the markers are placed in.\n const box = () => (column[0]?.parent as Node2D | null | undefined)?.layoutBounds ?? { width: 0, height: 0 };\n const sorted = () => [...state.markers].sort((a, b) => a - b);\n const fracAt = (value: number | undefined) => {\n if (value === undefined) return 0;\n const [min, max] = state.range;\n return max <= min ? 0 : (value - min) / (max - min);\n };\n\n const markers = state.markers.map((_, i) => {\n const marker: Node2D = new Text({\n ...textOps.styleProps(state.axis.labelStyle),\n text: () => {\n const value = sorted()[i];\n return value === undefined ? \"\" : markerCaption(state, value);\n },\n x: () => {\n const width = box().width;\n const centred = (fracAt(sorted()[i]) - 0.5) * width;\n // Held inside the right edge, the chart's own: the last marker is centred on the last grid line, so half of it would fall outside the box. The left edge has the vertical axis's gutter beyond it.\n const limit = width / 2 - marker.layoutBounds.width / 2;\n return Math.min(centred, limit);\n },\n opacity: () => arrivalOf(state.arrivals, i).opacity,\n y: () => arrivalOf(state.arrivals, i).shift,\n });\n return marker;\n });\n\n const children: Node2D[] = [\n new Rect({ width: \"fill\", height: \"hug\", padding: { top: tickPad }, children: markers }),\n ];\n if (state.label !== \"\") {\n children.push(\n new Text({\n ...textOps.styleProps(state.axis.titleStyle),\n text: state.label,\n opacity: () => arrivalOf(state.arrivals, \"label\").opacity,\n y: () => arrivalOf(state.arrivals, \"label\").shift,\n }),\n );\n }\n\n column.push(new Rect({ flow: \"vertical\", gap: titlePad, height: \"hug\", width: \"fill\", children }));\n return asParts(column);\n },\n commands: {\n /** Stagger markers in left to right, then the caption. The duration is the whole entrance ({@link enterBudget}). */\n enter: {\n args: {\n stagger: Types.custom<number | undefined>(undefined),\n slide: Types.number(SLIDE),\n },\n run: (timeline, state) => {\n const { stagger, slide } = timeline.args;\n const count = state.markers.length;\n const captioned = state.label !== \"\";\n const from: Arrival = { opacity: 0, shift: slide };\n const keys = [...state.markers.keys(), ...(captioned ? [\"label\"] : [])];\n const arm = timeline.animate(() => ({ arrivals: armed(keys, from) }));\n if (count === 0) return arm;\n const { step, per, caption } = enterBudget(timeline.duration, count, captioned, stagger);\n const ease = frameEase(timeline);\n const markers = state.markers.map((_, i) => ({ at: i * step, duration: per, piece: arrival(timeline, i, from, ease) }));\n const label = captioned ? [{ at: (count - 1) * step + per, duration: caption, piece: arrival(timeline, \"label\", from, ease) }] : [];\n return timeline.together([arm, beats(timeline, [...markers, ...label])]);\n },\n },\n },\n});\n\nexport type HorizontalAxis = NodeOf<typeof HorizontalAxis>;\n", "/**\n * What a line chart's x column means: a number (plotted as is), an instant\n * (`Jan`, `2026-W12`; plotted by time, so a gap in the data reads as a gap), or\n * anything else (the row's position, evenly spaced).\n *\n * A tick on a time or ordinal scale is a sample, so its caption is the cell the\n * author wrote; only a tick that is not (an author-set range, a `zoomTo`\n * mid-flight) is formatted, by {@link formatInstant}.\n */\n\nimport type { DataRecord } from \"@motionscript/sdk\"\n\nimport {\n formatMarker,\n markerValues,\n num,\n text,\n type Range,\n} from \"../kit/shared\"\nimport { formatNumber } from \"@motionscript/sdk/component\"\n\n/** How an x column's cells become positions along the axis. */\nexport type XScaleKind = \"numeric\" | \"time\" | \"ordinal\"\n\n/** An x column resolved into positions, captions and ticks: three closures built once per (data, column), not a tag callers switch on. */\nexport interface XScale {\n kind: XScaleKind\n /** Position of row `index` along the axis, in this scale's own units. */\n at(index: number): number\n /** `[min, max]` across the rows: the scale when no range is set. */\n extent: Range\n label(value: number): string\n /** `divisions + 1` tick values spanning `range`, ends included. */\n ticks(range: Range, divisions: number): number[]\n}\n\nconst DAY = 86_400_000\n\nconst MONTHS = [\n \"january\",\n \"february\",\n \"march\",\n \"april\",\n \"may\",\n \"june\",\n \"july\",\n \"august\",\n \"september\",\n \"october\",\n \"november\",\n \"december\",\n]\n\n/** The year a cell naming none is dated into; a leap year, so February spaces the same as in a column that carries years. */\nconst NOMINAL_YEAR = 2024\n\n/** Month index for `jan`, `sept` or `january`, else -1: three letters minimum, then a prefix match, so `sept` passes and `janitor` does not. */\nfunction monthIndex(word: string): number {\n if (word.length < 3) return -1\n return MONTHS.findIndex((month) => month.startsWith(word))\n}\n\nfunction isoWeekStart(year: number, week: number): number {\n // ISO week 1 is the week containing 4 January, and weeks start on Monday.\n const jan4 = Date.UTC(year, 0, 4)\n const weekday = (new Date(jan4).getUTCDay() + 6) % 7 // Monday = 0\n return jan4 - weekday * DAY + (week - 1) * 7 * DAY\n}\n\n/**\n * One cell read as an instant, or `null`. Not `Date.parse`, which accepts `\"5\"`\n * and `\"Q3\"`, differs between engines off ISO, and reads `2026-03-14` as UTC but\n * `2026/03/14` as local. Every form is matched explicitly, and an ambiguous\n * all-numeric date (`03/04/2026`) is not one of them.\n */\nexport function parseInstant(raw: unknown): number | null {\n if (typeof raw !== \"string\") return null\n const s = raw.trim().toLowerCase()\n if (s === \"\") return null\n\n // 2026-03-14 · 2026/03/14 · 2026-03 · 2026/03\n const ymd = /^(\\d{4})[-/](\\d{1,2})(?:[-/](\\d{1,2}))?$/.exec(s)\n if (ymd) {\n const month = Number(ymd[2])\n const day = ymd[3] === undefined ? 1 : Number(ymd[3])\n if (month < 1 || month > 12 || day < 1 || day > 31) return null\n return Date.UTC(Number(ymd[1]), month - 1, day)\n }\n\n // 2026-W12 · 2026W12 · w12 · week 12\n const week = /^(?:(\\d{4})[-\\s]?)?w(?:ee)?k?[-\\s]?(\\d{1,2})$/.exec(s)\n if (week) {\n const n = Number(week[2])\n if (n < 1 || n > 53) return null\n return isoWeekStart(\n week[1] === undefined ? NOMINAL_YEAR : Number(week[1]),\n n\n )\n }\n\n // jan · january · jan 2026 · january-2026 · 2026 jan\n const named = /^(?:(\\d{4})[-\\s]+)?([a-z]+)(?:[-\\s]+(\\d{4}))?$/.exec(s)\n if (named) {\n const month = monthIndex(named[2])\n if (month >= 0) {\n const year = named[1] ?? named[3]\n const y = year === undefined ? NOMINAL_YEAR : Number(year)\n return Date.UTC(y, month, 1)\n }\n }\n\n // 14 mar 2026 · mar 14 2026 · mar 14, 2026\n const dayNamed =\n /^(?:(\\d{1,2})[-\\s]+([a-z]+)|([a-z]+)[-\\s]+(\\d{1,2}),?)[-\\s]+(\\d{4})$/.exec(\n s\n )\n if (dayNamed) {\n const month = monthIndex(dayNamed[2] ?? dayNamed[3])\n const day = Number(dayNamed[1] ?? dayNamed[4])\n if (month >= 0 && day >= 1 && day <= 31) {\n return Date.UTC(Number(dayNamed[5]), month, day)\n }\n }\n\n return null\n}\n\n/** An instant captioned at the coarsest granularity the span tells apart: a decade reads as years, a year as months. */\nexport function formatInstant(value: number, span: number): string {\n const date = new Date(value)\n const year = date.getUTCFullYear()\n const month = MONTHS[date.getUTCMonth()].slice(0, 3)\n const label = month.charAt(0).toUpperCase() + month.slice(1)\n if (span > 4 * 365 * DAY) return String(year)\n if (span > 60 * DAY) return `${label} ${year}`\n return `${label} ${date.getUTCDate()}`\n}\n\n/** `numericFields`' rule for one column: a single text cell takes it off the numeric scale rather than plot that row at 0. */\nfunction isNumericColumn(data: DataRecord[], field: string): boolean {\n let sawValue = false\n for (const row of data) {\n const v = row[field]\n if (v === undefined || v === null || v === \"\") continue\n if (typeof v !== \"number\") return false\n sawValue = true\n }\n return sawValue\n}\n\n/**\n * Every non-empty cell as an instant, or `null` if any is not one. A blank cell\n * keeps its slot and takes the nearest known instant: stacking it on the epoch\n * would drag the whole scale back fifty years.\n */\nfunction instantColumn(data: DataRecord[], field: string): number[] | null {\n const parsed: (number | null)[] = []\n let sawValue = false\n for (const row of data) {\n const raw = row[field]\n if (raw === undefined || raw === null || raw === \"\") {\n parsed.push(null)\n continue\n }\n const t = parseInstant(raw)\n if (t === null) return null\n parsed.push(t)\n sawValue = true\n }\n if (!sawValue) return null\n\n const first = parsed.find((t): t is number => t !== null) as number\n let last = first\n return parsed.map((t) => (t === null ? last : (last = t)))\n}\n\n/** `[min, max]` of `values`, or `[0, 1]` when there is nothing to span. */\nfunction extentOf(values: number[]): Range {\n let lo = Infinity\n let hi = -Infinity\n for (const v of values) {\n if (v < lo) lo = v\n if (v > hi) hi = v\n }\n return lo <= hi ? [lo, hi] : [0, 1]\n}\n\n/**\n * Ticks spanning `range`, each a sample, nearest to where an evenly spaced tick\n * would go. They must be samples (a caption between two has nothing to say) and\n * spread (every nth sample would print five dates on top of each other across a\n * burst of rows), so this walks the evenly spaced targets, not the samples.\n * Targets that pick the same sample collapse to one tick, so a gappy column\n * draws fewer honest ticks rather than overlapping ones.\n */\nfunction sampledTicks(\n positions: number[],\n [min, max]: Range,\n divisions: number\n): number[] {\n const inside = positions.filter((v) => v >= min && v <= max)\n const unique = [...new Set(inside)].sort((a, b) => a - b)\n if (unique.length === 0) return markerValues([min, max], divisions)\n\n const nearest = (target: number) =>\n unique.reduce((best, v) =>\n Math.abs(v - target) < Math.abs(best - target) ? v : best\n )\n\n const out: number[] = []\n for (const target of markerValues([min, max], divisions)) {\n const tick = nearest(target)\n if (out[out.length - 1] !== tick) out.push(tick)\n }\n return out\n}\n\n/** The cell of the row a tick came from, or `fallback` when it is not a sample. */\nfunction labelBySample(\n positions: number[],\n captions: string[],\n value: number,\n fallback: (value: number) => string\n): string {\n const i = positions.indexOf(value)\n return i >= 0 ? captions[i] : fallback(value)\n}\n\n/** Resolved scales by data array, then by field and pattern. */\nconst scales = new WeakMap<DataRecord[], Map<string, XScale>>()\n\n/** Kept per `data` array: bound props ask a dozen times a frame, and parsing every row's instant each time was most of a frame. */\nexport function resolveXScale(\n data: DataRecord[],\n field: string,\n pattern?: string\n): XScale {\n let byKey = scales.get(data)\n if (!byKey) scales.set(data, (byKey = new Map()))\n const key = JSON.stringify([field, pattern ?? null])\n let scale = byKey.get(key)\n if (!scale) byKey.set(key, (scale = xScaleOf(data, field, pattern)))\n return scale\n}\n\nfunction xScaleOf(\n data: DataRecord[],\n field: string,\n /** Number-format pattern for a numeric column only; ticks on the other kinds are the author's cells. */\n pattern?: string\n): XScale {\n if (field !== \"\" && isNumericColumn(data, field)) {\n const positions = data.map((row) => num(row, field))\n return {\n kind: \"numeric\",\n at: (i) => positions[i] ?? 0,\n extent: extentOf(positions),\n label: (value) =>\n pattern === undefined ? formatMarker(value) : formatNumber(value, pattern),\n // A continuous axis is read by its scale, not by which samples are on it.\n ticks: markerValues,\n }\n }\n\n const captions = data.map((row) => text(row, field))\n const instants = field === \"\" ? null : instantColumn(data, field)\n\n if (instants) {\n const extent = extentOf(instants)\n const span = extent[1] - extent[0]\n return {\n kind: \"time\",\n at: (i) => instants[i] ?? 0,\n extent,\n label: (value) =>\n labelBySample(instants, captions, value, (v) =>\n formatInstant(v, span)\n ),\n ticks: (range, divisions) => sampledTicks(instants, range, divisions),\n }\n }\n\n const positions = data.map((_, i) => i)\n return {\n kind: \"ordinal\",\n at: (i) => i,\n // `[0, n-1]`, not `[0, n]`: the first and last rows sit on the plot's edges.\n extent: positions.length > 0 ? [0, positions.length - 1] : [0, 1],\n label: (value) => captions[Math.round(value)] ?? \"\",\n ticks: (range, divisions) => sampledTicks(positions, range, divisions),\n }\n}\n", "import {\n Rect,\n Types,\n clamp,\n component2d,\n easeOut,\n type ChildView,\n type DataRecord,\n type NodeOf,\n // Named so this component's emitted type reaches `Node2D` and its props through the sdk, not core.\n type Node2D,\n type Node2DProps as _Node2DProps,\n type Size2D,\n} from \"@motionscript/sdk\"\nimport { formatNumber } from \"@motionscript/sdk/component\"\n\nimport { cartesianFrame, cartesianViews, laidOutSize } from \"../kit/cartesian\"\nimport { ChartBorder } from \"../kit/chart-border\"\nimport { ChartCanvas } from \"../kit/chart-canvas\"\nimport { ChartLegend } from \"../kit/chart-legend\"\nimport { SERIES_ARG, asParts, fresh } from \"../kit/entries\"\nimport { DEFAULT_MARKERS, ENTER_SHARE, LEGEND_SLIDE, beats, type AnyTimeline, type Beat } from \"../kit/motion\"\nimport { VerticalAxis } from \"../kit/vertical-axis\"\nimport {\n formatMarker,\n lerpAxisDomain,\n lerpAxisEntry,\n markerStep,\n markerValues,\n num,\n resolveAxisDomain,\n settleAxisDomain,\n type AxisDomainConfig,\n type Range,\n type ResolvedAxisDomain,\n} from \"../kit/shared\"\nimport { CategoryAxis } from \"./category-axis\"\nimport { ChartBars, type BarGeometry } from \"./chart-bars\"\nimport {\n DEFAULT_BAR_CHART_THEME,\n barCategories,\n barSpans,\n lerpBarChartTheme,\n resolveBarChartTheme,\n resolveBarFill,\n resolveBarScale,\n stackCapIndex,\n type BarChartConfig,\n type BarChartTheme,\n type BarSeries,\n} from \"./shared\"\n\n/** What a bar chart plots, as its entries hold it. */\nexport interface BarChartValues {\n readonly data: DataRecord[]\n readonly series: BarSeries[]\n readonly stacked: boolean\n readonly valueAxis: AxisDomainConfig\n}\n\n/** The value axis's domain with its range and ticks resolved. */\nexport type BarChartDomain = ResolvedAxisDomain\n\ninterface BarState extends BarChartValues {\n readonly theme: BarChartConfig\n readonly weights: number[]\n readonly spotlight: number | null\n readonly spotlightReveal: number\n}\n\nfunction drawnValue(s: BarChartValues, domain: AxisDomainConfig): AxisDomainConfig {\n const divisions = (domain.ticks?.length ?? DEFAULT_MARKERS) - 1\n const range = resolveBarScale(s.data, s.series, s.stacked, domain.range, divisions)\n return { ...domain, range, ticks: domain.ticks ?? markerValues(range, DEFAULT_MARKERS - 1) }\n}\n\n/** The last domain worked out from each data array, and what it was read from. */\nconst domainsDrawn = new WeakMap<DataRecord[], Omit<BarChartValues, \"data\"> & { readonly domain: BarChartDomain }>()\n\n/** The value axis as drawn now. */\nexport function barChartDomain(s: BarChartValues): BarChartDomain {\n const { data, series, stacked, valueAxis } = s\n const kept = domainsDrawn.get(data)\n if (kept && kept.series === series && kept.stacked === stacked && kept.valueAxis === valueAxis) return kept.domain\n const domain = settleAxisDomain(valueAxis, (d) => drawnValue(s, d)) as BarChartDomain\n domainsDrawn.set(data, { series, stacked, valueAxis, domain })\n return domain\n}\n\n/** Ticks and the figures printed on columns both read through here, so they cannot disagree about how a number looks. */\nfunction valueLabel(s: BarChartValues, domain: BarChartDomain, value: number): string {\n const pattern = s.valueAxis.format\n return pattern === undefined ? formatMarker(value, markerStep(domain.ticks)) : formatNumber(value, pattern)\n}\n\n/** A value in the plot's centred pixel space (origin at its centre, y-up). */\nfunction valueToY(domain: BarChartDomain, plot: Size2D, value: number): number {\n const [min, max] = domain.range\n if (max <= min) return -plot.height / 2\n return ((value - min) / (max - min) - 0.5) * plot.height\n}\n\n/** Pixel y columns grow from: zero clamped onto the scale, so a cropped axis grows them from the plot floor. */\nfunction baselinePx(domain: BarChartDomain, plot: Size2D): number {\n const [min, max] = domain.range\n return valueToY(domain, plot, Math.min(Math.max(0, min), max))\n}\n\nfunction weightAt(s: BarState, index: number): number {\n return Math.max(0, s.weights[index] ?? 1)\n}\n\n/** `row` with each series' value scaled by its live weight, which stacked spans are measured from. */\nfunction weightedRow(s: BarState, row: DataRecord): DataRecord {\n const scaled: DataRecord = { ...row }\n s.series.forEach((series, i) => {\n const value = row[series.field]\n if (typeof value === \"number\") scaled[series.field] = value * weightAt(s, i)\n })\n return scaled\n}\n\n/**\n * Series `index`'s columns in centred pixel space. A grouped series divides the\n * slot with its siblings by live weight, a stacked one takes the whole slot, so\n * hiding a series reflows rather than punching a hole.\n */\nfunction seriesBars(s: BarState, index: number, plot: Size2D, captioned: boolean): BarGeometry[] {\n const series = s.series[index]\n if (!series) return []\n const domain = barChartDomain(s)\n const n = s.data.length\n const slotWidth = n > 0 ? plot.width / n : 0\n const slotCenter = (i: number) => -plot.width / 2 + slotWidth * (i + 0.5)\n const inner = slotWidth * clamp(s.theme.bar.slotFill, 0, 1)\n const gap = s.theme.bar.groupGap\n let total = 0\n for (let i = 0; i < s.series.length; i++) total += weightAt(s, i)\n const unit = inner / (total > 0 ? total : 1)\n const weight = weightAt(s, index)\n let before = 0\n for (let i = 0; i < index; i++) before += weightAt(s, i)\n\n return s.data.map((row, rowIndex) => {\n // Built from live contributions, so a fading series lowers the segments above it.\n const spans = barSpans(s.stacked ? weightedRow(s, row) : row, s.series, s.stacked)\n const span = spans[index] ?? { from: 0, to: 0, value: 0 }\n const cap = s.stacked ? stackCapIndex(row, s.series) : index\n const slotLeft = slotCenter(rowIndex) - inner / 2\n const x = s.stacked ? slotCenter(rowIndex) : slotLeft + unit * before + (unit * weight) / 2\n const width = s.stacked ? inner : Math.max(0, unit * weight - (s.series.length > 1 ? gap : 0))\n // The printed figure is the row's own value, not the weighted one.\n const value = num(row, series.field)\n return {\n x,\n width,\n from: valueToY(domain, plot, span.from),\n to: valueToY(domain, plot, span.to),\n round: cap === index,\n value,\n ...(captioned && { caption: valueLabel(s, domain, value) }),\n }\n })\n}\n\n/** Per-row opacity: 1 while nothing is spotlit, falling to the theme's `dimOpacity` for all but the chosen category. */\nfunction categoryDims(s: BarState): number[] {\n const focus = clamp(s.spotlightReveal, 0, 1)\n const dim = s.theme.spotlight.dimOpacity\n const chosen = s.spotlight\n return s.data.map((_, i) => (focus <= 0 || chosen === i ? 1 : 1 - (1 - dim) * focus))\n}\n\n/** The parts a bar chart's commands drive, found through its children. */\nfunction partsOf(children: readonly ChildView[]) {\n const frame = cartesianViews(children)\n if (!frame) return null\n const cAxis = frame.below.find(CategoryAxis.is)\n if (!VerticalAxis.is(frame.axis) || !cAxis) return null\n return {\n vAxis: frame.axis,\n cAxis,\n bars: frame.plot.filter(ChartBars.is),\n legend: frame.below.find(ChartLegend.is) ?? null,\n }\n}\n\n/**\n * Move series `index`'s slot share to `target` with its legend entry. Its\n * columns stay drawn for every frame the move covers and switch off only once a\n * hide has landed, so arriving at a time backwards matches arriving forwards.\n */\nfunction toggling(timeline: AnyTimeline, state: BarState & { readonly showing: boolean[] }, children: readonly ChildView[], index: number, target: number) {\n const parts = partsOf(children)\n if (!parts || !parts.bars[index]) return timeline.wait(timeline.duration)\n // Through `weightAt`, so short stored weights still tween from the 1 drawn.\n const from = state.series.map((_, i) => weightAt(state, i))\n const start = from[index] ?? 1\n const showing = state.series.map((s, i) => state.showing[i] ?? (s.enabled ?? true))\n return timeline.together([\n timeline.animate((t) => {\n const weights = [...from]\n weights[index] = start + (target - start) * timeline.ease(t)\n const shown = [...showing]\n shown[index] = target > 0 || t < 1\n return { weights, showing: shown }\n }, { eased: false }),\n ...(parts.legend ? [parts.legend.run(target > 0 ? \"showSeries\" : \"hideSeries\", { index })] : []),\n ])\n}\n\n/**\n * A grouped or stacked bar chart, built like the line chart from `ChartCanvas`,\n * `VerticalAxis`, {@link CategoryAxis}, one {@link ChartBars} per series and\n * `ChartLegend`. Zero is always on the scale and columns grow about it, so a\n * negative value draws downward from the same `growth` signal.\n *\n * Resting state is the finished chart; `enter` arms every part on its first\n * frame. A `to` of the value axis's `range` or `ticks` tweens from the domain as\n * drawn, so an auto axis rescales the columns continuously.\n */\nexport const BarChart = component2d({\n key: \"barChart\",\n state: {\n data: fresh<DataRecord[]>(() => []),\n // Names a column, not a value, like the line chart's `xField`.\n categoryField: \"\",\n series: fresh<BarSeries[]>(() => []),\n stacked: false,\n valueAxis: Types.custom<AxisDomainConfig>({}, { resolve: resolveAxisDomain, lerp: lerpAxisEntry }),\n categoryAxis: Types.custom<AxisDomainConfig>({}, { resolve: resolveAxisDomain, lerp: lerpAxisDomain }),\n theme: Types.custom<BarChartTheme, BarChartConfig>(DEFAULT_BAR_CHART_THEME, { resolve: resolveBarChartTheme, lerp: lerpBarChartTheme }),\n /** Rise of every column about the baseline, `[0, 1]`: 0 collapses them onto it. */\n growth: 1,\n /** Each series' share of the slot, seeded from `enabled`: a weight rather than a switch, so hiding one widens the others. */\n weights: fresh<number[]>(() => []),\n /** The category spotlit, or `null`. The choice snaps; `spotlightReveal` animates it. */\n spotlight: Types.custom<number | null>(null),\n spotlightReveal: 0,\n /** Read as the chart is built: the legend is a part the chart builds or doesn't. */\n showLegend: Types.boolean(true, { animatable: false }),\n /** Read as the chart is built: whether each column prints the figure it plots. */\n showValues: Types.boolean(false, { animatable: false }),\n /** Whether each series' columns are drawn: on for every frame a show or hide covers, off once a hide lands. */\n showing: Types.custom<boolean[]>([], { private: true }),\n },\n defaults: {\n // A node made of parts that fill would otherwise hug them.\n width: \"fill\",\n height: \"fill\",\n weights: (state) => state.series.map((s) => ((s.enabled ?? true) ? 1 : 0)),\n showing: (state) => state.series.map((s) => s.enabled ?? true),\n },\n allowedChildren: [],\n children: (state) => {\n const theme = () => state.theme\n const domain = () => barChartDomain(state)\n let plot: Node2D | undefined\n const plotSize = () => laidOutSize(plot)\n const captioned = state.showValues === true\n\n const vAxis = new VerticalAxis({\n range: () => domain().range,\n markers: () => domain().ticks,\n captions: () => {\n const d = domain()\n return d.ticks.map((value) => valueLabel(state, d, value))\n },\n axis: () => theme().yAxis,\n label: state.valueAxis.label ?? \"\",\n width: \"hug\",\n height: \"fill\",\n })\n\n const cAxis = new CategoryAxis({\n categories: barCategories(state.data, state.categoryField),\n axis: () => theme().xAxis,\n label: state.categoryAxis.label ?? \"\",\n dims: () => categoryDims(state),\n width: \"fill\",\n height: \"hug\",\n }) as unknown as Node2D\n\n const barSets = state.series.map(\n (s, i) =>\n new ChartBars({\n bars: () => seriesBars(state, i, plotSize(), captioned),\n baseline: () => baselinePx(domain(), plotSize()),\n fill: () => resolveBarFill(theme().seriesFills, s, i),\n barStroke: () => theme().bar.stroke,\n barShadow: () => theme().bar.shadow,\n cornerRadius: () => theme().bar.cornerRadius,\n growth: () => state.growth,\n dims: () => categoryDims(state),\n showValues: captioned,\n valueStyle: state.theme.valueStyle,\n enabled: () => state.showing[i] ?? (s.enabled ?? true),\n }) as unknown as Node2D\n )\n\n const below: Node2D[] = [cAxis]\n if (state.showLegend !== false) {\n below.push(\n new ChartLegend({\n series: state.series,\n swatches: () => state.series.map((s, i) => resolveBarFill(theme().seriesFills, s, i)),\n legend: () => theme().legend,\n // Spacing is fixed as built; a theme tween re-paints the swatches only.\n gap: state.theme.legend.itemGap,\n padding: state.theme.legend.padding,\n }) as unknown as Node2D\n )\n }\n\n plot = new Rect({\n clip: true,\n children: [\n new ChartCanvas({\n fill: () => theme().plotArea.fill,\n shadow: () => theme().plotArea.shadow,\n xGridStroke: () => theme().xAxis.lineStyle,\n yGridStroke: () => theme().yAxis.lineStyle,\n // Categorical x axis: no numeric position for a vertical rule to mark.\n xMarkers: [],\n yMarkers: () => domain().ticks,\n xExtent: () => [0, 1] as Range,\n yExtent: () => domain().range,\n }),\n ...barSets,\n new ChartBorder({ stroke: () => theme().plotArea.stroke }),\n ],\n }) as unknown as Node2D\n\n const gutter = new VerticalAxis({\n opacity: 0,\n range: () => domain().range,\n markers: () => domain().ticks,\n axis: () => theme().yAxis,\n label: state.valueAxis.label ?? \"\",\n width: \"hug\",\n height: 0,\n }) as unknown as Node2D\n\n return asParts([cartesianFrame({ axis: vAxis as unknown as Node2D, plot, gutter, below })])\n },\n commands: {\n /** Axes stagger in, columns rise from the baseline, the legend fades up last. `duration` is the whole entrance. */\n enter: {\n args: {},\n // Held from arrival, so it never shows finished ahead of its draw.\n entrance: true,\n easing: easeOut(),\n run: (timeline, _state, children) => {\n const parts = partsOf(children)\n if (!parts) return timeline.wait(timeline.duration)\n const duration = timeline.duration\n const axes = duration * ENTER_SHARE.axes\n const rise = duration * ENTER_SHARE.marks\n const { legend } = parts\n const at0 = (piece: Beat[\"piece\"]): Beat => ({ at: 0, duration: 0, piece })\n return beats(timeline, [\n at0(timeline.animate(() => ({ growth: 0 }))),\n ...(legend ? [at0(legend.to({ opacity: 0, y: -LEGEND_SLIDE }))] : []),\n { at: 0, duration: axes, piece: parts.vAxis.run(\"enter\", { slide: 24 }) },\n { at: 0, duration: axes, piece: parts.cAxis.run(\"enter\", { slide: 24 }) },\n // No priming to 0 first: the value at `t` is a function of `t`.\n { at: axes, duration: rise, piece: timeline.animate((t) => ({ growth: t })) },\n ...(legend ? [{ at: axes + rise, duration: duration * ENTER_SHARE.legend, piece: legend.to({ opacity: 1, y: 0 }) }] : []),\n ])\n },\n },\n /** Bring series `index` back, the group reflowing to make room, with its legend entry. */\n showSeries: {\n args: { seriesIndex: SERIES_ARG },\n easing: easeOut(),\n run: (timeline, state, children) => toggling(timeline, state, children, timeline.args.seriesIndex, 1),\n },\n /** The mirror of `showSeries`: columns narrow away (stacked, sink out) while the rest widen into the space. */\n hideSeries: {\n args: { seriesIndex: SERIES_ARG },\n easing: easeOut(),\n run: (timeline, state, children) => toggling(timeline, state, children, timeline.args.seriesIndex, 0),\n },\n /** Fade every other series, columns and legend entry, to focus on `index`. Nothing moves, so the group does not reflow. */\n spotlightSeries: {\n args: { seriesIndex: SERIES_ARG },\n easing: easeOut(),\n run: (timeline, state, children) => {\n const parts = partsOf(children)\n if (!parts) return timeline.wait(timeline.duration)\n const index = timeline.args.seriesIndex\n const dim = state.theme.spotlight.dimOpacity\n return timeline.together([\n ...parts.bars.map((bars, i) => bars.to({ opacity: i === index ? 1 : dim })),\n ...(parts.legend ? [parts.legend.run(\"spotlightSeries\", { index })] : []),\n ])\n },\n },\n /**\n * Fade every other category's columns and caption to `dimOpacity`. While\n * another is lit the focus moves directly. A missing category takes its slot\n * and changes nothing.\n */\n spotlightCategory: {\n args: { categoryIndex: Types.number(0, { min: 0, step: 1 }) },\n easing: easeOut(),\n run: (timeline, state) => {\n const index = timeline.args.categoryIndex\n if (index < 0 || index >= state.data.length) return timeline.wait(timeline.duration)\n const from = state.spotlightReveal\n return timeline.animate((t) => ({ spotlight: index, spotlightReveal: from + (1 - from) * t }))\n },\n },\n /** Every category back to full; the lit one is held until the fade lands. A no-op when nothing is lit, which still takes its slot. */\n clearCategory: {\n args: {},\n easing: easeOut(),\n run: (timeline, state) => {\n const from = state.spotlightReveal\n if (from === 0) return timeline.wait(timeline.duration)\n const lit = state.spotlight\n return timeline.animate((t) => (t >= 1 ? { spotlight: null, spotlightReveal: 0 } : { spotlight: lit, spotlightReveal: from * (1 - t) }))\n },\n },\n },\n})\n\nexport type BarChart = NodeOf<typeof BarChart>\n", "import {\n Rect,\n Text,\n Types,\n clamp,\n component2d,\n insetsOps,\n textOps,\n type NodeOf,\n // Named so this component's emitted type reaches `Node2D` and its props through the sdk, not core.\n type Node2D,\n type Node2DProps as _Node2DProps,\n} from \"@motionscript/sdk\"\nimport { enterBudget } from \"@motionscript/sdk/component\"\n\nimport { asParts, fresh } from \"../kit/entries\"\nimport { armed, arrival, arrivalOf, arrivalsEntry, beats, frameEase, type Arrival } from \"../kit/motion\"\nimport {\n DEFAULT_X_AXIS,\n type AxisConfig,\n} from \"../kit/shared\"\n\nconst SLIDE = 24\n\n/**\n * The categorical axis of a {@link BarChart}: one {@link Text} caption per row,\n * centred on its slot. Not folded into `HorizontalAxis`, which places markers by\n * value on a scale; this divides its width into equal slots. It is laid out to\n * exactly the plot's width, so each caption lands under its columns with neither\n * side told about the other. The resting state is landed.\n */\nexport const CategoryAxis = component2d({\n key: \"categoryAxis\",\n state: {\n categories: fresh<string[]>(() => []),\n axis: Types.custom<AxisConfig>(DEFAULT_X_AXIS),\n label: \"\",\n /** Per-category opacity multiplier, so the caption under a dimmed column dims with it. Missing reads as 1. */\n dims: fresh<number[]>(() => []),\n arrivals: arrivalsEntry(),\n },\n // A node made of captions would otherwise hug them.\n defaults: { width: \"fill\", height: \"fill\" },\n children: (state) => {\n const tickPad = insetsOps.resolve(state.axis.tickPadding).top\n const titlePad = insetsOps.resolve(state.axis.titlePadding).top\n\n const column: Node2D[] = []\n // The column's parent is the axis: its box is what the slots divide.\n const width = () => (column[0]?.parent as Node2D | null | undefined)?.layoutBounds.width ?? 0\n const slotCenter = (index: number) => {\n const n = state.categories.length\n if (n <= 0) return 0\n return -width() / 2 + (width() / n) * (index + 0.5)\n }\n\n // The spotlight dim sits on a hug box around each caption, so the entrance animates the text's own opacity without the two fighting over one value.\n const slots = state.categories.map((caption, i) =>\n new Rect({\n width: \"hug\",\n height: \"hug\",\n flow: \"freeform\",\n opacity: () => clamp(state.dims[i] ?? 1, 0, 1),\n x: () => slotCenter(i),\n children: [\n new Text({\n ...textOps.styleProps(state.axis.labelStyle),\n text: caption,\n opacity: () => arrivalOf(state.arrivals, i).opacity,\n y: () => arrivalOf(state.arrivals, i).shift,\n }),\n ],\n })\n )\n\n const children: Node2D[] = [new Rect({ width: \"fill\", height: \"hug\", padding: { top: tickPad }, children: slots })]\n if (state.label !== \"\") {\n children.push(\n new Text({\n ...textOps.styleProps(state.axis.titleStyle),\n text: state.label,\n opacity: () => arrivalOf(state.arrivals, \"label\").opacity,\n y: () => arrivalOf(state.arrivals, \"label\").shift,\n })\n )\n }\n\n column.push(new Rect({ flow: \"vertical\", gap: titlePad, height: \"hug\", width: \"fill\", children }))\n return asParts(column)\n },\n commands: {\n /**\n * Slide and fade captions in left to right, then the title. The duration is\n * the whole entrance ({@link enterBudget}). `stagger` is seconds between\n * captions (derived when omitted); `slide` is the starting offset in px.\n */\n enter: {\n args: {\n stagger: Types.custom<number | undefined>(undefined),\n slide: Types.number(SLIDE),\n },\n run: (timeline, state) => {\n const { stagger, slide } = timeline.args\n const count = state.categories.length\n const captioned = state.label !== \"\"\n const from: Arrival = { opacity: 0, shift: slide }\n const keys = [...state.categories.keys(), ...(captioned ? [\"label\"] : [])]\n const arm = timeline.animate(() => ({ arrivals: armed(keys, from) }))\n if (count === 0) return arm\n const { step, per, caption } = enterBudget(timeline.duration, count, captioned, stagger)\n const ease = frameEase(timeline)\n const lastCaptionEnds = (count - 1) * step + per\n const captions = state.categories.map((_, i) => ({ at: i * step, duration: per, piece: arrival(timeline, i, from, ease) }))\n const label = captioned ? [{ at: lastCaptionEnds, duration: caption, piece: arrival(timeline, \"label\", from, ease) }] : []\n return timeline.together([arm, beats(timeline, [...captions, ...label])])\n },\n },\n },\n})\n\nexport type CategoryAxis = NodeOf<typeof CategoryAxis>\n", "import {\n Graphics2D,\n Text,\n Types,\n clamp,\n component2d,\n fillOps,\n shadowOps,\n strokeOps,\n textOps,\n type Fill,\n type FillResolved,\n type NodeOf,\n // Named so this component's emitted type reaches `Node2D` and its props through the sdk, not core.\n type Node2D as _Node2D,\n type Node2DProps as _Node2DProps,\n type Shadow,\n type ShadowResolved,\n type Stroke,\n type StrokeResolved,\n type TextStyle,\n} from \"@motionscript/sdk\"\n\nimport { fresh } from \"../kit/entries\"\nimport { frameEase } from \"../kit/motion\"\nimport { loadPaints } from \"../kit/paints\"\nimport { formatMarker } from \"../kit/shared\"\nimport { DEFAULT_BAR_CHART_THEME } from \"./shared\"\n\n/**\n * One drawn column in the plot's centred pixel space (origin at the centre,\n * y-up), mapped by the parent {@link BarChart}. `from`/`to` are the two ends\n * rather than a base and a height, like {@link BarSpan}: one shape fits grouped,\n * stacked and negative.\n */\nexport interface BarGeometry {\n /** Centre x. */\n x: number\n width: number\n /** Pixel y of the base: the baseline when grouped, the running stack top when stacked. */\n from: number\n /** Pixel y of the far end at full growth. */\n to: number\n /** Whether the far end takes the corner radius (the stack's cap). */\n round: boolean\n /** The value plotted, for the printed figure. */\n value: number\n /** The printed figure, written as the value axis writes its ticks; absent, the bare marker reading of `value`. */\n caption?: string\n}\n\n/** Gap between a grouped column's end and the figure printed over it, in px. */\nconst VALUE_GAP = 10\n/** A segment shorter than this many multiples of the type size prints no figure. */\nconst VALUE_FIT = 1.6\n/** Fallback type size when the theme's value style leaves it unset or autofit. */\nconst VALUE_SIZE = 24\n\ninterface Columns {\n readonly bars: readonly BarGeometry[]\n readonly baseline: number\n readonly growth: number\n readonly dims: readonly number[]\n readonly enabled: boolean\n readonly valueStyle: TextStyle\n}\n\n/**\n * Column `i`'s dimming: the category spotlight, then the series switch. Captions\n * read this because they are child nodes, which a `render` guard would leave on\n * screen over a series that is not there.\n */\nfunction dimAt(s: Columns, index: number): number {\n if (!s.enabled) return 0\n return clamp(s.dims[index] ?? 1, 0, 1)\n}\n\nfunction endsAt(s: Columns, index: number): { base: number; end: number } | null {\n const bar = s.bars[index]\n if (!bar) return null\n const g = clamp(s.growth, 0, 1)\n return { base: s.baseline + (bar.from - s.baseline) * g, end: s.baseline + (bar.to - s.baseline) * g }\n}\n\nfunction valueSize(s: Columns): number {\n const size = s.valueStyle.fontSize\n return typeof size === \"number\" ? size : VALUE_SIZE\n}\n\nfunction insideAt(s: Columns, index: number): boolean {\n const bar = s.bars[index]\n return bar !== undefined && Math.abs(bar.from - s.baseline) > 0.5\n}\n\n/** Column `i`'s figure, or `\"\"` where there is no room. Blanking rather than dropping the node keeps captions index-aligned, so a rescale into more room prints again. */\nfunction captionAt(s: Columns, index: number): string {\n const bar = s.bars[index]\n if (!bar || bar.value === 0) return \"\"\n // An interior segment prints inside itself and must hold a line of type; a grouped column prints past its end.\n if (insideAt(s, index)) {\n const ends = endsAt(s, index)\n if (!ends) return \"\"\n if (Math.abs(ends.end - ends.base) < valueSize(s) * VALUE_FIT) return \"\"\n }\n return bar.caption ?? formatMarker(bar.value)\n}\n\nfunction captionY(s: Columns, index: number): number {\n const ends = endsAt(s, index)\n if (!ends) return 0\n if (insideAt(s, index)) return (ends.base + ends.end) / 2\n // Text is centred on `y`: clear the end by the gap plus half a line, on the side the column reached.\n const direction = ends.end >= ends.base ? 1 : -1\n return ends.end + direction * (VALUE_GAP + valueSize(s) / 2)\n}\n\n/**\n * One series of a {@link BarChart}: its column in every category plus the\n * printed figures. A node per series, like `ChartLine`, so `showSeries` and\n * `spotlightSeries` mean the same on both: enable the node or tween its opacity.\n * Positions come from the parent, which owns the slot arithmetic.\n *\n * The figures are {@link Text} children, so they take the theme's type style,\n * are measured by layout, and hide with the series.\n */\nexport const ChartBars = component2d({\n key: \"chartBars\",\n state: {\n bars: fresh<BarGeometry[]>(() => []),\n /** Pixel y of value 0, where every column grows from. */\n baseline: 0,\n fill: Types.custom<Fill, FillResolved[]>([], { resolve: fillOps.resolve, lerp: fillOps.lerp }),\n barStroke: Types.custom<Stroke, StrokeResolved[]>(DEFAULT_BAR_CHART_THEME.bar.stroke, { resolve: strokeOps.resolve, lerp: strokeOps.lerp }),\n barShadow: Types.custom<Shadow, ShadowResolved[]>(DEFAULT_BAR_CHART_THEME.bar.shadow, { resolve: shadowOps.resolve, lerp: shadowOps.lerp }),\n cornerRadius: DEFAULT_BAR_CHART_THEME.bar.cornerRadius,\n /**\n * Rise of every column in `[0, 1]` about the baseline, not each column's own\n * base: every segment travels by the same factor, so a stack stays flush\n * mid-animation and a negative column grows down.\n */\n growth: 1,\n /** Per-column opacity by row index, how the category spotlight reaches one column. Missing reads as 1. */\n dims: fresh<number[]>(() => []),\n showValues: false,\n valueStyle: Types.custom<TextStyle>(DEFAULT_BAR_CHART_THEME.valueStyle),\n /** The wholesale switch behind `showSeries`/`hideSeries`; off skips the columns and their figures. */\n enabled: true,\n },\n // A node made of its figures would otherwise hug them.\n defaults: { width: \"fill\", height: \"fill\" },\n // One caption per column, built once (the row count is fixed at construction)\n // and bound to the live geometry so it travels with its column.\n children: (state) => {\n if (!state.showValues) return []\n return state.bars.map((_, i) =>\n Text({\n ...textOps.styleProps(state.valueStyle),\n text: () => captionAt(state, i),\n x: () => state.bars[i]?.x ?? 0,\n y: () => captionY(state, i),\n opacity: () => dimAt(state, i),\n })\n )\n },\n // The stroke and shadow sit past the conventional slots, so they are named here.\n load: (loader, state) =>\n loadPaints(loader, { fills: [state.fill], shadows: [state.barShadow], strokes: [state.barStroke] }),\n commands: {\n /** Raise every column from the baseline (`growth` 0 to 1). The resting state is the finished one; the chart arms it back to 0. */\n enter: (timeline) => {\n const ease = frameEase(timeline)\n return timeline.animate((t) => ({ growth: ease(t) }))\n },\n },\n render: (ctx, state) => {\n if (state.enabled) {\n for (let i = 0; i < state.bars.length; i++) {\n const bar = state.bars[i]\n const ends = endsAt(state, i)\n if (!ends || bar.width <= 0) continue\n const height = Math.abs(ends.end - ends.base)\n if (height <= 0) continue\n\n const radius = bar.round ? Math.min(state.cornerRadius, height) : 0\n // One `Graphics2D` per column: opacity belongs to the command list, and the spotlight dims single columns.\n const g = new Graphics2D()\n const rect = g.rect({\n x: bar.x,\n y: (ends.base + ends.end) / 2,\n width: bar.width,\n height,\n // Corner keys are visual (y-up): a column reaching up rounds `top*`, one reaching down `bottom*`. Only the far end rounds.\n cornerRadius:\n radius > 0\n ? ends.end >= ends.base\n ? { topLeft: radius, topRight: radius, bottomLeft: 0, bottomRight: 0 }\n : { bottomLeft: radius, bottomRight: radius, topLeft: 0, topRight: 0 }\n : 0,\n })\n rect.shadow(state.barShadow)\n rect.fill(state.fill)\n rect.stroke(state.barStroke)\n ctx.draw(g.opacity(dimAt(state, i)))\n }\n }\n ctx.children()\n },\n})\n\nexport type ChartBars = NodeOf<typeof ChartBars>\n", "import {\n fillOps,\n lerpNumber,\n shadowOps,\n strokeOps,\n textOps,\n type DataRecord,\n type Fill,\n type Shadow,\n type ShadowResolved,\n type Stroke,\n type StrokeResolved,\n type TextStyle,\n} from \"@motionscript/sdk\"\nimport { niceStep } from \"@motionscript/sdk/component\"\n\nimport {\n CHART_PALETTE,\n DEFAULT_LEGEND,\n DEFAULT_PLOT_AREA,\n DEFAULT_SERIES_FILLS,\n DEFAULT_X_AXIS,\n DEFAULT_Y_AXIS,\n SPOTLIGHT_DIM,\n lerpAxisConfig,\n lerpLegendConfig,\n lerpPlotArea,\n lerpSeriesFills,\n num,\n resolveAxisConfig,\n resolveLegendConfig,\n resolvePlotArea,\n resolveSeriesFills,\n text,\n type AxisConfig,\n type ChartSeries,\n type LegendConfig,\n type LegendTheme,\n type PlotAreaConfig,\n type Range,\n} from \"../kit/shared\"\n\n/** One bar per row, beside its siblings in the category slot or stacked on them: {@link ChartSeries} under the name for what it draws here. */\nexport type BarSeries = ChartSeries\n\n/**\n * One bar's span in data units. A span rather than a height because both modes\n * fit it, and a negative value just runs the other way with no special case.\n */\nexport interface BarSpan {\n /** Where the base sits: 0 when grouped, the running total below when stacked. */\n from: number\n to: number\n /** `to - from`, kept for the figure printed on the bar. */\n value: number\n}\n\n/**\n * One row's spans, in series order. Stacking is a running sum in listed order,\n * so series 0 sits on the baseline. A zero value still yields a degenerate span,\n * keeping the list index-aligned with the series.\n */\nexport function barSpans(\n row: DataRecord,\n series: BarSeries[],\n stacked: boolean\n): BarSpan[] {\n let running = 0\n return series.map((s) => {\n const value = num(row, s.field)\n if (!stacked) return { from: 0, to: value, value }\n const from = running\n running += value\n return { from, to: running, value }\n })\n}\n\n/**\n * The series ending a row's stack, which gets the rounded cap; `-1` when the row\n * is all zero. It is the last non-zero series, so a zero one does not take the\n * cap and leave the segment below square.\n */\nexport function stackCapIndex(row: DataRecord, series: BarSeries[]): number {\n let last = -1\n for (let i = 0; i < series.length; i++) {\n if (num(row, series[i].field) !== 0) last = i\n }\n return last\n}\n\nexport function barCategories(data: DataRecord[], field: string): string[] {\n return data.map((row) => text(row, field))\n}\n\n/**\n * The `[min, max]` of everything drawn, zero always included: a scale starting\n * at 40 draws a column three times its neighbour for a 10% difference. A cropped\n * axis takes an explicit range.\n */\nexport function barValueExtent(\n data: DataRecord[],\n series: BarSeries[],\n stacked: boolean\n): Range {\n let lo = 0\n let hi = 0\n for (const row of data) {\n for (const span of barSpans(row, series, stacked)) {\n lo = Math.min(lo, span.from, span.to)\n hi = Math.max(hi, span.from, span.to)\n }\n }\n return lo < hi ? [lo, hi] : [0, 1]\n}\n\n/** The explicit `range`, else the data's extent nudged to tidy bounds; `divisions` (one fewer than the marker count) makes the drawn ticks land on them. */\nexport function resolveBarScale(\n data: DataRecord[],\n series: BarSeries[],\n stacked: boolean,\n range: Range | undefined,\n divisions: number\n): Range {\n if (range) return range\n const [lo, hi] = barValueExtent(data, series, stacked)\n const segments = Math.max(1, Math.round(divisions))\n const step = niceStep((hi - lo) / segments || 1)\n return [Math.floor(lo / step) * step, Math.ceil(hi / step) * step]\n}\n\n/** How the columns are drawn and the slot divided. */\nexport interface BarStyleConfig {\n /** Radius in px of a column's far-end corners; an interior stacked segment stays square. */\n cornerRadius: number\n /** Fraction of the category slot the bars fill, `[0, 1]`; the rest separates groups. */\n slotFill: number\n /** Gap in px between grouped bars within a slot; ignored when stacked. */\n groupGap: number\n stroke: Stroke\n shadow: Shadow\n}\n\n/** A {@link BarChart}'s full, resolved visual theme. */\nexport interface BarChartConfig {\n /** Cycled by series index; a series' own `fill` replaces its entry outright. */\n seriesFills: Fill[]\n bar: BarStyleConfig\n /** The category axis. */\n xAxis: AxisConfig\n /** The value axis. */\n yAxis: AxisConfig\n plotArea: PlotAreaConfig\n legend: LegendConfig\n /** The figure printed on each column. */\n valueStyle: TextStyle\n /** How far everything outside a spotlit series or category fades. */\n spotlight: { dimOpacity: number }\n}\n\n/** The author's partial theme, deep-merged onto {@link DEFAULT_BAR_CHART_THEME}. */\nexport type BarChartTheme = {\n seriesFills?: Fill[]\n bar?: Partial<BarStyleConfig>\n xAxis?: Partial<AxisConfig>\n yAxis?: Partial<AxisConfig>\n plotArea?: Partial<PlotAreaConfig>\n legend?: LegendTheme\n valueStyle?: TextStyle\n spotlight?: Partial<BarChartConfig[\"spotlight\"]>\n}\n\nexport const DEFAULT_BAR_CHART_THEME: BarChartConfig = {\n seriesFills: DEFAULT_SERIES_FILLS,\n bar: {\n cornerRadius: 6,\n slotFill: 0.72,\n groupGap: 6,\n stroke: [],\n shadow: [],\n },\n // Captions rather than numeric markers: less weight than the line chart's ticks.\n xAxis: {\n ...DEFAULT_X_AXIS,\n labelStyle: { ...DEFAULT_X_AXIS.labelStyle, fontWeight: 600 },\n tickPadding: 16,\n },\n yAxis: DEFAULT_Y_AXIS,\n plotArea: DEFAULT_PLOT_AREA,\n legend: DEFAULT_LEGEND,\n valueStyle: {\n fontSize: 24,\n fontWeight: 700,\n textAlign: \"center\",\n fill: CHART_PALETTE.markerColor,\n },\n spotlight: { dimOpacity: SPOTLIGHT_DIM },\n}\n\nfunction resolveBarStyle(\n bar: Partial<BarStyleConfig> | undefined,\n previous: BarStyleConfig\n): BarStyleConfig {\n return {\n cornerRadius: bar?.cornerRadius ?? previous.cornerRadius,\n slotFill: bar?.slotFill ?? previous.slotFill,\n groupGap: bar?.groupGap ?? previous.groupGap,\n stroke: strokeOps.resolve(bar?.stroke ?? previous.stroke),\n shadow: shadowOps.resolve(bar?.shadow ?? previous.shadow),\n }\n}\n\nfunction lerpBarStyle(\n from: BarStyleConfig,\n to: BarStyleConfig,\n t: number\n): BarStyleConfig {\n return {\n cornerRadius: lerpNumber(from.cornerRadius, to.cornerRadius, t),\n slotFill: lerpNumber(from.slotFill, to.slotFill, t),\n groupGap: lerpNumber(from.groupGap, to.groupGap, t),\n stroke: strokeOps.lerp(\n from.stroke as StrokeResolved[],\n to.stroke as StrokeResolved[],\n t\n ),\n shadow: shadowOps.lerp(\n from.shadow as ShadowResolved[],\n to.shadow as ShadowResolved[],\n t\n ),\n }\n}\n\n/** The `theme` entry's resolve: merges a partial {@link BarChartTheme} onto `previous` (first resolution: the default). */\nexport function resolveBarChartTheme(\n theme: BarChartTheme | undefined,\n previous?: BarChartConfig\n): BarChartConfig {\n const base = previous ?? DEFAULT_BAR_CHART_THEME\n return {\n seriesFills: resolveSeriesFills(theme?.seriesFills, base.seriesFills),\n bar: resolveBarStyle(theme?.bar, base.bar),\n xAxis: resolveAxisConfig(theme?.xAxis, base.xAxis),\n yAxis: resolveAxisConfig(theme?.yAxis, base.yAxis),\n plotArea: resolvePlotArea(theme?.plotArea, base.plotArea),\n legend: resolveLegendConfig(theme?.legend, base.legend),\n valueStyle: textOps.resolveStyle(theme?.valueStyle, base.valueStyle),\n spotlight: {\n dimOpacity: theme?.spotlight?.dimOpacity ?? base.spotlight.dimOpacity,\n },\n }\n}\n\nexport function lerpBarChartTheme(\n from: BarChartConfig,\n to: BarChartConfig,\n t: number\n): BarChartConfig {\n return {\n seriesFills: lerpSeriesFills(from.seriesFills, to.seriesFills, t),\n bar: lerpBarStyle(from.bar, to.bar, t),\n xAxis: lerpAxisConfig(from.xAxis, to.xAxis, t),\n yAxis: lerpAxisConfig(from.yAxis, to.yAxis, t),\n plotArea: lerpPlotArea(from.plotArea, to.plotArea, t),\n legend: lerpLegendConfig(from.legend, to.legend, t),\n valueStyle: textOps.lerpStyle(from.valueStyle, to.valueStyle, t),\n spotlight: {\n dimOpacity: lerpNumber(\n from.spotlight.dimOpacity,\n to.spotlight.dimOpacity,\n t\n ),\n },\n }\n}\n\n/** Series `index`'s own `fill`, else the palette's entry. */\nexport function resolveBarFill(\n palette: Fill[],\n series: BarSeries,\n index: number\n): Fill {\n if (series.fill != null) return fillOps.resolve(series.fill)\n if (palette.length === 0) return \"white\"\n return palette[index % palette.length]\n}\n", "import {\n Rect,\n Types,\n clamp,\n component2d,\n easeOut,\n type ChildView,\n type DataRecord,\n type NodeOf,\n // Named so this component's emitted type reaches `Node2D` and its props through the sdk, not core.\n type Node2D,\n type Node2DProps as _Node2DProps,\n} from \"@motionscript/sdk\"\n\nimport { asParts, fresh } from \"../kit/entries\"\nimport { beats, type AnyTimeline, type Beat } from \"../kit/motion\"\nimport { PieLegend } from \"./pie-legend\"\nimport { PieRing } from \"./pie-ring\"\nimport {\n DEFAULT_PIE_CHART_THEME,\n lerpPieChartTheme,\n resolvePieChartTheme,\n resolvePieSlices,\n selectionFade,\n type PieChartConfig,\n type PieChartTheme,\n type PieLabelMode,\n type PieLabelPlacement,\n type PieSlice,\n} from \"./shared\"\n\nexport interface PieChartValues {\n readonly data: DataRecord[]\n readonly categoryField: string\n readonly valueField: string\n readonly theme: PieChartConfig\n readonly pops: number[]\n}\n\n/** How `enter` divides its duration: wedges sweeping in, then labels and legend together. Sums to 1. */\nconst SWEEP_SHARE = 0.65\nconst LABEL_SHARE = 0.35\n\nconst SLICE_ARG = Types.number(0, { min: 0, step: 1 })\n\nfunction slicesOf(s: PieChartValues): PieSlice[] {\n return resolvePieSlices(s.data, s.categoryField, s.valueField, s.theme.sliceFills)\n}\n\nfunction popAt(s: PieChartValues, index: number): number {\n return clamp(s.pops[index] ?? 0, 0, 1)\n}\n\n/** Whether slice `index` is past half way out, so a slice mid-swell is not yet counted. */\nexport function isSliceSelected(s: PieChartValues, index: number): boolean {\n return popAt(s, index) > 0.5\n}\n\n/** Per-slice opacity: 1 while nothing is out and for the selected slice, falling toward `dimOpacity` for the rest as the selection grows. */\nfunction sliceDims(s: PieChartValues): number[] {\n let focus = 0\n for (let i = 0; i < s.data.length; i++) focus = Math.max(focus, popAt(s, i))\n const dim = s.theme.spotlight.dimOpacity\n return s.data.map((_, i) => selectionFade(focus, dim, popAt(s, i)))\n}\n\n/** Every slice's pop eased to what `pop` asks for. A move that changes nothing still takes its slot. */\nfunction popping(timeline: AnyTimeline, s: PieChartValues, pop: (index: number) => number) {\n const from = s.data.map((_, i) => popAt(s, i))\n const targets = s.data.map((_, i) => clamp(pop(i), 0, 1))\n if (from.every((value, i) => value === targets[i])) return timeline.wait(timeline.duration)\n return timeline.animate((t) => ({ pops: from.map((value, i) => value + (targets[i] - value) * t) }))\n}\n\n/** The ring and legend among the children; the last row is the one the chart built. */\nfunction partsOf(children: readonly ChildView[]) {\n for (let i = children.length - 1; i >= 0; i--) {\n const row = children[i]\n const ring = Rect.is(row) ? row.children.find(PieRing.is) : undefined\n if (ring) return { ring, legend: row.children.find(PieLegend.is) ?? null }\n }\n return null\n}\n\n/**\n * A pie or donut chart: a {@link PieRing} filling what the hugging {@link PieLegend}\n * leaves. One mark per row, not per column, and nothing to hide, since dropping\n * a slice would re-cut every other wedge; `selectSlice` and its siblings swell\n * a slice and dim the rest instead.\n *\n * Resting state is the finished chart, except the hole's summary, which\n * `showCenter: false` holds back for `revealCenter`.\n */\nexport const PieChart = component2d({\n key: \"pieChart\",\n state: {\n data: fresh<DataRecord[]>(() => []),\n /** Slice caption, used on the labels and the legend. */\n categoryField: \"\",\n /** Slice magnitude: the wedge's share of the ring. */\n valueField: \"\",\n theme: Types.custom<PieChartTheme, PieChartConfig>(DEFAULT_PIE_CHART_THEME, { resolve: resolvePieChartTheme, lerp: lerpPieChartTheme }),\n /** What the ring's labels say. */\n labelMode: Types.custom<PieLabelMode>(\"percent\"),\n /** Where they sit: inside their wedge, outside on a leader, or nowhere. */\n labelPlacement: Types.custom<PieLabelPlacement>(\"leader\"),\n /** Gap between the ring and the legend, in px. */\n gap: 40,\n /** Text set in the ring's hole. */\n centerLabel: \"\",\n /** Smaller second line under `centerLabel`. */\n centerCaption: \"\",\n /** How far each slice is pushed out, `0`-`1` per index. Here, not on the ring, because the wedges and the legend both read it. */\n pops: fresh<number[]>(() => []),\n /** Read as the chart is built: the legend is a part the chart builds or doesn't. */\n showLegend: Types.boolean(true, { animatable: false }),\n /** Read as the chart is built: whether the hole's summary is drawn at rest. A scene that wants it to land later sets `false` and runs `revealCenter`. */\n showCenter: Types.boolean(true, { animatable: false }),\n },\n defaults: {\n // A node made of parts that fill would otherwise hug them.\n width: \"fill\",\n height: \"fill\",\n pops: (state) => slicesOf(state).map(() => 0),\n },\n allowedChildren: [],\n // The structure (a label and legend row per row) is read once; slices, paint and selection are bound, so a theme tween re-paints and a `set` of `data` re-cuts.\n children: (state) => {\n const theme = () => state.theme\n const slices = () => slicesOf(state)\n const parts: Node2D[] = [\n new PieRing({\n width: \"fill\",\n height: \"fill\",\n slices,\n ring: () => theme().ring,\n label: () => theme().label,\n centerStyle: () => theme().center,\n labelMode: state.labelMode,\n labelPlacement: state.labelPlacement,\n pops: () => state.pops,\n dimOpacity: () => theme().spotlight.dimOpacity,\n centerLabel: state.centerLabel,\n centerCaption: state.centerCaption,\n centerReveal: state.showCenter === false ? 0 : 1,\n }) as unknown as Node2D,\n ]\n if (state.showLegend !== false) {\n parts.push(\n new PieLegend({\n slices,\n legend: () => theme().legend,\n dims: () => sliceDims(state),\n // Spacing is fixed as built.\n gap: state.theme.legend.itemGap / 3,\n padding: state.theme.legend.padding,\n }) as unknown as Node2D\n )\n }\n const row = new Rect({ flow: \"horizontal\", width: \"fill\", height: \"fill\", gap: state.gap, align: \"center\", children: parts })\n return asParts([row as unknown as Node2D])\n },\n commands: {\n /** Wedges sweep in clockwise from `startAngle`, then labels fade up and the legend staggers in. `duration` is the whole entrance; the hole's summary keeps its own signal. */\n enter: {\n args: {},\n // Held from arrival, so it never shows finished ahead of its draw.\n entrance: true,\n easing: easeOut(),\n run: (timeline, _state, children) => {\n const parts = partsOf(children)\n if (!parts) return timeline.wait(timeline.duration)\n const { ring, legend } = parts\n const sweep = timeline.duration * SWEEP_SHARE\n const labels = timeline.duration * LABEL_SHARE\n const at0 = (piece: Beat[\"piece\"]): Beat => ({ at: 0, duration: 0, piece })\n return beats(timeline, [\n at0(ring.to({ growth: 0, labelReveal: 0 })),\n ...(legend ? [at0(legend.run(\"arm\"))] : []),\n { at: 0, duration: sweep, piece: ring.run(\"sweep\") },\n { at: sweep, duration: labels, piece: ring.run(\"revealLabels\") },\n ...(legend ? [{ at: sweep, duration: labels, piece: legend.run(\"enter\") }] : []),\n ])\n },\n },\n /** Fade the hole's summary up. Not part of `enter`: it is usually the conclusion, which a scene lands on its own beat. */\n revealCenter: {\n args: {},\n easing: easeOut(),\n run: (timeline, _state, children) => partsOf(children)?.ring.run(\"revealCenter\") ?? timeline.wait(timeline.duration),\n },\n /** Grow slice `index` out past the ring's rim and dim the rest, leaving any other selected slice where it is. */\n selectSlice: {\n args: { sliceIndex: SLICE_ARG },\n easing: easeOut(),\n run: (timeline, state) => {\n const index = timeline.args.sliceIndex\n return popping(timeline, state, (i) => (i === index ? 1 : popAt(state, i)))\n },\n },\n /** Settle slice `index` back flush with the ring. */\n deselectSlice: {\n args: { sliceIndex: SLICE_ARG },\n easing: easeOut(),\n run: (timeline, state) => {\n const index = timeline.args.sliceIndex\n return popping(timeline, state, (i) => (i === index ? 0 : popAt(state, i)))\n },\n },\n /** Move the focus to slice `index` alone, settling whatever else was out. */\n selectOnlySlice: {\n args: { sliceIndex: SLICE_ARG },\n easing: easeOut(),\n run: (timeline, state) => {\n const index = timeline.args.sliceIndex\n return popping(timeline, state, (i) => (i === index ? 1 : 0))\n },\n },\n /** Settle every slice flush and bring the whole chart back to full. */\n clearSelection: {\n args: {},\n easing: easeOut(),\n run: (timeline, state) => popping(timeline, state, () => 0),\n },\n },\n})\n\nexport type PieChart = NodeOf<typeof PieChart>\n", "import {\n Flow,\n Paint,\n Rect,\n Shapes,\n Text,\n Types,\n clamp,\n component2d,\n textOps,\n type NodeOf,\n // Named so the column's emitted type reaches the Rect surface it spreads through the sdk, not core.\n type Alignment as _Alignment,\n type CornerRadiusResolved as _CornerRadiusResolved,\n type CornerStyleResolved as _CornerStyleResolved,\n type Fill as _Fill,\n type FillResolved as _FillResolved,\n type FlowMode as _FlowMode,\n type FlowResolved as _FlowResolved,\n type GapSize as _GapSize,\n type Node2D as _Node2D,\n type Node2DProps as _Node2DProps,\n type RectCornerRadius as _RectCornerRadius,\n type RectCornerStyle as _RectCornerStyle,\n type Shadow as _Shadow,\n type ShadowResolved as _ShadowResolved,\n type Stroke as _Stroke,\n type StrokeResolved as _StrokeResolved,\n type Vector2 as _Vector2,\n} from \"@motionscript/sdk\"\n\nimport { fresh } from \"../kit/entries\"\nimport { armed, arrival, arrivalOf, arrivalsEntry, beats, frameEase, type Arrival } from \"../kit/motion\"\nimport { DEFAULT_LEGEND, type LegendConfig } from \"../kit/shared\"\nimport type { PieSlice } from \"./shared\"\n\nconst SLIDE = 16\n\n/**\n * The legend of a {@link PieChart}: a column (it sits beside the ring, in the\n * vertical strip it leaves) of caption-and-swatch entries, one per slice. No\n * show/hide: removing a row would re-cut every other wedge's share. Selection\n * arrives through `dims`.\n */\nexport const PieLegend = component2d({\n key: \"pieLegend\",\n state: {\n ...Paint.state,\n ...Flow.state,\n ...Shapes.corners,\n /** One entry per slice, in ring order. */\n slices: fresh<PieSlice[]>(() => []),\n legend: Types.custom<LegendConfig>(DEFAULT_LEGEND),\n /** Per-slice opacity multiplier, the signal that dims the ring, so an entry fades in step with its wedge. Missing reads as 1. */\n dims: fresh<number[]>(() => []),\n arrivals: arrivalsEntry(),\n },\n // The flow must be stated: a `Rect` with none lays children out `freeform`, every caption on top of the last.\n defaults: {\n flow: \"vertical\",\n align: \"centerLeft\",\n width: \"hug\",\n height: \"hug\",\n gap: (state) => state.legend.itemGap / 3,\n padding: (state) => state.legend.padding,\n },\n // The selection dim rides a wrapper, so the entrance animates the entry's own opacity without the two fighting over one value.\n children: (state) => {\n const marker = state.legend.markerStyle\n return state.slices.map((_, index) =>\n Rect({\n width: \"hug\",\n height: \"hug\",\n flow: \"freeform\",\n opacity: () => clamp(state.dims[index] ?? 1, 0, 1),\n children: [\n Rect({\n flow: \"horizontal\",\n gap: 12,\n align: \"center\",\n width: \"hug\",\n height: \"hug\",\n opacity: () => arrivalOf(state.arrivals, index).opacity,\n x: () => arrivalOf(state.arrivals, index).shift,\n children: [\n Rect({\n width: marker.height,\n height: marker.height,\n cornerRadius: marker.borderRadius,\n fill: () => state.slices[index]?.fill ?? \"white\",\n stroke: marker.stroke,\n shadow: marker.shadow,\n }),\n Text({\n ...textOps.styleProps(state.legend.textStyle),\n text: () => state.slices[index]?.category ?? \"\",\n }),\n ],\n }),\n ],\n })\n )\n },\n commands: {\n ...Paint.commands,\n /** Every entry shifted right and transparent: what a chart shows ahead of this legend's beat, since `enter` arms only on its first frame. */\n arm: (timeline, state) =>\n timeline.animate(() => ({ arrivals: armed([...state.slices.keys()], { opacity: 0, shift: SLIDE }) })),\n /** Slide and fade entries in from the right, top to bottom. The duration is the whole entrance, the stagger carved out of it. */\n enter: (timeline, state) => {\n const count = state.slices.length\n if (count === 0) return timeline.wait(timeline.duration)\n const from: Arrival = { opacity: 0, shift: SLIDE }\n const arm = timeline.animate(() => ({ arrivals: armed([...state.slices.keys()], from) }))\n // Each gap is a fifth of an entry's move, the axes' rhythm, solved from the budget.\n const per = timeline.duration / (1 + Math.max(0, count - 1) / 5)\n const step = per / 5\n const ease = frameEase(timeline)\n const entries = state.slices.map((_, i) => ({ at: i * step, duration: per, piece: arrival(timeline, i, from, ease) }))\n return timeline.together([arm, beats(timeline, entries)])\n },\n },\n measure: (measurer, state, children) => Flow.measure(measurer, state, children, Paint.inset(state)),\n layout: (box, state, children) => Flow.layout(box, state, children, Paint.inset(state)),\n render: (ctx, state) => Paint.draw(ctx, state, Shapes.rect(ctx.size, state)),\n})\n\nexport type PieLegend = NodeOf<typeof PieLegend>\n", "import {\n Graphics2D,\n PathBuilder,\n Rect,\n Text,\n Types,\n clamp,\n component2d,\n fillOps,\n shadowOps,\n strokeOps,\n textOps,\n type Fill,\n type FillResolved,\n type NodeOf,\n // Named so this component's emitted type reaches `Node2D` and its props through the sdk, not core.\n type Node2D,\n type Node2DProps as _Node2DProps,\n type TextStyle,\n type Vector2,\n} from \"@motionscript/sdk\"\n\nimport { asParts, fresh } from \"../kit/entries\"\nimport { frameEase } from \"../kit/motion\"\nimport { loadPaints } from \"../kit/paints\"\nimport { formatPercent } from \"../kit/shared\"\nimport {\n DEFAULT_PIE_CHART_THEME,\n pieArcs,\n relaxLabels,\n ringPoint,\n selectionFade,\n type CenterConfig,\n type LabelConfig,\n type PieArc,\n type PieLabelMode,\n type PieLabelPlacement,\n type PieSlice,\n type RingConfig,\n} from \"./shared\"\n\nconst LABEL_GAP = 12\nconst LABEL_PAD = 14\n/** Rough glyph width as a fraction of the font size, for gutter sizing. */\nconst GLYPH_RATIO = 0.62\nconst LEADER_LIFTOFF = 6\nconst LEADER_LENGTH = 34\nconst LABEL_STUB = 26\nconst TEXT_INSET = 8\n/** Floor on the elbow's distance from the ring's axis, as a fraction of the leader radius, so a label pushed above or below the ring still elbows out sideways. */\nconst MIN_ELBOW = 0.18\nconst ARC_STEPS = 240\nconst ELBOW_RADIUS = 8\nconst BARE_PAD = 2\nconst INSIDE_CAPTION_RATIO = 0.44\n/** Where an inside label sits on a solid pie, as a fraction of the radius: far enough out to clear the point every wedge converges on. */\nconst PIE_LABEL_RADIUS = 0.62\nconst CENTER_LABEL_FIT = 0.78\nconst INSIDE_LABEL_FIT = 0.9\n/** Below this a wedge's bisector counts as axis-aligned and that bound stops binding. */\nconst AXIS_EPSILON = 1e-3\n/** Type size when a theme style leaves it unset or on autofit. */\nconst FALLBACK_SIZE = 26\n\nfunction sizeOf(style: TextStyle, fallback = FALLBACK_SIZE): number {\n const size = style.fontSize\n return typeof size === \"number\" ? size : fallback\n}\n\ninterface LabelPlacement {\n index: number\n /** +1 right, -1 left. */\n side: 1 | -1\n /** Wedge edge, elbow, label. */\n leader: Vector2[]\n /** Where the label's inner edge is pinned. */\n text: Vector2\n}\n\n/**\n * A wedge over `a0`..`a1` (radians clockwise from 12 o'clock) from `rInner` to\n * `rOuter`, centred on `cx`; with `rInner <= 0` it closes through the centre.\n * `halfGap` is half the px gap to the neighbour, converted to an angle per\n * radius (wider near the hole) so the channel keeps one width across the band.\n * The inset is capped at the wedge's half-span so a sliver narrows to nothing\n * rather than turning inside out.\n */\nfunction wedgePath(\n a0: number,\n a1: number,\n rInner: number,\n rOuter: number,\n halfGap: number,\n cx: number\n): PathBuilder {\n const path = new PathBuilder()\n const span = a1 - a0\n const steps = Math.max(\n 2,\n Math.ceil((Math.abs(span) / (Math.PI * 2)) * ARC_STEPS)\n )\n const insetAt = (r: number): number =>\n r <= 0 ? 0 : Math.min(halfGap / r, span / 2.2)\n const arcTo = (r: number, i: number): Vector2 => {\n const inset = insetAt(r)\n const from = a0 + inset\n const to = a1 - inset\n const point = ringPoint(r, from + ((to - from) * i) / steps)\n return { x: point.x + cx, y: point.y }\n }\n\n if (rInner <= 0) {\n // The sides converge on the centre, so only the rim carries the gap.\n path.moveTo(cx, 0)\n for (let i = 0; i <= steps; i++) {\n const p = arcTo(rOuter, i)\n path.lineTo(p.x, p.y)\n }\n path.close()\n return path\n }\n\n for (let i = 0; i <= steps; i++) {\n const p = arcTo(rOuter, i)\n if (i === 0) path.moveTo(p.x, p.y)\n else path.lineTo(p.x, p.y)\n }\n // The inner arc's own wider inset makes each side edge a chord `halfGap` px off the divide at both ends.\n for (let i = steps; i >= 0; i--) {\n const p = arcTo(rInner, i)\n path.lineTo(p.x, p.y)\n }\n path.close()\n return path\n}\n\ninterface RingState {\n readonly slices: PieSlice[]\n readonly ring: RingConfig\n readonly label: LabelConfig\n readonly centerStyle: CenterConfig\n readonly labelMode: PieLabelMode\n readonly labelPlacement: PieLabelPlacement\n readonly pops: readonly number[]\n readonly dimOpacity: number\n readonly centerLabel: string\n readonly centerCaption: string\n}\n\n/** Every radius, gutter and label position for one box, derived afresh wherever read so the drawing and the labels' bound positions cannot disagree. */\nexport class RingGeometry {\n constructor(\n private readonly s: RingState,\n private readonly box: { readonly width: number; readonly height: number }\n ) { }\n\n /** Re-read every frame so a value tween re-cuts them. */\n get arcs(): PieArc[] {\n return pieArcs(this.s.slices, this.s.ring.startAngle)\n }\n\n /** Leader labels are the only thing that costs a gutter. */\n get hasLeaderLabels(): boolean {\n return this.s.labelPlacement === \"leader\" && this.s.labelMode !== \"none\" && this.arcs.length > 0\n }\n\n /**\n * Px a horizontal label centred in `arc`'s band can run before leaving the\n * wedge: bound by the band's thickness near 3 o'clock, by its arc near 12.\n * Measured off the unpopped radius, so a slice growing out of the ring does not\n * resize its own text mid-animation.\n */\n insideRoom(arc: PieArc): number {\n const outer = this.outerRadius\n const hole = this.holeRadius\n const band = hole > 0 ? outer - hole : outer * (1 - PIE_LABEL_RADIUS) * 2\n const mid = hole > 0 ? (hole + outer) / 2 : outer * PIE_LABEL_RADIUS\n const sin = Math.abs(Math.sin(arc.mid))\n const cos = Math.abs(Math.cos(arc.mid))\n const acrossBand = sin > AXIS_EPSILON ? band / sin : Infinity\n const alongArc = cos > AXIS_EPSILON ? ((arc.a1 - arc.a0) * mid) / cos : Infinity\n return Math.max(0, Math.min(acrossBand, alongArc))\n }\n\n /** Widest line, in glyphs at the leading line's size, an inside label runs to. */\n insideGlyphs(arc: PieArc): number {\n const mode = this.s.labelMode\n const category = this.s.slices[arc.index]?.category.length ?? 0\n const percent = mode === \"both\" || mode === \"percent\" ? formatPercent(arc.fraction).length : 0\n // The caption is a fraction of the leading line, unless it is the leading line.\n const caption = mode === \"category\" ? category : mode === \"both\" ? category * INSIDE_CAPTION_RATIO : 0\n return Math.max(1, percent, caption)\n }\n\n /** The theme's `insideStyle.fontSize` shrunk to fit, an upper bound. One size across every inside label (the smallest any needs): mismatched sizes read as a mistake. */\n get insideFont(): number {\n let size = sizeOf(this.s.label.insideStyle, FALLBACK_SIZE * 1.9)\n for (const arc of this.arcs) {\n const fits = (this.insideRoom(arc) * INSIDE_LABEL_FIT) / (this.insideGlyphs(arc) * GLYPH_RATIO)\n size = Math.min(size, fits)\n }\n return Math.max(1, size)\n }\n\n /** The configured size capped to fit across the hole, which derives from the box: a smaller reflow would otherwise push the summary through the ring. */\n get centerFont(): number {\n const { centerStyle, centerLabel, centerCaption } = this.s\n const wanted = sizeOf(centerStyle.labelStyle, 46)\n const hole = this.holeRadius\n if (hole <= 0) return wanted\n const captionRatio = sizeOf(centerStyle.captionStyle, 22) / Math.max(1, wanted)\n // Both lines must clear the hole; the caption is a fraction of this size, so the wider once drawn binds, not the longer.\n const glyphs = Math.max(1, centerLabel.length, centerCaption.length * captionRatio)\n return Math.min(wanted, (hole * 2 * CENTER_LABEL_FIT) / (glyphs * GLYPH_RATIO))\n }\n\n /** Rim radius of a fully selected slice as a multiple of the ring's. */\n get growFactor(): number {\n return Math.max(1, Math.min(1.6, this.s.ring.selectedGrow))\n }\n\n get maxPop(): number {\n return this.outerRadius * (this.growFactor - 1)\n }\n\n /** Height of one leader label block, the unit the columns are spaced by. Inside labels cost no vertical reserve. */\n get labelRowHeight(): number {\n if (!this.hasLeaderLabels) return 0\n const caption = sizeOf(this.s.label.textStyle) * 1.25\n const percent = sizeOf(this.s.label.percentStyle) * 1.25\n if (this.s.labelMode === \"both\") return caption + percent + 2\n return this.s.labelMode === \"category\" ? caption : percent\n }\n\n /** Widest line in glyphs a leader label for slice `index` runs to. */\n captionGlyphs(index: number): number {\n const mode = this.s.labelMode\n // \"100%\" is the widest a percentage gets; captions are their own length.\n const percent = mode === \"percent\" || mode === \"both\" ? 4 : 0\n const category = mode === \"category\" || mode === \"both\" ? (this.s.slices[index]?.category.length ?? 0) : 0\n return Math.max(percent, category)\n }\n\n /** Room reserved per side for leaders and labels, from the slices that leader out that way. Excludes selection growth, which scales with the radius and is divided out in {@link outerRadius}. */\n get labelGutters(): { left: number; right: number } {\n const gutters = { left: BARE_PAD, right: BARE_PAD }\n if (!this.hasLeaderLabels) return gutters\n const glyph = sizeOf(this.s.label.textStyle) * GLYPH_RATIO\n for (const arc of this.arcs) {\n const room = LEADER_LENGTH + LABEL_STUB + TEXT_INSET + this.captionGlyphs(arc.index) * glyph + LABEL_PAD\n const key = sideOf(arc.mid) > 0 ? \"right\" : \"left\"\n gutters[key] = Math.max(gutters[key], room)\n }\n return gutters\n }\n\n /** Slides the ring into the empty half when labels all fall one way, keeping the ring-plus-labels group centred. */\n get centerX(): number {\n const { left, right } = this.labelGutters\n return (left - right) / 2\n }\n\n /** Fitted to what the gutters leave. The divisor keeps a fully selected slice (`radius * growFactor`) inside the box, and the ring one size whether or not anything is selected. */\n get outerRadius(): number {\n const { left, right } = this.labelGutters\n const room = Math.min((this.box.width - left - right) / 2, this.box.height / 2 - this.labelRowHeight - 4)\n return Math.max(0, room / this.growFactor)\n }\n\n get holeRadius(): number {\n const configured = this.s.ring.holeRadius\n if (configured <= 0) return 0\n const px = configured < 1 ? configured * this.outerRadius : configured\n return Math.min(px, this.outerRadius * 0.95)\n }\n\n /** The circle every leader elbows on, clear of even a popped-out slice. */\n get elbowRadius(): number {\n return this.outerRadius + this.maxPop + LEADER_LENGTH\n }\n\n popOf(index: number): number {\n return clamp(this.s.pops[index] ?? 0, 0, 1)\n }\n\n anchorRadius(index: number): number {\n return this.outerRadius + this.maxPop * this.popOf(index)\n }\n\n /** Halfway across the band on a donut, further out on a solid pie, where halfway would crowd the point the wedges converge on. Rides the slice's pop. */\n bandMidRadius(index: number): number {\n const rim = this.anchorRadius(index)\n const hole = this.holeRadius\n return hole > 0 ? (hole + rim) / 2 : rim * PIE_LABEL_RADIUS\n }\n\n insideAnchor(index: number): Vector2 {\n const arc = this.arcs.find((a) => a.index === index)\n if (arc === undefined) return { x: this.centerX, y: 0 }\n const point = ringPoint(this.bandMidRadius(index), arc.mid)\n return { x: point.x + this.centerX, y: point.y }\n }\n\n /** The most-grown slice, so the dim in {@link sliceOpacity} rides the pops' tween. */\n get selectionStrength(): number {\n let strongest = 0\n for (let i = 0; i < this.s.slices.length; i++) strongest = Math.max(strongest, this.popOf(i))\n return strongest\n }\n\n /** Opacity of slice `index` with its leader and label. */\n sliceOpacity(index: number): number {\n return selectionFade(this.selectionStrength, this.s.dimOpacity, this.popOf(index))\n }\n\n percentText(index: number): string {\n const arc = this.arcs.find((a) => a.index === index)\n return formatPercent(arc?.fraction ?? 0)\n }\n\n /** Labels split into left and right columns by their slice's bisector, each spread so nothing overlaps or leaves the box, then a leader built to each. */\n placements(): LabelPlacement[] {\n if (!this.hasLeaderLabels) return []\n const arcs = this.arcs\n if (arcs.length === 0 || this.outerRadius <= 0) return []\n\n const radius = this.elbowRadius\n const rowHeight = this.labelRowHeight\n const top = this.box.height / 2 - rowHeight / 2\n const bottom = -top\n const cx = this.centerX\n\n interface Slot {\n index: number\n side: 1 | -1\n mid: number\n ideal: number\n y: number\n }\n const right: Slot[] = []\n const left: Slot[] = []\n for (const arc of arcs) {\n const side = sideOf(arc.mid)\n const ideal = ringPoint(radius, arc.mid).y\n ;(side > 0 ? right : left).push({ index: arc.index, side, mid: arc.mid, ideal, y: ideal })\n }\n relaxLabels(right, rowHeight + LABEL_GAP, top, bottom)\n relaxLabels(left, rowHeight + LABEL_GAP, top, bottom)\n\n return [...right, ...left].map((slot) => {\n const start = ringPoint(this.anchorRadius(slot.index) + LEADER_LIFTOFF, slot.mid)\n // The elbow sits where the label's y crosses the leader circle. Solving\n // x^2 + y^2 = radius^2 for x keeps it on that circle (never inside the\n // ring); a label pushed past the circle's top or bottom falls back to the\n // sideways floor so its leader still reads as pointing outward.\n const onCircle = Math.sqrt(Math.max(0, radius * radius - slot.y * slot.y))\n const elbowX = cx + slot.side * Math.max(onCircle, radius * MIN_ELBOW)\n const endX = cx + slot.side * (radius + LABEL_STUB)\n\n return {\n index: slot.index,\n side: slot.side,\n leader: [\n { x: start.x + cx, y: start.y },\n { x: elbowX, y: slot.y },\n { x: endX, y: slot.y },\n ],\n text: { x: endX + slot.side * TEXT_INSET, y: slot.y },\n }\n })\n }\n\n placementFor(index: number): LabelPlacement | undefined {\n return this.placements().find((placement) => placement.index === index)\n }\n}\n\nfunction sideOf(mid: number): 1 | -1 {\n return Math.sin(mid) >= 0 ? 1 : -1\n}\n\nconst NO_BOX = { width: 0, height: 0 }\n\n/** Fills resolved once per paint, not per frame: a theme tween hands over new paint, but a frame that moves nothing must not re-resolve. Keyed by the paint, so it needs no node. */\nconst resolvedFills = new WeakMap<object, FillResolved[]>()\n\nfunction resolvedFill(fill: Fill): FillResolved[] {\n if (typeof fill !== \"object\" || fill === null) return fillOps.resolve(fill)\n let resolved = resolvedFills.get(fill)\n if (resolved === undefined) resolvedFills.set(fill, resolved = fillOps.resolve(fill))\n return resolved\n}\n\nexport function ringFills(slices: readonly PieSlice[]): FillResolved[][] {\n return slices.map((slice) => resolvedFill(slice.fill))\n}\n\n/** One label per drawn slice (a zero-valued slice has no wedge), then the hole's summary. Only the choice of form is settled here; the pinned points stay reactive. */\nfunction ringLabels(s: RingState & { readonly labelReveal: number; readonly centerReveal: number }) {\n const blocks: Node2D[] = []\n // A label block's parent is the ring, whose box it was laid out at.\n const geometry = () => new RingGeometry(s, (blocks[0]?.parent as Node2D | null | undefined)?.layoutBounds ?? NO_BOX)\n\n const label = (arc: PieArc): Node2D => {\n const opacity = () => clamp(s.labelReveal, 0, 1) * geometry().sliceOpacity(arc.index)\n const wantsCategory = s.labelMode === \"both\" || s.labelMode === \"category\"\n const wantsPercent = s.labelMode === \"both\" || s.labelMode === \"percent\"\n\n if (s.labelPlacement === \"inside\") {\n // The number leads: inside a wedge the reader knows which slice it is. Sizes are bound because the fitted size derives from a radius unknown until layout.\n const inside: Node2D[] = []\n if (wantsPercent) {\n inside.push(new Text({\n ...textOps.styleProps(s.label.insideStyle),\n text: () => geometry().percentText(arc.index),\n fontSize: () => geometry().insideFont,\n }))\n }\n if (wantsCategory) {\n inside.push(new Text({\n ...textOps.styleProps(s.label.insideStyle),\n text: () => s.slices[arc.index]?.category ?? \"\",\n fontSize: () => (s.labelMode === \"category\" ? geometry().insideFont : geometry().insideFont * INSIDE_CAPTION_RATIO),\n }))\n }\n return new Rect({\n flow: \"vertical\",\n width: \"hug\",\n height: \"hug\",\n gap: 0,\n align: \"center\",\n opacity,\n center: () => geometry().insideAnchor(arc.index),\n children: inside,\n })\n }\n\n const side = sideOf(arc.mid)\n const at = () => geometry().placementFor(arc.index)?.text ?? { x: 0, y: 0 }\n const children: Node2D[] = []\n if (wantsCategory) {\n children.push(new Text({\n ...textOps.styleProps(s.label.textStyle),\n text: () => s.slices[arc.index]?.category ?? \"\",\n textAlign: side > 0 ? \"left\" : \"right\",\n }))\n }\n if (wantsPercent) {\n children.push(new Text({\n ...textOps.styleProps(s.label.percentStyle),\n text: () => geometry().percentText(arc.index),\n textAlign: side > 0 ? \"left\" : \"right\",\n }))\n }\n return new Rect({\n flow: \"vertical\",\n width: \"hug\",\n height: \"hug\",\n gap: 2,\n align: side > 0 ? \"centerLeft\" : \"centerRight\",\n opacity,\n centerLeft: side > 0 ? at : undefined,\n centerRight: side > 0 ? undefined : at,\n children,\n })\n }\n\n if (s.labelPlacement !== \"none\" && s.labelMode !== \"none\") {\n for (const arc of pieArcs(s.slices, s.ring.startAngle)) blocks.push(label(arc))\n }\n\n // Pinned to the ring's centre, so it tracks the sideways shift a one-sided label column causes.\n if (s.centerLabel !== \"\" || s.centerCaption !== \"\") {\n const children: Node2D[] = []\n if (s.centerLabel !== \"\") {\n children.push(new Text({\n ...textOps.styleProps(s.centerStyle.labelStyle),\n text: s.centerLabel,\n fontSize: () => geometry().centerFont,\n }))\n }\n if (s.centerCaption !== \"\") {\n children.push(new Text({\n ...textOps.styleProps(s.centerStyle.captionStyle),\n text: s.centerCaption,\n fontSize: () =>\n geometry().centerFont * (sizeOf(s.centerStyle.captionStyle, 22) / sizeOf(s.centerStyle.labelStyle, 46)),\n }))\n }\n blocks.push(new Rect({\n flow: \"vertical\",\n width: \"hug\",\n height: \"hug\",\n gap: 2,\n align: \"center\",\n opacity: () => clamp(s.centerReveal, 0, 1),\n center: () => ({ x: geometry().centerX, y: 0 }),\n children,\n }))\n }\n return asParts(blocks)\n}\n\n/**\n * The ring: one wedge per {@link PieSlice} swept clockwise from `startAngle`,\n * with a constant-width `gap` (see {@link wedgePath}). A selected slice swells\n * out while its hole edge stays put, and the rest dims, leaders and labels\n * included. The only path-drawing part of the chart family; labels are real\n * {@link Text} nodes, so no glyph-width guesswork places a caption. Only the\n * sides that carry a leader reserve a gutter, and the ring slides into the rest.\n */\nexport const PieRing = component2d({\n key: \"pieRing\",\n state: {\n slices: fresh<PieSlice[]>(() => []),\n ring: Types.custom<RingConfig>(DEFAULT_PIE_CHART_THEME.ring),\n label: Types.custom<LabelConfig>(DEFAULT_PIE_CHART_THEME.label),\n // Not `center`: every node already has one, the anchor its position is measured from.\n centerStyle: Types.custom<CenterConfig>(DEFAULT_PIE_CHART_THEME.center),\n labelMode: Types.custom<PieLabelMode>(\"percent\"),\n labelPlacement: Types.custom<PieLabelPlacement>(\"leader\"),\n /** Sweep `[0, 1]`: the wedges grow clockwise from `startAngle` as it rises. */\n growth: 1,\n /** Fade of leaders and labels, separate from `growth` so they arrive after the sweep. */\n labelReveal: 1,\n /** Per-slice pop `[0, 1]`, owned by the chart so the ring and legend read one signal. */\n pops: fresh<number[]>(() => []),\n /** Opacity everything unselected falls back to while any slice is selected. */\n dimOpacity: DEFAULT_PIE_CHART_THEME.spotlight.dimOpacity,\n /** Empty leaves the hole clear; ignored when there is no hole. */\n centerLabel: \"\",\n centerCaption: \"\",\n /** The hole's summary fade, untouched by the entrance: a scene that wants the conclusion on a later beat sets 0 and calls `revealCenter`. */\n centerReveal: 1,\n },\n // A plain node filled its parent; one made of its labels would hug them.\n defaults: { width: \"fill\", height: \"fill\" },\n children: (state) => ringLabels(state),\n // The paints sit past the paint entries: a fill per slice, the wedge outline and shadow, and the leader stroke.\n load: (loader, state) =>\n loadPaints(loader, {\n fills: ringFills(state.slices),\n shadows: [shadowOps.resolve(state.ring.shadow)],\n strokes: [strokeOps.resolve(state.ring.stroke), strokeOps.resolve(state.label.leaderStroke)],\n }),\n commands: {\n /** Wipe the wedges in around the ring (`growth` 0 to 1). */\n sweep: (timeline) => {\n const ease = frameEase(timeline)\n return timeline.animate((t) => ({ growth: ease(t) }))\n },\n /** Fade the leaders and captions up. */\n revealLabels: (timeline) => {\n const ease = frameEase(timeline)\n return timeline.animate((t) => ({ labelReveal: ease(t) }))\n },\n /** Fade the hole's summary up; not part of the entrance, since it is usually the conclusion. */\n revealCenter: (timeline, state) => {\n const ease = frameEase(timeline)\n const from = clamp(state.centerReveal, 0, 1)\n return timeline.animate((t) => ({ centerReveal: from + (1 - from) * ease(t) }))\n },\n },\n render: (ctx, state) => {\n const g = new RingGeometry(state, ctx.size)\n const rOuter = g.outerRadius\n const arcs = rOuter > 0 ? g.arcs : []\n if (arcs.length > 0) {\n const sweep = clamp(state.growth, 0, 1)\n const revealEnd = state.ring.startAngle * (Math.PI / 180) + Math.PI * 2 * sweep\n const hole = g.holeRadius\n const halfGap = Math.max(0, state.ring.gap) / 2\n\n // One shared frame (the whole ring square, grown slices included) pinned onto\n // every wedge path, so `Graphics2D.path()` doesn't re-centre each wedge on its\n // own bbox and collapse them all onto the origin. It stays symmetric about the\n // node's origin, so path coordinates land exactly in node space, and is grown\n // by the ring's sideways offset rather than moved with it, which would undo\n // the very shift it has to contain.\n const cx = g.centerX\n const half = rOuter + g.maxPop + 1 + Math.abs(cx)\n const centerBounds: [number, number, number, number] = [-half, -half, half, half]\n // Explicit width/height alongside centerBounds: a wedge path's command list\n // carries no implied extent, so a slice filled with an image or a video needs\n // this to size its decode.\n const span = half * 2\n\n const fills = ringFills(state.slices)\n for (const arc of arcs) {\n // Clipped to the revealed arc, so the ring draws in as one clockwise wipe.\n const a1 = Math.min(arc.a1, revealEnd)\n if (a1 <= arc.a0) continue\n\n const path = wedgePath(arc.a0, a1, hole, g.anchorRadius(arc.index), halfGap, cx)\n const wedge = new Graphics2D().path(path.toPathState({ centerBounds, width: span, height: span }))\n wedge.shadow(state.ring.shadow)\n wedge.fill(fills[arc.index] ?? [])\n wedge.stroke(state.ring.stroke)\n ctx.draw(wedge.opacity(g.sliceOpacity(arc.index)))\n }\n\n // One `Graphics2D` per leader: open polylines chained into one command list\n // are combined into a single surface before the stroke, which closes each\n // contour and paints the leaders as thin triangles.\n const reveal = clamp(state.labelReveal, 0, 1)\n if (reveal > 0.001) {\n for (const placement of g.placements()) {\n ctx.draw(\n new Graphics2D()\n .line({ points: placement.leader, radius: ELBOW_RADIUS })\n .stroke(state.label.leaderStroke)\n .opacity(reveal * g.sliceOpacity(placement.index))\n )\n }\n }\n }\n ctx.children()\n },\n})\n\nexport type PieRing = NodeOf<typeof PieRing>\n", "import {\n clamp,\n lerpNumber,\n shadowOps,\n strokeOps,\n textOps,\n type DataRecord,\n type Fill,\n type Shadow,\n type ShadowResolved,\n type Stroke,\n type StrokeResolved,\n type TextStyle,\n type Vector2,\n} from \"@motionscript/sdk\"\n\nimport {\n CHART_PALETTE,\n DEFAULT_LEGEND,\n DEFAULT_SERIES_FILLS,\n lerpLegendConfig,\n lerpSeriesFills,\n num,\n resolveLegendConfig,\n resolveSeriesFills,\n text,\n type LegendConfig,\n type LegendTheme,\n} from \"../kit/shared\"\n\n/** One resolved slice, which the ring and legend consume; the chart resolves it from `data` and two field names. */\nexport interface PieSlice {\n /** Label and legend caption. */\n category: string\n /** The wedge covers `value / (sum of all values)`. */\n value: number\n /** Wedge and swatch paint. */\n fill: Fill\n}\n\n/**\n * One slice per row, painted from `palette` by row order (cycling). Paint comes\n * from the palette, not the rows, because a pie's mark is a row where a line's\n * colour belongs to a column; that lets a pie be fed a CSV of plain numbers.\n */\nexport function resolvePieSlices(\n data: DataRecord[],\n category: string,\n value: string,\n palette: Fill[]\n): PieSlice[] {\n return data.map((row, i) => ({\n category: text(row, category),\n value: num(row, value),\n fill:\n palette.length > 0 ? palette[i % palette.length] : (\"white\" as Fill),\n }))\n}\n\n/** What a {@link PieChart}'s labels say. */\nexport type PieLabelMode =\n /** Two lines, whose order flips with placement: a leader label reads category over percentage, an inside label reads the percentage big over a small category. */\n | \"both\"\n | \"category\"\n | \"percent\"\n /** No labels and no leader lines. */\n | \"none\"\n\n/** `\"inside\"` centres the label in the wedge's band with no leader or gutter, so the ring draws bigger; `\"leader\"` is what a sliver too narrow for text needs. */\nexport type PieLabelPlacement = \"inside\" | \"leader\" | \"none\"\n\nexport const DEG = Math.PI / 180\n\n/**\n * Opacity of a slice under the selection: 1 while nothing is out (`focus` is the\n * most-grown pop) and for the selected slice itself, falling toward `dimOpacity`\n * for the rest as the selection grows.\n */\nexport function selectionFade(focus: number, dimOpacity: number, pop: number): number {\n if (focus <= 0) return 1\n return 1 - (1 - clamp(dimOpacity, 0, 1)) * focus * (1 - pop)\n}\n\n/** A slice's plotted value: negatives and non-finite values count as 0. */\nexport function sliceValue(slice: PieSlice): number {\n return slice.value > 0 && Number.isFinite(slice.value) ? slice.value : 0\n}\n\n/** Sum of every slice's plotted value, the denominator of every share. */\nexport function sliceTotal(slices: PieSlice[]): number {\n let sum = 0\n for (const slice of slices) sum += sliceValue(slice)\n return sum\n}\n\n/** The point at radius `r`, angle `a` on a ring about the origin in centred y-up space. Angles run clockwise from 12 o'clock. */\nexport function ringPoint(r: number, a: number): Vector2 {\n return { x: r * Math.sin(a), y: r * Math.cos(a) }\n}\n\n/** One slice's resolved angular span on the ring. */\nexport interface PieArc {\n /** Index of the slice in the source list. */\n index: number\n /** Radians clockwise from 12 o'clock. */\n a0: number\n a1: number\n /** Where the slice's label and pop-out point. */\n mid: number\n /** Share of the total, `[0, 1]`. */\n fraction: number\n}\n\n/**\n * One arc per positive slice, walking clockwise from `startDegrees` and abutting.\n * Arcs are the undivided shares: a fixed angular gap would pinch shut at the\n * hole, so the gap is applied when the wedge is drawn, as a constant-width inset\n * off both ends.\n */\nexport function pieArcs(slices: PieSlice[], startDegrees: number): PieArc[] {\n const total = sliceTotal(slices)\n if (total <= 0) return []\n\n const arcs: PieArc[] = []\n let cursor = startDegrees * DEG\n slices.forEach((slice, index) => {\n const value = sliceValue(slice)\n if (value <= 0) return\n const fraction = value / total\n const a0 = cursor\n const a1 = a0 + fraction * Math.PI * 2\n cursor = a1\n arcs.push({ index, a0, a1, mid: (a0 + a1) / 2, fraction })\n })\n return arcs\n}\n\ninterface LabelSlot {\n /** Where the label would sit if nothing else were in the way. */\n ideal: number\n /** Written by {@link relaxLabels}. */\n y: number\n}\n\n/**\n * Spread one column of labels so none overlap, each near its `ideal` y and all\n * inside `[bottom, top]`. A top-down pass clears each label from the one above\n * by `minGap`; a reverse pass lifts a tail pushed past `bottom`. When the column\n * cannot hold them all, the reverse pass wins and the crowding is shared evenly.\n * Mutates each `y` and returns `slots` ordered top to bottom.\n */\nexport function relaxLabels<T extends LabelSlot>(\n slots: T[],\n minGap: number,\n top: number,\n bottom: number\n): T[] {\n slots.sort((a, b) => b.ideal - a.ideal)\n\n let ceiling = top\n for (const slot of slots) {\n slot.y = Math.min(slot.ideal, ceiling)\n ceiling = slot.y - minGap\n }\n\n let floor = bottom\n for (let i = slots.length - 1; i >= 0; i--) {\n slots[i].y = Math.max(slots[i].y, floor)\n floor = slots[i].y + minGap\n }\n\n return slots\n}\n\n/** The ring's geometry and paint: everything about the wedges but their colour. */\nexport interface RingConfig {\n /** Channel between adjacent slices in px, constant across the band (an angular gap would splay open toward the rim). */\n gap: number\n /** Below 1 a fraction of the outer radius (`0.6` leaves a band 40% thick); 1 and up a pixel radius; `0` draws solid wedges. */\n holeRadius: number\n /** Degrees clockwise from 12 o'clock where the first slice begins. */\n startAngle: number\n /** How far a selected slice's rim reaches, as a multiple of the outer radius so the emphasis scales with the chart. Clamped to `[1, 1.6]`. */\n selectedGrow: number\n stroke: Stroke\n shadow: Shadow\n}\n\n/** How the ring's labels are set, and the leader lines that point at them. */\nexport interface LabelConfig {\n /** The category caption. */\n textStyle: TextStyle\n /** The percentage. */\n percentStyle: TextStyle\n /** Both lines of a label inside its wedge: it sits on the wedge's paint, so it usually wants the opposite end of the value range from everything else. */\n insideStyle: TextStyle\n /** Runs from a wedge out to its label. */\n leaderStroke: Stroke\n}\n\n/** The summary set in the ring's hole. */\nexport interface CenterConfig {\n labelStyle: TextStyle\n captionStyle: TextStyle\n}\n\n/** A {@link PieChart}'s full, resolved visual theme. */\nexport interface PieChartConfig {\n /** Wedge and swatch paint, cycled by **row** index (`i % length`). */\n sliceFills: Fill[]\n ring: RingConfig\n label: LabelConfig\n center: CenterConfig\n legend: LegendConfig\n /** How far everything outside the selection fades while any slice is selected. */\n spotlight: { dimOpacity: number }\n}\n\n/** The author's partial theme, deep-merged onto {@link DEFAULT_PIE_CHART_THEME}. */\nexport type PieChartTheme = {\n sliceFills?: Fill[]\n ring?: Partial<RingConfig>\n label?: Partial<LabelConfig>\n center?: Partial<CenterConfig>\n legend?: LegendTheme\n spotlight?: Partial<PieChartConfig[\"spotlight\"]>\n}\n\nconst LABEL_TEXT_STYLE: TextStyle = {\n fontSize: 26,\n fontWeight: 600,\n textAlign: \"center\",\n fill: CHART_PALETTE.markerColor,\n}\n\n/** Fainter than `SPOTLIGHT_DIM`: a ring's slices touch, so at that a selected slice barely separates. */\nexport const PIE_SPOTLIGHT_DIM = 0.1\n\nexport const DEFAULT_PIE_CHART_THEME: PieChartConfig = {\n sliceFills: DEFAULT_SERIES_FILLS,\n ring: {\n gap: 6,\n // A donut by default: the hole is the one place a conclusion can be set.\n holeRadius: 0.6,\n startAngle: 0,\n selectedGrow: 1.08,\n stroke: [],\n shadow: [],\n },\n label: {\n textStyle: LABEL_TEXT_STYLE,\n percentStyle: { ...LABEL_TEXT_STYLE, fontWeight: 700 },\n // On a mid-tone wedge, so the panel colour rather than the axis ink.\n insideStyle: { ...LABEL_TEXT_STYLE, fontWeight: 800, fill: \"#15130e\" },\n leaderStroke: { weight: 2, fill: CHART_PALETTE.markerColor },\n },\n center: {\n labelStyle: {\n fontSize: 46,\n fontWeight: 800,\n textAlign: \"center\",\n fill: CHART_PALETTE.markerColor,\n },\n captionStyle: {\n fontSize: 22,\n fontWeight: 500,\n textAlign: \"center\",\n fill: CHART_PALETTE.markerColor,\n },\n },\n legend: DEFAULT_LEGEND,\n spotlight: { dimOpacity: PIE_SPOTLIGHT_DIM },\n}\n\nfunction resolveRingConfig(\n ring: Partial<RingConfig> | undefined,\n previous: RingConfig\n): RingConfig {\n return {\n gap: ring?.gap ?? previous.gap,\n holeRadius: ring?.holeRadius ?? previous.holeRadius,\n startAngle: ring?.startAngle ?? previous.startAngle,\n selectedGrow: ring?.selectedGrow ?? previous.selectedGrow,\n stroke: strokeOps.resolve(ring?.stroke ?? previous.stroke),\n shadow: shadowOps.resolve(ring?.shadow ?? previous.shadow),\n }\n}\n\nfunction lerpRingConfig(\n from: RingConfig,\n to: RingConfig,\n t: number\n): RingConfig {\n return {\n gap: lerpNumber(from.gap, to.gap, t),\n holeRadius: lerpNumber(from.holeRadius, to.holeRadius, t),\n startAngle: lerpNumber(from.startAngle, to.startAngle, t),\n selectedGrow: lerpNumber(from.selectedGrow, to.selectedGrow, t),\n stroke: strokeOps.lerp(\n from.stroke as StrokeResolved[],\n to.stroke as StrokeResolved[],\n t\n ),\n shadow: shadowOps.lerp(\n from.shadow as ShadowResolved[],\n to.shadow as ShadowResolved[],\n t\n ),\n }\n}\n\nfunction resolveLabelConfig(\n label: Partial<LabelConfig> | undefined,\n previous: LabelConfig\n): LabelConfig {\n return {\n textStyle: textOps.resolveStyle(label?.textStyle, previous.textStyle),\n percentStyle: textOps.resolveStyle(\n label?.percentStyle,\n previous.percentStyle\n ),\n insideStyle: textOps.resolveStyle(label?.insideStyle, previous.insideStyle),\n leaderStroke: strokeOps.resolve(\n label?.leaderStroke ?? previous.leaderStroke\n ),\n }\n}\n\nfunction lerpLabelConfig(\n from: LabelConfig,\n to: LabelConfig,\n t: number\n): LabelConfig {\n return {\n textStyle: textOps.lerpStyle(from.textStyle, to.textStyle, t),\n percentStyle: textOps.lerpStyle(from.percentStyle, to.percentStyle, t),\n insideStyle: textOps.lerpStyle(from.insideStyle, to.insideStyle, t),\n leaderStroke: strokeOps.lerp(\n from.leaderStroke as StrokeResolved[],\n to.leaderStroke as StrokeResolved[],\n t\n ),\n }\n}\n\nfunction resolveCenterConfig(\n center: Partial<CenterConfig> | undefined,\n previous: CenterConfig\n): CenterConfig {\n return {\n labelStyle: textOps.resolveStyle(center?.labelStyle, previous.labelStyle),\n captionStyle: textOps.resolveStyle(\n center?.captionStyle,\n previous.captionStyle\n ),\n }\n}\n\nfunction lerpCenterConfig(\n from: CenterConfig,\n to: CenterConfig,\n t: number\n): CenterConfig {\n return {\n labelStyle: textOps.lerpStyle(from.labelStyle, to.labelStyle, t),\n captionStyle: textOps.lerpStyle(from.captionStyle, to.captionStyle, t),\n }\n}\n\n/** The `theme` entry's resolve: merges a partial {@link PieChartTheme} onto `previous` (first resolution: the default). */\nexport function resolvePieChartTheme(\n theme: PieChartTheme | undefined,\n previous?: PieChartConfig\n): PieChartConfig {\n const base = previous ?? DEFAULT_PIE_CHART_THEME\n return {\n sliceFills: resolveSeriesFills(theme?.sliceFills, base.sliceFills),\n ring: resolveRingConfig(theme?.ring, base.ring),\n label: resolveLabelConfig(theme?.label, base.label),\n center: resolveCenterConfig(theme?.center, base.center),\n legend: resolveLegendConfig(theme?.legend, base.legend),\n spotlight: {\n dimOpacity: theme?.spotlight?.dimOpacity ?? base.spotlight.dimOpacity,\n },\n }\n}\n\nexport function lerpPieChartTheme(\n from: PieChartConfig,\n to: PieChartConfig,\n t: number\n): PieChartConfig {\n return {\n sliceFills: lerpSeriesFills(from.sliceFills, to.sliceFills, t),\n ring: lerpRingConfig(from.ring, to.ring, t),\n label: lerpLabelConfig(from.label, to.label, t),\n center: lerpCenterConfig(from.center, to.center, t),\n legend: lerpLegendConfig(from.legend, to.legend, t),\n spotlight: {\n dimOpacity: lerpNumber(\n from.spotlight.dimOpacity,\n to.spotlight.dimOpacity,\n t\n ),\n },\n }\n}\n", "import { LineChart } from \"./line-chart\";\nimport { BarChart } from \"./bar-chart\";\nimport { PieChart } from \"./pie-chart\";\n\n// Typed by name, so the declaration names each component rather than spelling out its type through core.\nexport const NODES: (typeof LineChart | typeof BarChart | typeof PieChart)[] = [LineChart, BarChart, PieChart];\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["Rect", "Types", "component2d", "easeOut", "formatNumber", "Graphics2D", "Types", "component2d", "strokeOps", "Fills", "fillOps", "lerpNumber", "strokeOps", "niceStep", "EMPTY_REGION_BOX", "hasRegion", "box", "resolveRegionBox", "region", "plot", "xScale", "yScale", "span", "range", "min", "max", "lo", "hi", "a", "b", "toPx", "v", "left", "right", "xBounded", "bottom", "top", "yBounded", "clipPolylineToBox", "points", "runs", "run", "i", "seg", "clipSegment", "samePoint", "dx", "dy", "t0", "t1", "edges", "p", "q", "r", "LINE_CHART_LEGEND", "DEFAULT_LEGEND", "DEFAULT_CHART_THEME", "DEFAULT_SERIES_STROKES", "DEFAULT_X_AXIS", "DEFAULT_Y_AXIS", "DEFAULT_PLOT_AREA", "CHART_PALETTE", "Fills", "SPOTLIGHT_DIM", "resolveRegionConfig", "previous", "strokeOps", "fillOps", "lerpRegionConfig", "from", "to", "t", "lerpNumber", "resolveChartTheme", "theme", "base", "resolveSeriesStrokes", "resolveAxisConfig", "resolvePlotArea", "resolveLegendConfig", "lerpChartTheme", "lerpSeriesStrokes", "lerpAxisConfig", "lerpPlotArea", "lerpLegendConfig", "seriesPoints", "data", "x", "series", "row", "num", "yExtent", "s", "resolveYScale", "divisions", "segments", "constantScale", "step", "niceStep", "value", "tracePoints", "pts", "p", "segLen", "total", "i", "d", "target", "out", "t", "ChartLine", "component2d", "fresh", "Types", "strokeOps", "EMPTY_REGION_BOX", "SPOTLIGHT_DIM", "loader", "state", "loadPaints", "timeline", "from", "to", "ease", "frameEase", "progress", "ctx", "drawn", "stroke", "radius", "box", "hasRegion", "Graphics2D", "run", "clipPolylineToBox", "Graphics2D", "Types", "component2d", "fillOps", "strokeOps", "ChartRegion", "component2d", "fresh", "EMPTY_REGION_BOX", "Types", "DEFAULT_CHART_THEME", "strokeOps", "fillOps", "loader", "state", "loadPaints", "ctx", "box", "hasRegion", "r", "width", "height", "cx", "cy", "Graphics2D", "rule", "points", "half", "x", "y", "Rect", "Text", "Types", "component2d", "insetsOps", "textOps", "enterBudget", "SLIDE", "HorizontalAxis", "component2d", "fresh", "Types", "DEFAULT_X_AXIS", "arrivalsEntry", "state", "tickPad", "insetsOps", "titlePad", "column", "box", "sorted", "a", "b", "fracAt", "value", "min", "max", "markers", "_", "i", "marker", "Text", "textOps", "markerCaption", "width", "centred", "limit", "arrivalOf", "children", "Rect", "timeline", "stagger", "slide", "count", "captioned", "from", "keys", "arm", "armed", "step", "per", "caption", "enterBudget", "ease", "frameEase", "arrival", "label", "beats", "formatNumber", "DAY", "MONTHS", "NOMINAL_YEAR", "monthIndex", "word", "month", "isoWeekStart", "year", "week", "jan4", "weekday", "parseInstant", "raw", "s", "ymd", "day", "n", "named", "y", "dayNamed", "formatInstant", "value", "span", "date", "label", "isNumericColumn", "data", "field", "sawValue", "row", "v", "instantColumn", "parsed", "t", "last", "extentOf", "values", "lo", "hi", "sampledTicks", "positions", "min", "max", "divisions", "inside", "unique", "a", "b", "markerValues", "nearest", "target", "best", "out", "tick", "labelBySample", "captions", "fallback", "i", "scales", "resolveXScale", "pattern", "byKey", "key", "scale", "xScaleOf", "num", "formatMarker", "text", "instants", "extent", "range", "_", "REGION_ARG", "Types", "columnOf", "s", "format", "resolveXScale", "drawnX", "domain", "column", "range", "DEFAULT_MARKERS", "drawnY", "divisions", "resolveYScale", "markerValues", "domainsDrawn", "lineChartDomains", "data", "xField", "xAxis", "yAxis", "series", "kept", "domains", "settleAxisDomain", "d", "seriesPixels", "index", "plot", "points", "seriesPoints", "w", "h", "xMin", "xMax", "yMin", "yMax", "xToPx", "x", "yToPx", "y", "regionBox", "resolveRegionBox", "hasCallout", "region", "regionArg", "span", "a", "b", "rescale", "ticks", "to", "axesAt", "from", "t", "lerpAxisDomain", "partsOf", "children", "frame", "cartesianViews", "hAxis", "HorizontalAxis", "VerticalAxis", "ChartLine", "ChartLegend", "clearing", "timeline", "state", "toggling", "show", "parts", "line", "LineChart", "component2d", "fresh", "resolveAxisDomain", "lerpAxisEntry", "DEFAULT_CHART_THEME", "resolveChartTheme", "lerpChartTheme", "theme", "plotSize", "laidOutSize", "enabled", "vAxis", "markers", "value", "formatMarker", "markerStep", "formatNumber", "lines", "i", "resolveSeriesStroke", "below", "resolveSeriesFill", "Rect", "ChartCanvas", "ChartRegion", "ChartBorder", "gutter", "cartesianFrame", "easeOut", "duration", "revealing", "body", "axes", "ENTER_SHARE", "traced", "legend", "at0", "piece", "beats", "LEGEND_SLIDE", "previous", "home", "SERIES_ARG", "_state", "SPOTLIGHT_DIM", "Rect", "Types", "clamp", "component2d", "easeOut", "formatNumber", "Rect", "Text", "Types", "clamp", "component2d", "insetsOps", "textOps", "enterBudget", "SLIDE", "CategoryAxis", "component2d", "fresh", "Types", "DEFAULT_X_AXIS", "arrivalsEntry", "state", "tickPad", "insetsOps", "titlePad", "column", "width", "slotCenter", "index", "n", "slots", "caption", "i", "Rect", "clamp", "Text", "textOps", "arrivalOf", "children", "timeline", "stagger", "slide", "count", "captioned", "from", "keys", "arm", "armed", "step", "per", "enterBudget", "ease", "frameEase", "lastCaptionEnds", "captions", "_", "arrival", "label", "beats", "Graphics2D", "Text", "Types", "clamp", "component2d", "fillOps", "shadowOps", "strokeOps", "textOps", "fillOps", "lerpNumber", "shadowOps", "strokeOps", "textOps", "niceStep", "barSpans", "row", "series", "stacked", "running", "s", "value", "num", "from", "stackCapIndex", "last", "i", "barCategories", "data", "field", "text", "barValueExtent", "lo", "hi", "span", "resolveBarScale", "range", "divisions", "segments", "step", "niceStep", "DEFAULT_BAR_CHART_THEME", "DEFAULT_SERIES_FILLS", "DEFAULT_X_AXIS", "DEFAULT_Y_AXIS", "DEFAULT_PLOT_AREA", "DEFAULT_LEGEND", "CHART_PALETTE", "SPOTLIGHT_DIM", "resolveBarStyle", "bar", "previous", "strokeOps", "shadowOps", "lerpBarStyle", "to", "t", "lerpNumber", "resolveBarChartTheme", "theme", "base", "resolveSeriesFills", "resolveAxisConfig", "resolvePlotArea", "resolveLegendConfig", "textOps", "lerpBarChartTheme", "lerpSeriesFills", "lerpAxisConfig", "lerpPlotArea", "lerpLegendConfig", "resolveBarFill", "palette", "index", "fillOps", "VALUE_GAP", "VALUE_FIT", "VALUE_SIZE", "dimAt", "s", "index", "clamp", "endsAt", "bar", "g", "valueSize", "size", "insideAt", "captionAt", "ends", "formatMarker", "captionY", "direction", "ChartBars", "component2d", "fresh", "Types", "fillOps", "DEFAULT_BAR_CHART_THEME", "strokeOps", "shadowOps", "state", "_", "i", "Text", "textOps", "loader", "loadPaints", "timeline", "ease", "frameEase", "t", "ctx", "height", "radius", "Graphics2D", "rect", "drawnValue", "s", "domain", "divisions", "DEFAULT_MARKERS", "range", "resolveBarScale", "markerValues", "domainsDrawn", "barChartDomain", "data", "series", "stacked", "valueAxis", "kept", "settleAxisDomain", "d", "valueLabel", "value", "pattern", "formatMarker", "markerStep", "formatNumber", "valueToY", "plot", "min", "max", "baselinePx", "weightAt", "index", "weightedRow", "row", "scaled", "i", "seriesBars", "captioned", "n", "slotWidth", "slotCenter", "inner", "clamp", "gap", "total", "unit", "weight", "before", "rowIndex", "span", "barSpans", "cap", "stackCapIndex", "slotLeft", "x", "width", "num", "categoryDims", "focus", "dim", "chosen", "_", "partsOf", "children", "frame", "cartesianViews", "cAxis", "CategoryAxis", "VerticalAxis", "ChartBars", "ChartLegend", "toggling", "timeline", "state", "target", "parts", "from", "start", "showing", "t", "weights", "shown", "BarChart", "component2d", "fresh", "Types", "resolveAxisDomain", "lerpAxisEntry", "lerpAxisDomain", "DEFAULT_BAR_CHART_THEME", "resolveBarChartTheme", "lerpBarChartTheme", "theme", "plotSize", "laidOutSize", "vAxis", "barCategories", "barSets", "resolveBarFill", "below", "Rect", "ChartCanvas", "ChartBorder", "gutter", "cartesianFrame", "easeOut", "_state", "duration", "axes", "ENTER_SHARE", "rise", "legend", "at0", "piece", "beats", "LEGEND_SLIDE", "SERIES_ARG", "bars", "lit", "Rect", "Types", "clamp", "component2d", "easeOut", "Flow", "Paint", "Rect", "Shapes", "Text", "Types", "clamp", "component2d", "textOps", "SLIDE", "PieLegend", "component2d", "Paint", "Flow", "Shapes", "fresh", "Types", "DEFAULT_LEGEND", "arrivalsEntry", "state", "marker", "_", "index", "Rect", "clamp", "arrivalOf", "Text", "textOps", "timeline", "armed", "count", "from", "arm", "per", "step", "ease", "frameEase", "entries", "i", "arrival", "beats", "measurer", "children", "box", "ctx", "Graphics2D", "PathBuilder", "Rect", "Text", "Types", "clamp", "component2d", "fillOps", "shadowOps", "strokeOps", "textOps", "clamp", "lerpNumber", "shadowOps", "strokeOps", "textOps", "resolvePieSlices", "data", "category", "value", "palette", "row", "text", "num", "DEG", "selectionFade", "focus", "dimOpacity", "pop", "clamp", "sliceValue", "slice", "sliceTotal", "slices", "sum", "ringPoint", "r", "a", "pieArcs", "startDegrees", "total", "arcs", "cursor", "index", "fraction", "a0", "a1", "relaxLabels", "slots", "minGap", "top", "bottom", "b", "ceiling", "slot", "floor", "i", "LABEL_TEXT_STYLE", "CHART_PALETTE", "PIE_SPOTLIGHT_DIM", "DEFAULT_PIE_CHART_THEME", "DEFAULT_SERIES_FILLS", "DEFAULT_LEGEND", "resolveRingConfig", "ring", "previous", "strokeOps", "shadowOps", "lerpRingConfig", "from", "to", "t", "lerpNumber", "resolveLabelConfig", "label", "textOps", "lerpLabelConfig", "resolveCenterConfig", "center", "lerpCenterConfig", "resolvePieChartTheme", "theme", "base", "resolveSeriesFills", "resolveLegendConfig", "lerpPieChartTheme", "lerpSeriesFills", "lerpLegendConfig", "LABEL_GAP", "LABEL_PAD", "GLYPH_RATIO", "LEADER_LIFTOFF", "LEADER_LENGTH", "LABEL_STUB", "TEXT_INSET", "MIN_ELBOW", "ARC_STEPS", "ELBOW_RADIUS", "BARE_PAD", "INSIDE_CAPTION_RATIO", "PIE_LABEL_RADIUS", "CENTER_LABEL_FIT", "INSIDE_LABEL_FIT", "AXIS_EPSILON", "FALLBACK_SIZE", "sizeOf", "style", "fallback", "size", "wedgePath", "a0", "a1", "rInner", "rOuter", "halfGap", "cx", "path", "PathBuilder", "span", "steps", "insetAt", "r", "arcTo", "i", "inset", "from", "to", "point", "ringPoint", "p", "RingGeometry", "s", "box", "pieArcs", "arc", "outer", "hole", "band", "mid", "sin", "cos", "acrossBand", "alongArc", "mode", "category", "percent", "formatPercent", "caption", "fits", "centerStyle", "centerLabel", "centerCaption", "wanted", "captionRatio", "glyphs", "index", "gutters", "glyph", "room", "key", "sideOf", "left", "right", "configured", "px", "clamp", "rim", "a", "strongest", "selectionFade", "arcs", "radius", "rowHeight", "top", "bottom", "side", "ideal", "relaxLabels", "slot", "start", "onCircle", "elbowX", "endX", "placement", "NO_BOX", "resolvedFills", "resolvedFill", "fill", "fillOps", "resolved", "ringFills", "slices", "slice", "ringLabels", "blocks", "geometry", "label", "opacity", "wantsCategory", "wantsPercent", "inside", "Text", "textOps", "Rect", "at", "children", "PieRing", "component2d", "fresh", "Types", "DEFAULT_PIE_CHART_THEME", "state", "loader", "loadPaints", "shadowOps", "strokeOps", "timeline", "ease", "frameEase", "t", "ctx", "g", "sweep", "revealEnd", "half", "centerBounds", "fills", "wedge", "Graphics2D", "reveal", "SWEEP_SHARE", "LABEL_SHARE", "SLICE_ARG", "Types", "slicesOf", "s", "resolvePieSlices", "popAt", "index", "clamp", "isSliceSelected", "sliceDims", "focus", "i", "dim", "_", "selectionFade", "popping", "timeline", "pop", "from", "targets", "value", "t", "partsOf", "children", "row", "ring", "Rect", "PieRing", "PieLegend", "PieChart", "component2d", "fresh", "DEFAULT_PIE_CHART_THEME", "resolvePieChartTheme", "lerpPieChartTheme", "state", "theme", "slices", "parts", "easeOut", "_state", "legend", "sweep", "labels", "at0", "piece", "beats", "NODES", "LineChart", "BarChart", "PieChart"]
|
|
7
7
|
}
|