graphein 0.20.0 → 0.22.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/dist/animation/animate.d.ts +6 -1
- package/dist/animation/animate.d.ts.map +1 -1
- package/dist/animation/animate.js +2 -2
- package/dist/animation/animate.js.map +2 -2
- package/dist/animation/env.d.ts +5 -0
- package/dist/animation/env.d.ts.map +1 -1
- package/dist/animation/env.js +4 -0
- package/dist/animation/env.js.map +2 -2
- package/dist/animation/index.d.ts +1 -0
- package/dist/animation/index.d.ts.map +1 -1
- package/dist/animation/index.js +1 -0
- package/dist/animation/index.js.map +2 -2
- package/dist/animation/stagger.d.ts +13 -0
- package/dist/animation/stagger.d.ts.map +1 -0
- package/dist/animation/stagger.js +13 -0
- package/dist/animation/stagger.js.map +7 -0
- package/dist/axes/draw.d.ts +5 -1
- package/dist/axes/draw.d.ts.map +1 -1
- package/dist/axes/draw.js +13 -1
- package/dist/axes/draw.js.map +2 -2
- package/dist/axes/morph.d.ts +111 -0
- package/dist/axes/morph.d.ts.map +1 -0
- package/dist/axes/morph.js +207 -0
- package/dist/axes/morph.js.map +7 -0
- package/dist/axes/overlay.d.ts.map +1 -1
- package/dist/axes/overlay.js +88 -25
- package/dist/axes/overlay.js.map +2 -2
- package/dist/axes/text.d.ts +2 -0
- package/dist/axes/text.d.ts.map +1 -1
- package/dist/axes/text.js +1 -0
- package/dist/axes/text.js.map +2 -2
- package/dist/axes/tickLabels.d.ts +2 -0
- package/dist/axes/tickLabels.d.ts.map +1 -1
- package/dist/axes/tickLabels.js +2 -2
- package/dist/axes/tickLabels.js.map +2 -2
- package/dist/charts/annotations.d.ts +9 -3
- package/dist/charts/annotations.d.ts.map +1 -1
- package/dist/charts/annotations.js +82 -27
- package/dist/charts/annotations.js.map +2 -2
- package/dist/charts/area.d.ts +37 -1
- package/dist/charts/area.d.ts.map +1 -1
- package/dist/charts/area.js +35 -17
- package/dist/charts/area.js.map +2 -2
- package/dist/charts/bar.d.ts +53 -0
- package/dist/charts/bar.d.ts.map +1 -1
- package/dist/charts/bar.js +117 -83
- package/dist/charts/bar.js.map +2 -2
- package/dist/charts/box.d.ts +31 -0
- package/dist/charts/box.d.ts.map +1 -1
- package/dist/charts/box.js +93 -70
- package/dist/charts/box.js.map +2 -2
- package/dist/charts/bubbleMap.d.ts +20 -0
- package/dist/charts/bubbleMap.d.ts.map +1 -0
- package/dist/charts/bubbleMap.js +951 -0
- package/dist/charts/bubbleMap.js.map +7 -0
- package/dist/charts/bullet.d.ts.map +1 -1
- package/dist/charts/bullet.js +69 -51
- package/dist/charts/bullet.js.map +2 -2
- package/dist/charts/calendarHeatmap.d.ts.map +1 -1
- package/dist/charts/calendarHeatmap.js +56 -19
- package/dist/charts/calendarHeatmap.js.map +2 -2
- package/dist/charts/candlestick.d.ts +40 -0
- package/dist/charts/candlestick.d.ts.map +1 -0
- package/dist/charts/candlestick.js +325 -0
- package/dist/charts/candlestick.js.map +7 -0
- package/dist/charts/choropleth.d.ts.map +1 -1
- package/dist/charts/choropleth.js +46 -67
- package/dist/charts/choropleth.js.map +2 -2
- package/dist/charts/combo.d.ts.map +1 -1
- package/dist/charts/combo.js +127 -27
- package/dist/charts/combo.js.map +3 -3
- package/dist/charts/dumbbell.d.ts.map +1 -1
- package/dist/charts/dumbbell.js +100 -31
- package/dist/charts/dumbbell.js.map +2 -2
- package/dist/charts/funnel.d.ts.map +1 -1
- package/dist/charts/funnel.js +93 -28
- package/dist/charts/funnel.js.map +3 -3
- package/dist/charts/gantt.d.ts +22 -0
- package/dist/charts/gantt.d.ts.map +1 -0
- package/dist/charts/gantt.js +564 -0
- package/dist/charts/gantt.js.map +7 -0
- package/dist/charts/gauge.d.ts.map +1 -1
- package/dist/charts/gauge.js +30 -4
- package/dist/charts/gauge.js.map +2 -2
- package/dist/charts/geo.d.ts +87 -0
- package/dist/charts/geo.d.ts.map +1 -0
- package/dist/charts/geo.js +140 -0
- package/dist/charts/geo.js.map +7 -0
- package/dist/charts/geometry.d.ts +20 -0
- package/dist/charts/geometry.d.ts.map +1 -0
- package/dist/charts/geometry.js +25 -0
- package/dist/charts/geometry.js.map +7 -0
- package/dist/charts/heatmap.d.ts.map +1 -1
- package/dist/charts/heatmap.js +58 -49
- package/dist/charts/heatmap.js.map +2 -2
- package/dist/charts/histogram.d.ts +1 -1
- package/dist/charts/histogram.d.ts.map +1 -1
- package/dist/charts/histogram.js +49 -31
- package/dist/charts/histogram.js.map +2 -2
- package/dist/charts/index.d.ts +35 -1
- package/dist/charts/index.d.ts.map +1 -1
- package/dist/charts/index.js +12 -0
- package/dist/charts/index.js.map +2 -2
- package/dist/charts/legibility.d.ts +38 -0
- package/dist/charts/legibility.d.ts.map +1 -0
- package/dist/charts/legibility.js +72 -0
- package/dist/charts/legibility.js.map +7 -0
- package/dist/charts/line.d.ts +39 -1
- package/dist/charts/line.d.ts.map +1 -1
- package/dist/charts/line.js +48 -16
- package/dist/charts/line.js.map +2 -2
- package/dist/charts/matrix.d.ts +2 -1
- package/dist/charts/matrix.d.ts.map +1 -1
- package/dist/charts/matrix.js +481 -319
- package/dist/charts/matrix.js.map +3 -3
- package/dist/charts/morphFrame.d.ts +83 -0
- package/dist/charts/morphFrame.d.ts.map +1 -0
- package/dist/charts/morphFrame.js +128 -0
- package/dist/charts/morphFrame.js.map +7 -0
- package/dist/charts/pie.d.ts.map +1 -1
- package/dist/charts/pie.js +89 -26
- package/dist/charts/pie.js.map +2 -2
- package/dist/charts/pieState.d.ts +27 -0
- package/dist/charts/pieState.d.ts.map +1 -0
- package/dist/charts/pieState.js +55 -0
- package/dist/charts/pieState.js.map +7 -0
- package/dist/charts/radar.d.ts +62 -0
- package/dist/charts/radar.d.ts.map +1 -0
- package/dist/charts/radar.js +912 -0
- package/dist/charts/radar.js.map +7 -0
- package/dist/charts/radialBar.d.ts +60 -0
- package/dist/charts/radialBar.d.ts.map +1 -0
- package/dist/charts/radialBar.js +708 -0
- package/dist/charts/radialBar.js.map +7 -0
- package/dist/charts/sankey.d.ts.map +1 -1
- package/dist/charts/sankey.js +223 -57
- package/dist/charts/sankey.js.map +3 -3
- package/dist/charts/scatter.d.ts +31 -0
- package/dist/charts/scatter.d.ts.map +1 -1
- package/dist/charts/scatter.js +48 -12
- package/dist/charts/scatter.js.map +2 -2
- package/dist/charts/slope.d.ts.map +1 -1
- package/dist/charts/slope.js +55 -3
- package/dist/charts/slope.js.map +2 -2
- package/dist/charts/sunburst.d.ts +14 -0
- package/dist/charts/sunburst.d.ts.map +1 -0
- package/dist/charts/sunburst.js +885 -0
- package/dist/charts/sunburst.js.map +7 -0
- package/dist/charts/table.d.ts +2 -1
- package/dist/charts/table.d.ts.map +1 -1
- package/dist/charts/table.js +214 -224
- package/dist/charts/table.js.map +3 -3
- package/dist/charts/tableView/canvas.d.ts +16 -1
- package/dist/charts/tableView/canvas.d.ts.map +1 -1
- package/dist/charts/tableView/canvas.js +361 -252
- package/dist/charts/tableView/canvas.js.map +3 -3
- package/dist/charts/tableView/conditional.d.ts +66 -40
- package/dist/charts/tableView/conditional.d.ts.map +1 -1
- package/dist/charts/tableView/conditional.js +163 -256
- package/dist/charts/tableView/conditional.js.map +3 -3
- package/dist/charts/tableView/dom.d.ts +26 -2
- package/dist/charts/tableView/dom.d.ts.map +1 -1
- package/dist/charts/tableView/dom.js +609 -269
- package/dist/charts/tableView/dom.js.map +3 -3
- package/dist/charts/tableView/layout.d.ts +22 -0
- package/dist/charts/tableView/layout.d.ts.map +1 -0
- package/dist/charts/tableView/layout.js +246 -0
- package/dist/charts/tableView/layout.js.map +7 -0
- package/dist/charts/tableView/sketch.d.ts +3 -2
- package/dist/charts/tableView/sketch.d.ts.map +1 -1
- package/dist/charts/tableView/sketch.js +2 -2
- package/dist/charts/tableView/sketch.js.map +2 -2
- package/dist/charts/tableView/sketchBar.d.ts +7 -0
- package/dist/charts/tableView/sketchBar.d.ts.map +1 -0
- package/dist/charts/tableView/sketchBar.js +32 -0
- package/dist/charts/tableView/sketchBar.js.map +7 -0
- package/dist/charts/tableView/sort.d.ts +9 -0
- package/dist/charts/tableView/sort.d.ts.map +1 -0
- package/dist/charts/tableView/sort.js +70 -0
- package/dist/charts/tableView/sort.js.map +7 -0
- package/dist/charts/tableView/sparkline.d.ts +30 -0
- package/dist/charts/tableView/sparkline.d.ts.map +1 -0
- package/dist/charts/tableView/sparkline.js +63 -0
- package/dist/charts/tableView/sparkline.js.map +7 -0
- package/dist/charts/tableView/style.d.ts +67 -0
- package/dist/charts/tableView/style.d.ts.map +1 -0
- package/dist/charts/tableView/style.js +131 -0
- package/dist/charts/tableView/style.js.map +7 -0
- package/dist/charts/tableView/types.d.ts +69 -46
- package/dist/charts/tableView/types.d.ts.map +1 -1
- package/dist/charts/tableView/types.js +22 -87
- package/dist/charts/tableView/types.js.map +2 -2
- package/dist/charts/tableView.d.ts +10 -5
- package/dist/charts/tableView.d.ts.map +1 -1
- package/dist/charts/tableView.js +12 -8
- package/dist/charts/tableView.js.map +2 -2
- package/dist/charts/treemap.d.ts.map +1 -1
- package/dist/charts/treemap.js +177 -68
- package/dist/charts/treemap.js.map +3 -3
- package/dist/charts/trendline.d.ts +13 -5
- package/dist/charts/trendline.d.ts.map +1 -1
- package/dist/charts/trendline.js +205 -56
- package/dist/charts/trendline.js.map +2 -2
- package/dist/charts/waterfall.d.ts.map +1 -1
- package/dist/charts/waterfall.js +76 -5
- package/dist/charts/waterfall.js.map +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/interaction/cartesian.d.ts +4 -1
- package/dist/interaction/cartesian.d.ts.map +1 -1
- package/dist/interaction/cartesian.js +104 -34
- package/dist/interaction/cartesian.js.map +2 -2
- package/dist/interaction/controller.d.ts +29 -1
- package/dist/interaction/controller.d.ts.map +1 -1
- package/dist/interaction/controller.js +131 -8
- package/dist/interaction/controller.js.map +2 -2
- package/dist/interaction/focus.d.ts +23 -0
- package/dist/interaction/focus.d.ts.map +1 -0
- package/dist/interaction/focus.js +65 -0
- package/dist/interaction/focus.js.map +7 -0
- package/dist/interaction/tooltip.d.ts +6 -0
- package/dist/interaction/tooltip.d.ts.map +1 -1
- package/dist/interaction/tooltip.js +41 -16
- package/dist/interaction/tooltip.js.map +2 -2
- package/dist/interaction/types.d.ts +19 -0
- package/dist/interaction/types.d.ts.map +1 -1
- package/dist/interaction/veil.d.ts +25 -0
- package/dist/interaction/veil.d.ts.map +1 -0
- package/dist/interaction/veil.js +35 -0
- package/dist/interaction/veil.js.map +7 -0
- package/dist/pivot/pivot.d.ts +18 -0
- package/dist/pivot/pivot.d.ts.map +1 -1
- package/dist/pivot/pivot.js +46 -34
- package/dist/pivot/pivot.js.map +2 -2
- package/dist/render/chromeMotion.d.ts +65 -0
- package/dist/render/chromeMotion.d.ts.map +1 -0
- package/dist/render/chromeMotion.js +79 -0
- package/dist/render/chromeMotion.js.map +7 -0
- package/dist/render/pathSample.d.ts +24 -0
- package/dist/render/pathSample.d.ts.map +1 -0
- package/dist/render/pathSample.js +76 -0
- package/dist/render/pathSample.js.map +7 -0
- package/dist/render/transient.d.ts +18 -0
- package/dist/render/transient.d.ts.map +1 -0
- package/dist/render/transient.js +19 -0
- package/dist/render/transient.js.map +7 -0
- package/dist/runtime/candlestick.d.ts +112 -0
- package/dist/runtime/candlestick.d.ts.map +1 -0
- package/dist/runtime/candlestick.js +531 -0
- package/dist/runtime/candlestick.js.map +7 -0
- package/dist/runtime/cartesian.d.ts +14 -3
- package/dist/runtime/cartesian.d.ts.map +1 -1
- package/dist/runtime/cartesian.js.map +2 -2
- package/dist/runtime/chartAnimation.d.ts +54 -7
- package/dist/runtime/chartAnimation.d.ts.map +1 -1
- package/dist/runtime/chartAnimation.js +146 -28
- package/dist/runtime/chartAnimation.js.map +2 -2
- package/dist/runtime/chartRuntime.d.ts +42 -0
- package/dist/runtime/chartRuntime.d.ts.map +1 -1
- package/dist/runtime/chartRuntime.js +196 -30
- package/dist/runtime/chartRuntime.js.map +2 -2
- package/dist/runtime/dashboardDom.d.ts.map +1 -1
- package/dist/runtime/dashboardDom.js +45 -0
- package/dist/runtime/dashboardDom.js.map +2 -2
- package/dist/runtime/facet.d.ts +1 -1
- package/dist/runtime/facet.d.ts.map +1 -1
- package/dist/runtime/gantt.d.ts +137 -0
- package/dist/runtime/gantt.d.ts.map +1 -0
- package/dist/runtime/gantt.js +647 -0
- package/dist/runtime/gantt.js.map +7 -0
- package/dist/runtime/morph.d.ts +33 -0
- package/dist/runtime/morph.d.ts.map +1 -0
- package/dist/runtime/morph.js +332 -0
- package/dist/runtime/morph.js.map +7 -0
- package/dist/runtime/radar.d.ts +75 -0
- package/dist/runtime/radar.d.ts.map +1 -0
- package/dist/runtime/radar.js +201 -0
- package/dist/runtime/radar.js.map +7 -0
- package/dist/runtime/radialBar.d.ts +81 -0
- package/dist/runtime/radialBar.d.ts.map +1 -0
- package/dist/runtime/radialBar.js +125 -0
- package/dist/runtime/radialBar.js.map +7 -0
- package/dist/runtime/report/constants.d.ts +2 -2
- package/dist/runtime/report/constants.d.ts.map +1 -1
- package/dist/runtime/runtimeShared.d.ts +3 -3
- package/dist/runtime/runtimeShared.d.ts.map +1 -1
- package/dist/runtime/sunburst.d.ts +74 -0
- package/dist/runtime/sunburst.d.ts.map +1 -0
- package/dist/runtime/sunburst.js +150 -0
- package/dist/runtime/sunburst.js.map +7 -0
- package/dist/runtime/wire.d.ts.map +1 -1
- package/dist/runtime/wire.js +3 -1
- package/dist/runtime/wire.js.map +2 -2
- package/dist/shape/arc.d.ts +15 -2
- package/dist/shape/arc.d.ts.map +1 -1
- package/dist/shape/arc.js +204 -32
- package/dist/shape/arc.js.map +2 -2
- package/dist/spec/chartDefinitions.d.ts +6 -0
- package/dist/spec/chartDefinitions.d.ts.map +1 -1
- package/dist/spec/chartDefinitions.js +7 -1
- package/dist/spec/chartDefinitions.js.map +2 -2
- package/dist/spec/templates.d.ts.map +1 -1
- package/dist/spec/templates.js +177 -2
- package/dist/spec/templates.js.map +2 -2
- package/dist/spec/types.d.ts +258 -6
- package/dist/spec/types.d.ts.map +1 -1
- package/dist/spec/types.js +6 -0
- package/dist/spec/types.js.map +2 -2
- package/dist/spec/validate/charts.js +147 -15
- package/dist/spec/validate/charts.js.map +2 -2
- package/dist/spec/validate/common.d.ts +6 -0
- package/dist/spec/validate/common.d.ts.map +1 -1
- package/dist/spec/validate/overlays.js +2 -2
- package/dist/spec/validate/overlays.js.map +2 -2
- package/dist/spec/validate.data.d.ts.map +1 -1
- package/dist/spec/validate.data.js +8 -3
- package/dist/spec/validate.data.js.map +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/charts/radialBar.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Radial bar renderer.\n *\n * Two layouts share one keyed mark model:\n * - `radial` (default): one concentric track per category, the first category\n * outermost. Each arc sweeps clockwise from 12 o'clock with a length\n * proportional to `value / max` over at most `sweep` degrees (progress rings).\n * - `polar`: equal-angle wedges starting at 12 o'clock whose AREA is\n * proportional to `value / max` \u2014 the radius grows with the square root \u2014 a\n * polar-area (\"nightingale rose\") chart.\n *\n * Values above `max` fill their track and carry a small chevron so overflow is\n * never silently hidden; the tooltip shows the true value and its share of max.\n * Labels name each arc at its start (radial) or around the rim (polar); when\n * they cannot fit, a legend takes over. Arcs sweep in on entrance, and an update\n * carries every arc to its new length (new categories grow in, removed ones\n * shrink away) instead of cross-fading.\n */\n\nimport { contrastRatio, parseColor, rgbaToCss } from '../color';\nimport { formatValue } from '../format';\nimport type { Hover, HoverPaintState, InteractionModel, TooltipRow } from '../interaction/types';\nimport { FOCUS_VEIL, hoverFocusEnabled, paintCutoutVeil } from '../interaction/veil';\nimport type { MarkBoundary } from '../render/evidence';\nimport { boxesOverlap, fitLabel } from '../render/labelLedger';\nimport type { Surface } from '../render/surface';\nimport { fontString, measureText } from '../render/text';\nimport { RoughPen } from '../rough';\nimport {\n buildRadialBarModel,\n minArcSpan,\n polarRadius,\n radialBarPalette,\n radialTracks,\n sectorBounds,\n sectorExtents,\n TAU,\n valueAngle,\n type RadialBarModel,\n type TrackLayout,\n} from '../runtime/radialBar';\nimport { arc, type PathSink } from '../shape';\nimport { resolveSketch } from '../spec/sketch';\nimport type { ChartSpec, RadialBarSpec } from '../spec/types';\nimport type { ThemeTokens } from '../theme';\nimport type { Insets, Rect, RGBA, Size } from '../types';\nimport { addOverlayText, drawCategoricalLegend, drawTitleBlock, type LegendEntry } from './chrome';\nimport { rowAlpha } from './emphasis';\nimport type { RenderContext } from './index';\nimport { compositeOver, contrastTarget, inkOn, knownBackdrop, toneForContrast } from './legibility';\nimport { lerp, lerpValue, mixColor, morphInput, morphKeyed, stashMorphFrame, switchAt, type MorphPlan } from './morphFrame';\nimport { drawNoData } from './noData';\n\ntype Layout = 'radial' | 'polar';\n\n/** Gap between a radial label's right edge and the 12 o'clock line. */\nconst LABEL_GAP = 8;\n/** Gap between a label's category name and its value. */\nconst NAME_GAP = 5;\n/** Distance from the rose's rim to its outside labels. */\nconst POLAR_LABEL_OFFSET = 8;\n/** Thinnest wedge (px) a positive value draws in the polar layout. */\nconst MIN_POLAR_THICKNESS = 2;\n/** Fills fainter than this against the backdrop get a tone-on-tone hairline (the report's mark floor is 1.5). */\nconst PALE_CONTRAST = 1.6;\nconst EPSILON = 1e-6;\n\n/** An annular sector: radii, clockwise angles from 12 o'clock, rounding and gaps. */\ninterface Sector {\n inner: number;\n outer: number;\n start: number;\n end: number;\n corner: number;\n pad: number;\n /** Radius the pad gap is measured at, so gaps stay parallel-sided. */\n padRadius: number;\n}\n\n/** An arc or wedge as drawn, keyed by category so an update carries it to its new value. */\nexport interface RadialBarMark {\n key: string;\n layout: Layout;\n label: string;\n /** Displayed value; counts between datasets mid-morph. */\n value: number;\n color: string;\n cx: number;\n cy: number;\n /** The value arc (radial) or wedge (polar). */\n arc: Sector;\n /** Its background track (radial) or full-radius slot (polar). */\n track: Sector;\n /** Overflow-indicator strength: 1 when the value exceeds `max`. */\n over: number;\n /** Highlight alpha, carried so emphasis changes fade instead of snapping. */\n emph: number;\n /** Fade for categories arriving or leaving mid-morph. */\n alpha: number;\n}\n\ninterface LabelStyle {\n size: number;\n lineH: number;\n nameFont: string;\n valueFont: string;\n}\n\n/** The resolved layout of one draw. */\ninterface Frame {\n layout: Layout;\n /** Drawing area after title, padding and legend. */\n area: Rect;\n cx: number;\n cy: number;\n radius: number;\n hole: number;\n /** Largest arc sweep (radial); a full turn for polar. */\n sweep: number;\n /** Radial track thickness, pitch and radii (outermost first). */\n tracks: TrackLayout;\n /** Polar wedge span and pad angle. */\n span: number;\n pad: number;\n /** Label style when labels are drawn, else null. */\n labels: LabelStyle | null;\n legend: boolean;\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value));\n}\n\nfunction finiteOr(value: unknown, fallback: number): number {\n return typeof value === 'number' && Number.isFinite(value) ? value : fallback;\n}\n\nfunction valueFormat(spec: RadialBarSpec): string | undefined {\n const format = spec.format ?? spec.encoding?.value?.format;\n return typeof format === 'string' ? format : undefined;\n}\n\nfunction insetRect(rect: Rect, padding: Partial<Insets> | undefined): Rect {\n const pad = (side: unknown): number => (typeof side === 'number' && Number.isFinite(side) ? Math.max(0, side) : 0);\n const p = padding && typeof padding === 'object' ? padding : {};\n const left = pad(p.left);\n const top = pad(p.top);\n return {\n x: rect.x + left,\n y: rect.y + top,\n width: Math.max(0, rect.width - left - pad(p.right)),\n height: Math.max(0, rect.height - top - pad(p.bottom)),\n };\n}\n\nfunction tooltipEnabled(spec: RadialBarSpec): boolean {\n const tooltip = spec.tooltip;\n return tooltip !== false && !(tooltip && typeof tooltip === 'object' && tooltip.show === false);\n}\n\n// ---------------------------------------------------------------------------\n// Geometry\n// ---------------------------------------------------------------------------\n\nfunction traceSector(sink: PathSink, cx: number, cy: number, s: Sector): void {\n if (s.end - s.start >= TAU - 1e-9) {\n traceRing(sink, cx, cy, s.inner, s.outer, s.start);\n return;\n }\n arc({\n innerRadius: s.inner,\n outerRadius: s.outer,\n startAngle: s.start,\n endAngle: s.end,\n cornerRadius: s.corner,\n padAngle: s.pad,\n padRadius: s.pad > 0 ? s.padRadius : undefined,\n })(sink, cx, cy);\n}\n\n/**\n * A full ring (or disk) as half-turn arcs, the outer circle clockwise and the\n * inner one counter-clockwise so the hole stays empty. Some canvas backends\n * (Skia in `@napi-rs/canvas`) drop a single arc that sweeps exactly 2\u03C0.\n */\nfunction traceRing(sink: PathSink, cx: number, cy: number, inner: number, outer: number, start: number): void {\n const a0 = start - Math.PI / 2;\n sink.moveTo(cx + outer * Math.cos(a0), cy + outer * Math.sin(a0));\n sink.arc(cx, cy, outer, a0, a0 + Math.PI, false);\n sink.arc(cx, cy, outer, a0 + Math.PI, a0 + TAU, false);\n sink.closePath();\n if (!(inner > EPSILON)) return;\n sink.moveTo(cx + inner * Math.cos(a0), cy + inner * Math.sin(a0));\n sink.arc(cx, cy, inner, a0, a0 - Math.PI, true);\n sink.arc(cx, cy, inner, a0 - Math.PI, a0 - TAU, true);\n sink.closePath();\n}\n\nfunction drawable(s: Sector): boolean {\n return s.end - s.start > EPSILON && s.outer - s.inner > EPSILON;\n}\n\nfunction lerpSector(a: Sector, b: Sector, t: number): Sector {\n return {\n inner: lerp(a.inner, b.inner, t),\n outer: lerp(a.outer, b.outer, t),\n start: lerp(a.start, b.start, t),\n end: lerp(a.end, b.end, t),\n corner: lerp(a.corner, b.corner, t),\n pad: lerp(a.pad, b.pad, t),\n padRadius: lerp(a.padRadius, b.padRadius, t),\n };\n}\n\nconst NO_TRACKS: TrackLayout = { width: 0, pitch: 0, radii: [] };\n\n/**\n * Fit concentric tracks spanning `sweep` into `area`. Labels sit left of the\n * 12 o'clock line, so when they need more room than the empty quarter offers\n * the rings may shrink (down to `minScale` of their full size) and shift right.\n */\nfunction fitRadial(area: Rect, n: number, sweep: number, holeFrac: number, column: number, lineH: number, minScale = 0.75) {\n const ext = sectorExtents(holeFrac, 1, 0, sweep);\n const spanX = Math.max(EPSILON, ext.maxX - ext.minX);\n const spanY = Math.max(EPSILON, ext.maxY - ext.minY);\n // The outermost label is centered on the outer track and may poke above it.\n const top = lineH / 2;\n const availH = Math.max(0, area.height - top);\n const full = Math.max(0, Math.min(availH / spanY, area.width / spanX));\n const labelsLeft = column > 0 && sweep <= 1.5 * Math.PI + EPSILON ? column + LABEL_GAP : 0;\n const leftOf = (r: number): number => Math.max(-ext.minX * r, labelsLeft);\n let radius = full;\n if (leftOf(radius) + ext.maxX * radius > area.width && ext.maxX > 0) {\n radius = Math.max(full * minScale, Math.min(full, (area.width - labelsLeft) / ext.maxX));\n }\n const right = ext.maxX * radius;\n const left = Math.min(leftOf(radius), Math.max(0, area.width - right));\n const cx = area.x + Math.max(0, area.width - left - right) / 2 + left;\n const cy = area.y + top + Math.max(0, availH - spanY * radius) / 2 - ext.minY * radius;\n const hole = holeFrac * radius;\n return { cx, cy, radius, hole, tracks: radialTracks(radius, hole, n), span: 0, pad: 0 };\n}\n\n/** Fit the rose into `area`, leaving a ring of room for outside labels. */\nfunction fitPolar(area: Rect, n: number, holeFrac: number, column: number, lineH: number) {\n const bare = Math.max(0, Math.min(area.width, area.height) / 2 - 1);\n let radius = bare;\n if (column > 0) {\n const byHeight = (area.height - 2 * (lineH + POLAR_LABEL_OFFSET)) / 2;\n const wanted = Math.min((area.width - 2 * (column + POLAR_LABEL_OFFSET)) / 2, byHeight);\n // Long names ellipsize rather than starve the rose.\n radius = Math.max(Math.min(bare * 0.6, byHeight), wanted);\n }\n radius = Math.max(0, radius);\n const span = TAU / Math.max(1, n);\n const pad = n > 1 && radius > 0 ? Math.min(2 / radius, span * 0.25) : 0;\n return {\n cx: area.x + area.width / 2,\n cy: area.y + area.height / 2,\n radius,\n hole: holeFrac * radius,\n tracks: NO_TRACKS,\n span,\n pad,\n };\n}\n\n/**\n * How wide the radial label of the track at `radius` may be. Up to a 270\u00B0\n * sweep the quarter left of 12 o'clock is empty; beyond it, the label stops\n * short of any track the sweep draws across its row.\n */\nfunction radialLabelBudget(area: Rect, cx: number, sweep: number, radius: number, rings: readonly number[], width: number, lineH: number): number {\n let budget = cx - LABEL_GAP - area.x;\n if (sweep <= 1.5 * Math.PI + EPSILON) return Math.max(0, budget);\n const slope = Math.tan(TAU - sweep);\n const half = width / 2;\n for (const ring of rings) {\n for (const dy of [radius - lineH / 2, radius, radius + lineH / 2]) {\n if (dy <= 0) continue;\n const outer = ring + half;\n const inner = ring - half;\n if (outer <= dy) continue;\n // Where this ring's drawn part begins along the label row (distance left of 12 o'clock).\n const from = Math.max(inner > dy ? Math.sqrt(inner * inner - dy * dy) : 0, dy * slope);\n if (from > Math.sqrt(outer * outer - dy * dy)) continue;\n budget = Math.min(budget, from - LABEL_GAP - 2);\n }\n }\n return Math.max(0, budget);\n}\n\nfunction labelStyle(tokens: ThemeTokens, size: number): LabelStyle {\n return {\n size,\n lineH: size * 1.2,\n nameFont: fontString(size, tokens.font.family, tokens.font.weight.normal),\n valueFont: fontString(size, tokens.font.family, tokens.font.weight.bold),\n };\n}\n\ninterface LegendChoice {\n mode: 'auto' | 'on' | 'off';\n position: 'top' | 'right' | 'bottom' | 'left';\n}\n\n/** A label style that fits, and how far the rings may shrink to make room for it. */\ninterface Labelling {\n style: LabelStyle;\n minScale: number;\n}\n\nfunction legendChoice(spec: RadialBarSpec, area: Rect): LegendChoice {\n const legend = spec.legend;\n const auto = area.width >= area.height * 1.15 ? 'right' : 'bottom';\n if (legend === false || (legend && typeof legend === 'object' && legend.show === false)) return { mode: 'off', position: auto };\n if (legend && typeof legend === 'object') return { mode: 'on', position: legend.position ?? auto };\n return { mode: legend === true ? 'on' : 'auto', position: auto };\n}\n\n/**\n * Resolve the layout: labels when (most) arcs can carry one; otherwise \u2014 or\n * when asked \u2014 a legend names the arcs and the rings fit the space left over.\n */\nfunction planFrame(surface: Surface, spec: RadialBarSpec, tokens: ThemeTokens, content: Rect, model: RadialBarModel): Frame {\n const layout: Layout = spec.layout === 'polar' ? 'polar' : 'radial';\n const n = model.items.length;\n const sweep = layout === 'polar' ? TAU : (clamp(finiteOr(spec.sweep, 270), 30, 360) * Math.PI) / 180;\n const holeFrac = clamp(finiteOr(spec.innerRadius, 0.25), 0, 0.9);\n const format = valueFormat(spec);\n const texts = model.items.map((item) => ({ name: item.label, value: formatValue(item.value, format) }));\n // Widest label column per font size: names alone (polar) or name + value (radial).\n const columns = new Map<number, number>();\n const columnFor = (style: LabelStyle): number => {\n let width = columns.get(style.size);\n if (width === undefined) {\n width = texts.reduce((w, t) => Math.max(w, measureText(t.name, style.nameFont).width +\n (layout === 'radial' ? NAME_GAP + measureText(t.value, style.valueFont).width : 0)), 0);\n columns.set(style.size, width);\n }\n return width;\n };\n const fit = (area: Rect, labels: Labelling | null) => {\n const style = labels?.style;\n if (layout === 'polar') {\n const column = style ? Math.min(columnFor(style), Math.max(40, area.width * 0.22)) : 0;\n return fitPolar(area, n, holeFrac, column, style?.lineH ?? 0);\n }\n return fitRadial(area, n, sweep, holeFrac, style ? columnFor(style) : 0, style?.lineH ?? 0, labels?.minScale);\n };\n const readable = (area: Rect): Labelling | null => {\n if (spec.labels === false || area.width <= 0 || area.height <= 0) return null;\n for (const size of [tokens.font.size.small, tokens.font.size.tiny]) {\n const style = labelStyle(tokens, size);\n if (layout === 'polar') {\n const g = fit(area, { style, minScale: 1 });\n if (g.radius >= 24 && (g.radius + POLAR_LABEL_OFFSET) * g.span >= style.lineH * 0.9) return { style, minScale: 1 };\n continue;\n }\n // Shrinking the rings to make room for long names must not make the tracks too thin to label;\n // full-size rings with shortened names beat a legend.\n for (const minScale of [0.75, 1]) {\n const g = fit(area, { style, minScale });\n if (!(g.radius > 0) || g.tracks.pitch < style.lineH - 1.5) continue;\n // Most tracks need room for at least a short name beside their value.\n const roomy = g.tracks.radii.filter((r, i) =>\n radialLabelBudget(area, g.cx, sweep, r, g.tracks.radii, g.tracks.width, style.lineH) >=\n Math.min(48, measureText(texts[i].value, style.valueFont).width + NAME_GAP + 24)).length;\n if (roomy * 2 >= n) return { style, minScale };\n }\n }\n return null;\n };\n\n const legend = legendChoice(spec, content);\n let labels = readable(content);\n const showLegend = legend.mode === 'on' || (legend.mode === 'auto' && labels === null);\n let area = content;\n if (showLegend) {\n const entries: LegendEntry[] = model.items.map((item) => ({ label: item.label, color: item.color, symbol: 'circle' }));\n area = drawCategoricalLegend(surface, tokens, content, entries, legend.position);\n if (labels) labels = readable(area);\n }\n if (labels === null && spec.labels !== false) {\n surface.labels?.omit({ role: 'data-label', reason: 'space', required: !showLegend, count: n, path: '/labels' });\n }\n return { layout, area, sweep, labels: labels?.style ?? null, legend: showLegend, ...fit(area, labels) };\n}\n\n// ---------------------------------------------------------------------------\n// Marks\n// ---------------------------------------------------------------------------\n\n/** Final marks for the frame, swept to `sweepProgress` on entrance (outer rings lead). */\nfunction buildMarks(frame: Frame, model: RadialBarModel, spec: RadialBarSpec, context: RenderContext | undefined): RadialBarMark[] {\n const progress = clamp(finiteOr(context?.sweepProgress, 1), 0, 1);\n const n = model.items.length;\n const stagger = n > 1 ? Math.min(0.08, 0.35 / (n - 1)) : 0;\n const categoryField = spec.encoding.category.field;\n const emphasis = context?.emphasis ?? null;\n const corner = (thickness: number): number => Math.max(0, Math.min(4, thickness * 0.22));\n return model.items.map((item, i): RadialBarMark => {\n const local = progress >= 1 ? 1 : clamp((progress - i * stagger) / (1 - (n - 1) * stagger), 0, 1);\n const shows = item.value > 0 && local > 0;\n const base = {\n key: item.key,\n layout: frame.layout,\n label: item.label,\n value: item.value,\n color: item.color,\n cx: frame.cx,\n cy: frame.cy,\n over: item.value > model.max && local >= 1 ? 1 : 0,\n emph: rowAlpha(emphasis, { [categoryField]: item.category }),\n alpha: 1,\n };\n if (frame.layout === 'radial') {\n const r = frame.tracks.radii[i];\n const w = frame.tracks.width;\n const track: Sector = { inner: r - w / 2, outer: r + w / 2, start: 0, end: frame.sweep, corner: w / 2, pad: 0, padRadius: 0 };\n const swept = valueAngle(item.value, model.max, frame.sweep) * local;\n // A positive value always shows at least a round dot.\n const end = shows ? Math.min(frame.sweep, Math.max(swept, minArcSpan(r, w))) : 0;\n return { ...base, track, arc: { ...track, end } };\n }\n const start = i * frame.span;\n const room = Math.max(0, frame.radius - frame.hole);\n const track: Sector = {\n inner: frame.hole, outer: frame.radius, start, end: start + frame.span,\n corner: corner(room), pad: frame.pad, padRadius: frame.radius,\n };\n const reach = polarRadius(item.value, model.max, frame.hole, frame.radius) - frame.hole;\n const thickness = shows ? Math.min(room, Math.max(reach * local, MIN_POLAR_THICKNESS)) : 0;\n return { ...base, track, arc: { ...track, outer: frame.hole + thickness, corner: corner(thickness) } };\n });\n}\n\n/**\n * `mark` grown to fraction `k`. Its slot opens (or closes) as neighbours\n * re-space \u2014 a ring thickens about its radius, a wedge widens about its\n * mid-angle \u2014 while the arc sweeps out of 12 o'clock or the wedge out of the hole.\n */\nfunction grown(mark: RadialBarMark, k: number, alpha: number): RadialBarMark {\n const scale = (s: Sector): Sector => {\n if (mark.layout === 'radial') {\n const mid = (s.inner + s.outer) / 2;\n const half = ((s.outer - s.inner) / 2) * k;\n return { ...s, inner: mid - half, outer: mid + half, corner: s.corner * k };\n }\n const mid = (s.start + s.end) / 2;\n const half = ((s.end - s.start) / 2) * k;\n return { ...s, start: mid - half, end: mid + half };\n };\n const track = scale(mark.track);\n const arc = scale(mark.arc);\n if (mark.layout === 'radial') arc.end = lerp(arc.start, arc.end, k);\n else arc.outer = lerp(arc.inner, arc.outer, k);\n // An arc in transit isn't full, so it carries no overflow chevron.\n return { ...mark, arc, track, over: k >= 1 ? mark.over : 0, alpha };\n}\n\nconst RADIAL_BAR_MORPH: MorphPlan<RadialBarMark> = {\n lerp: (a, b, t) => ({\n key: b.key,\n layout: switchAt(a.layout, b.layout, t),\n label: b.label,\n value: lerpValue(a.value, b.value, t),\n color: mixColor(a.color, b.color, t),\n cx: lerp(a.cx, b.cx, t),\n cy: lerp(a.cy, b.cy, t),\n arc: lerpSector(a.arc, b.arc, t),\n track: lerpSector(a.track, b.track, t),\n over: lerp(a.over, b.over, t),\n emph: lerp(a.emph, b.emph, t),\n alpha: lerp(a.alpha, b.alpha, t),\n }),\n enter: (b, t) => grown(b, t, t),\n exit: (a, t) => grown(a, 1 - t, a.alpha * (1 - t)),\n};\n\n// ---------------------------------------------------------------------------\n// Painting\n// ---------------------------------------------------------------------------\n\ninterface PaintStyle {\n tokens: ThemeTokens;\n pen: RoughPen | null;\n /** Track color, or null when tracks are off. */\n track: string | null;\n backdrop: RGBA | null;\n}\n\nfunction trackColor(tokens: ThemeTokens): string {\n if (!tokens.dark) return tokens.color.grid;\n const muted = parseColor(tokens.color.textMuted);\n return muted ? rgbaToCss({ ...muted, a: 0.16 }) : tokens.color.grid;\n}\n\n/** The color actually under a mark's ink: its fill, composited when faded. */\nfunction underColor(mark: RadialBarMark, style: PaintStyle): RGBA | null {\n const fill = parseColor(mark.color);\n if (!fill) return null;\n const alpha = mark.emph * mark.alpha;\n return style.backdrop && (alpha < 1 || fill.a < 1) ? compositeOver(fill, alpha, style.backdrop) : { ...fill, a: 1 };\n}\n\n/** A pale fill's identifying hairline: its own hue, deepened just enough to read. */\nfunction paleBoundary(mark: RadialBarMark, style: PaintStyle, opacity: number): MarkBoundary | undefined {\n const fill = parseColor(mark.color);\n const { backdrop } = style;\n if (!fill || !backdrop || contrastRatio(compositeOver(fill, 1, backdrop), backdrop) >= PALE_CONTRAST) return undefined;\n const color = toneForContrast(mark.color, contrastTarget(backdrop, style.tokens), backdrop, PALE_CONTRAST);\n return { color, opacity, width: 1 };\n}\n\nfunction paintTrack(ctx: CanvasRenderingContext2D, mark: RadialBarMark, style: PaintStyle, color: string): void {\n const s = mark.track;\n if (!drawable(s) || !(mark.alpha > 0)) return;\n if (mark.alpha !== 1) ctx.globalAlpha = mark.alpha;\n if (style.pen) {\n style.pen.wedge(mark.cx, mark.cy, s.inner, s.outer, s.start, s.end, { fill: color, fillStyle: 'solid' });\n } else {\n ctx.beginPath();\n traceSector(ctx, mark.cx, mark.cy, s);\n ctx.fillStyle = color;\n ctx.fill();\n }\n if (mark.alpha !== 1) ctx.globalAlpha = 1;\n}\n\nfunction paintArc(surface: Surface, ctx: CanvasRenderingContext2D, mark: RadialBarMark, style: PaintStyle): void {\n const s = mark.arc;\n const alpha = mark.emph * mark.alpha;\n if (!drawable(s) || !(alpha > 0)) return;\n const boundary = style.pen ? undefined : paleBoundary(mark, style, alpha);\n if (alpha !== 1) ctx.globalAlpha = alpha;\n if (style.pen) {\n style.pen.wedge(mark.cx, mark.cy, s.inner, s.outer, s.start, s.end, { fill: mark.color, stroke: mark.color, strokeWidth: 1 });\n } else {\n ctx.beginPath();\n traceSector(ctx, mark.cx, mark.cy, s);\n ctx.fillStyle = mark.color;\n ctx.fill();\n if (boundary) {\n ctx.strokeStyle = boundary.color;\n ctx.lineWidth = boundary.width;\n ctx.stroke();\n }\n }\n if (alpha !== 1) ctx.globalAlpha = 1;\n const b = sectorBounds(mark.cx, mark.cy, s.inner, s.outer, s.start, s.end);\n surface.evidence?.mark(mark.layout === 'radial' ? 'bar' : 'slice', b.x, b.y, b.width, b.height, mark.color, alpha,\n undefined, 1, undefined, alpha < 1, boundary);\n}\n\n/**\n * A \"\u00BB\" inside the end of an arc that exceeds `max` \u2014 pointing along the sweep\n * (radial) or out of the rim (polar) \u2014 so a clamped value never reads as\n * exactly full. Thin marks get a single chevron; hairlines rely on the label\n * and tooltip.\n */\nfunction paintOverflow(ctx: CanvasRenderingContext2D, mark: RadialBarMark, style: PaintStyle): void {\n const s = mark.arc;\n const alpha = mark.over * mark.emph * mark.alpha;\n if (!drawable(s) || !(alpha > 0)) return;\n const thickness = s.outer - s.inner;\n let size: number;\n let a: number;\n let r: number;\n let tangential: boolean;\n if (mark.layout === 'radial') {\n r = (s.inner + s.outer) / 2;\n size = Math.min(thickness * 0.5, 12);\n // Centered on the round end cap.\n a = s.end - minArcSpan(r, thickness) / 2;\n if (thickness < 7 || a <= s.start) return;\n tangential = true;\n } else {\n size = Math.min(thickness * 0.3, 12);\n r = s.outer - size * 1.1;\n a = (s.start + s.end) / 2;\n const room = 2 * r * Math.sin(Math.min(Math.PI / 2, (s.end - s.start - s.pad) / 2));\n if (size < 5 || room < size + 6 || r - size <= s.inner) return;\n tangential = false;\n }\n const radial = { x: Math.sin(a), y: -Math.cos(a) };\n const along = { x: Math.cos(a), y: Math.sin(a) };\n // Point along the sweep (radial) or out of the rim (polar).\n const dir = tangential ? along : radial;\n const side = tangential ? radial : along;\n const back = size / 4;\n const offsets = thickness >= 12 ? [-size * 0.3, size * 0.3] : [0];\n const under = underColor(mark, style);\n const ink = under ? inkOn(under, style.tokens, 3) : style.tokens.color.background;\n const width = Math.max(1.4, size * 0.17);\n ctx.globalAlpha = alpha * 0.9;\n for (const offset of offsets) {\n const cx = mark.cx + r * radial.x + dir.x * offset;\n const cy = mark.cy + r * radial.y + dir.y * offset;\n const pts = [\n { x: cx - dir.x * back + (side.x * size) / 2, y: cy - dir.y * back + (side.y * size) / 2 },\n { x: cx + dir.x * back, y: cy + dir.y * back },\n { x: cx - dir.x * back - (side.x * size) / 2, y: cy - dir.y * back - (side.y * size) / 2 },\n ];\n if (style.pen) {\n style.pen.polyline(pts, { stroke: ink, strokeWidth: width });\n continue;\n }\n ctx.beginPath();\n ctx.moveTo(pts[0].x, pts[0].y);\n ctx.lineTo(pts[1].x, pts[1].y);\n ctx.lineTo(pts[2].x, pts[2].y);\n ctx.strokeStyle = ink;\n ctx.lineWidth = width;\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n ctx.stroke();\n }\n ctx.globalAlpha = 1;\n}\n\n/** Tracks beneath every arc, then the arcs, then overflow chevrons on top. */\nfunction paintMarks(surface: Surface, marks: readonly RadialBarMark[], style: PaintStyle): void {\n if (marks.length === 0) return;\n const ctx = surface.marks.ctx;\n ctx.save();\n if (style.track) for (const mark of marks) paintTrack(ctx, mark, style, style.track);\n for (const mark of marks) paintArc(surface, ctx, mark, style);\n for (const mark of marks) if (mark.over > 0) paintOverflow(ctx, mark, style);\n ctx.restore();\n}\n\n// ---------------------------------------------------------------------------\n// Labels\n// ---------------------------------------------------------------------------\n\ninterface LabelContext {\n surface: Surface;\n tokens: ThemeTokens;\n spec: RadialBarSpec;\n frame: Frame;\n style: LabelStyle;\n paint: PaintStyle;\n /** Hit boxes of drawn labels, by mark key. */\n boxes: Map<string, Rect>;\n}\n\nfunction omitLabel(ctx: LabelContext, reason: 'space' | 'collision' | 'unreadable', example: string): void {\n ctx.surface.labels?.omit({ role: 'data-label', reason, required: !ctx.frame.legend, example, path: '/labels' });\n}\n\n/** Opacity for a faded label, and the backdrop its contrast is judged on (intentionally dimmed text isn't). */\nfunction labelPaint(ctx: LabelContext, mark: RadialBarMark): { opacity?: number; background?: string } {\n const alpha = mark.emph * mark.alpha;\n return alpha < 1 ? { opacity: alpha } : { background: ctx.surface.evidence?.background };\n}\n\n/** Name + value right-aligned against the 12 o'clock line, at the start of each track. */\nfunction drawRadialLabels(ctx: LabelContext, marks: readonly RadialBarMark[]): void {\n const { surface, tokens, frame, style } = ctx;\n const format = valueFormat(ctx.spec);\n const rings = marks.map((m) => (m.track.inner + m.track.outer) / 2);\n for (const mark of marks) {\n const r = (mark.track.inner + mark.track.outer) / 2;\n const y = mark.cy - r;\n const right = mark.cx - LABEL_GAP;\n const valueText = formatValue(mark.value, format);\n const budget = radialLabelBudget(frame.area, mark.cx, frame.sweep, r, rings, mark.track.outer - mark.track.inner, style.lineH);\n const valueW = measureText(valueText, style.valueFont).width;\n const nameBudget = budget - valueW - NAME_GAP;\n // Rows stay inside the area and above the 9 o'clock line, where the arcs return.\n const fitsRow = y - style.lineH / 2 >= frame.area.y - 1 && y + style.lineH / 2 <= mark.cy - 1;\n if (!fitsRow || nameBudget < 12) {\n omitLabel(ctx, 'space', mark.label);\n continue;\n }\n const fitted = fitLabel(mark.label, nameBudget, style.nameFont);\n if (fitted.truncated && (fitted.text.length < 4 || fitted.text === '\u2026')) {\n omitLabel(ctx, 'unreadable', mark.label);\n continue;\n }\n const paint = labelPaint(ctx, mark);\n addOverlayText(surface, tokens, {\n left: right, top: y, text: valueText, color: tokens.color.text, size: style.size,\n weight: tokens.font.weight.bold, transform: 'translate(-100%,-50%)', path: '/labels', ...paint,\n });\n const nameRight = right - valueW - NAME_GAP;\n addOverlayText(surface, tokens, {\n left: nameRight, top: y, text: mark.label, color: tokens.color.textMuted, size: style.size,\n transform: 'translate(-100%,-50%)', maxWidth: nameBudget, path: '/labels', ...paint,\n });\n const nameW = measureText(fitted.text, style.nameFont).width;\n ctx.boxes.set(mark.key, { x: nameRight - nameW, y: y - style.lineH / 2, width: right - nameRight + nameW, height: style.lineH });\n }\n}\n\n/** Category names around the rim at each wedge's mid-angle; values inside wedges that fit them. */\nfunction drawPolarLabels(ctx: LabelContext, marks: readonly RadialBarMark[]): void {\n const { surface, tokens, frame, style } = ctx;\n const { area } = frame;\n const format = valueFormat(ctx.spec);\n const placed: Rect[] = [];\n const cap = Math.max(40, area.width * 0.22);\n for (const mark of marks) {\n const a = (mark.track.start + mark.track.end) / 2;\n const sin = Math.sin(a);\n const cos = Math.cos(a);\n const rim = mark.track.outer + POLAR_LABEL_OFFSET;\n const px = mark.cx + rim * sin;\n const py = mark.cy - rim * cos;\n const align = sin > 0.3 ? 'left' : sin < -0.3 ? 'right' : 'center';\n const midY = py - (cos * style.lineH) / 2;\n const room = align === 'left' ? area.x + area.width - px : align === 'right' ? px - area.x\n : 2 * Math.min(px - area.x, area.x + area.width - px);\n const budget = Math.min(room, align === 'center' ? cap * 1.6 : cap);\n const fitted = fitLabel(mark.label, budget, style.nameFont);\n if (budget < 16 || (fitted.truncated && (fitted.text.length < 4 || fitted.text === '\u2026'))) {\n omitLabel(ctx, 'unreadable', mark.label);\n continue;\n }\n const w = measureText(fitted.text, style.nameFont).width;\n const box = { x: align === 'left' ? px : align === 'right' ? px - w : px - w / 2, y: midY - style.lineH / 2, width: w, height: style.lineH };\n if (box.y < area.y - 1 || box.y + box.height > area.y + area.height + 1) omitLabel(ctx, 'space', mark.label);\n else if (placed.some((p) => boxesOverlap(p, box, 2))) omitLabel(ctx, 'collision', mark.label);\n else {\n placed.push(box);\n addOverlayText(surface, tokens, {\n left: px, top: midY, text: mark.label, color: tokens.color.text, size: style.size,\n transform: align === 'left' ? 'translateY(-50%)' : align === 'right' ? 'translate(-100%,-50%)' : 'translate(-50%,-50%)',\n maxWidth: budget, path: '/labels', ...labelPaint(ctx, mark),\n });\n ctx.boxes.set(mark.key, box);\n }\n }\n drawPolarValues(ctx, marks, format);\n}\n\n/**\n * Each wedge's value, centered inside it in contrast-aware ink. Values show\n * where they fit as long as most wedges can carry theirs; otherwise none do,\n * so the rose never reads as randomly annotated.\n */\nfunction drawPolarValues(ctx: LabelContext, marks: readonly RadialBarMark[], format: string | undefined): void {\n const { surface, tokens } = ctx;\n const size = tokens.font.size.tiny;\n const font = fontString(size, tokens.font.family, tokens.font.weight.bold);\n const lineH = size * 1.2;\n const plans = marks.filter((mark) => drawable(mark.arc)).map((mark) => {\n const s = mark.arc;\n const text = formatValue(mark.value, format);\n const thickness = s.outer - s.inner;\n const r = s.inner + thickness * 0.55;\n const half = Math.min(Math.PI / 2, (s.end - s.start - s.pad) / 2);\n const chord = 2 * Math.max(0, r - lineH / 2) * Math.sin(Math.max(0, half));\n const fits = thickness >= lineH + 6 && chord >= measureText(text, font).width + 6;\n return { mark, text, r, fits };\n });\n const fitting = plans.filter((plan) => plan.fits);\n const shown = fitting.length * 3 >= plans.length * 2 ? fitting : [];\n surface.labels?.omit({\n role: 'data-label', reason: 'space', required: false, count: plans.length - shown.length,\n example: plans.find((plan) => !shown.includes(plan))?.text, path: '/labels',\n });\n for (const { mark, text, r } of shown) {\n const s = mark.arc;\n const a = (s.start + s.end) / 2;\n // Ink is chosen against the (possibly dimmed) fill actually beneath it.\n const under = underColor(mark, ctx.paint);\n addOverlayText(surface, tokens, {\n left: mark.cx + r * Math.sin(a), top: mark.cy - r * Math.cos(a), text, size,\n color: under ? inkOn(under, tokens) : tokens.color.text, weight: tokens.font.weight.bold,\n transform: 'translate(-50%,-50%)', opacity: mark.alpha < 1 ? mark.alpha : undefined,\n background: under && mark.alpha >= 1 ? rgbaToCss(under) : undefined, path: '/labels',\n });\n }\n}\n\n// ---------------------------------------------------------------------------\n// Interaction\n// ---------------------------------------------------------------------------\n\n/** The slot each hover focuses, so a moving hover can glide between slots. */\nconst HOVER_SLOTS = new WeakMap<Hover, { key: string; cx: number; cy: number; slot: Sector }>();\n\nfunction buildInteraction(\n spec: RadialBarSpec,\n tokens: ThemeTokens,\n frame: Frame,\n model: RadialBarModel,\n marks: readonly RadialBarMark[],\n labelBoxes: ReadonlyMap<string, Rect>,\n): InteractionModel {\n const categoryField = spec.encoding.category.field;\n const format = valueFormat(spec);\n const measureLabel = spec.encoding.value.title ?? spec.encoding.value.field;\n const tooltips = tooltipEnabled(spec);\n const focusOn = hoverFocusEnabled(spec);\n const veil = spec.background ?? tokens.color.background;\n const region = frame.area;\n const n = model.items.length;\n\n const indexAt = (px: number, py: number): number => {\n for (let i = 0; i < n; i++) {\n const box = labelBoxes.get(marks[i].key);\n if (box && px >= box.x - 2 && px <= box.x + box.width + 2 && py >= box.y && py <= box.y + box.height) return i;\n }\n const dx = px - frame.cx;\n const dy = py - frame.cy;\n const r = Math.hypot(dx, dy);\n let theta = Math.atan2(dx, -dy);\n if (theta < 0) theta += TAU;\n if (frame.layout === 'polar') {\n if (r > frame.radius + 2 || r < frame.hole - 2 || !(frame.span > 0)) return -1;\n return Math.min(n - 1, Math.floor(theta / frame.span));\n }\n if (theta > frame.sweep + EPSILON && frame.sweep < TAU - EPSILON) return -1;\n // The whole track band (its gap included) answers, so thin arcs are easy to hover.\n const half = Math.max(frame.tracks.pitch, frame.tracks.width) / 2;\n for (let i = 0; i < n; i++) if (Math.abs(r - frame.tracks.radii[i]) <= half) return i;\n return -1;\n };\n\n const paintHover = (ictx: CanvasRenderingContext2D, index: number, state: HoverPaintState): void => {\n const mark = marks[index];\n const prev = state.from ? HOVER_SLOTS.get(state.from) : undefined;\n const glide = prev && prev.key !== mark.key && state.glide < 1 ? state.glide : 1;\n const from = glide < 1 ? prev : undefined;\n const cx = from ? lerp(from.cx, mark.cx, glide) : mark.cx;\n const cy = from ? lerp(from.cy, mark.cy, glide) : mark.cy;\n const slot = from ? lerpSector(from.slot, mark.track, glide) : mark.track;\n if (focusOn) {\n // Everything recedes but the hovered category's track (or wedge slot) and its arc.\n paintCutoutVeil(ictx, region, veil, FOCUS_VEIL * state.focus, (c) => traceSector(c, cx, cy, slot));\n return;\n }\n // Hand-drawn charts (and `tooltip.focus: false`) outline the hovered slot instead.\n ictx.save();\n ictx.globalAlpha = state.focus;\n ictx.beginPath();\n traceSector(ictx, cx, cy, slot);\n ictx.lineWidth = 1.5;\n ictx.strokeStyle = tokens.color.text;\n ictx.stroke();\n ictx.restore();\n };\n\n return {\n region: { ...region },\n hitTest: (px, py) => {\n if (!tooltips) return null;\n const i = indexAt(px, py);\n if (i < 0) return null;\n const item = model.items[i];\n const ratio = item.value / model.max;\n const rows: TooltipRow[] = [\n { swatch: item.color, label: measureLabel, value: formatValue(item.value, format) },\n { label: '% of max', value: formatValue(ratio, ratio >= 10 ? ',.0%' : '.1%'), muted: true },\n ];\n const hover: Hover = {\n key: item.key,\n anchorX: px,\n anchorY: py,\n content: { title: item.label, rows },\n paint: (ictx, state) => paintHover(ictx, i, state),\n draw: (ictx) => paintHover(ictx, i, { focus: 1, glide: 1 }),\n };\n HOVER_SLOTS.set(hover, { key: item.key, cx: marks[i].cx, cy: marks[i].cy, slot: marks[i].track });\n return hover;\n },\n pick: (px, py) => {\n const i = indexAt(px, py);\n if (i < 0) return null;\n return { kind: 'point', fields: [categoryField], tuples: [[model.items[i].category]] };\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Entry point\n// ---------------------------------------------------------------------------\n\nexport function drawRadialBar(\n surface: Surface,\n spec: ChartSpec,\n tokens: ThemeTokens,\n size: Size,\n context?: RenderContext,\n): InteractionModel | void {\n surface.evidence?.begin('aggregate');\n surface.evidence?.data(0, 'aggregate');\n const rb = spec as RadialBarSpec;\n const content = insetRect(drawTitleBlock(surface, tokens, size, rb.title), rb.padding);\n const model = buildRadialBarModel(rb, radialBarPalette(rb, tokens));\n const sketch = resolveSketch(rb);\n const paint: PaintStyle = {\n tokens,\n pen: sketch ? new RoughPen(surface.marks.ctx, sketch) : null,\n track: rb.track === false ? null : trackColor(tokens),\n backdrop: knownBackdrop(surface, tokens),\n };\n const morph = morphInput<RadialBarMark[]>(context);\n\n if (model.items.length === 0 || content.width < 2 || content.height < 2) {\n // Arcs on screen shrink away; a later update grows its arcs back in.\n const leaving = morph ? morphKeyed(morph.from, [], morph.t, RADIAL_BAR_MORPH).exits : [];\n paintMarks(surface, leaving, paint);\n stashMorphFrame(surface, 'radialBar', leaving);\n drawNoData(surface, tokens, content, (rb.data?.length ?? 0) > 0 ? 'No usable values' : 'No data', 'aggregate');\n return;\n }\n\n const frame = planFrame(surface, rb, tokens, content, model);\n const finals = buildMarks(frame, model, rb, context);\n // Update morph: arcs travel from the ones on screen (tracks re-space, values\n // count, colors and highlights blend); new categories grow in, removed ones\n // shrink away beneath the rest.\n const moved = morph ? morphKeyed(morph.from, finals, morph.t, RADIAL_BAR_MORPH) : null;\n const shown = moved ? [...moved.exits, ...moved.items] : finals;\n paintMarks(surface, shown, paint);\n stashMorphFrame(surface, 'radialBar', shown);\n surface.evidence?.data(model.represented, 'aggregate');\n\n const labelBoxes = new Map<string, Rect>();\n // Labels arrive with the final entrance frame; mid-morph, a category keeps\n // its label while at least half shown.\n if (frame.labels && finiteOr(context?.sweepProgress, 1) >= 1) {\n const labelled = moved ? shown.filter((mark) => mark.alpha >= 0.5) : finals;\n const ctx: LabelContext = { surface, tokens, spec: rb, frame, style: frame.labels, paint, boxes: labelBoxes };\n if (frame.layout === 'radial') drawRadialLabels(ctx, labelled);\n else drawPolarLabels(ctx, labelled);\n }\n\n return buildInteraction(rb, tokens, frame, model, finals, labelBoxes);\n}"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["area", "labels", "track", "mid", "half", "arc"]
|
|
7
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sankey.d.ts","sourceRoot":"","sources":["../../src/charts/sankey.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAKjD,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAS,IAAI,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAElD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"sankey.d.ts","sourceRoot":"","sources":["../../src/charts/sankey.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAKjD,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAS,IAAI,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAElD,OAAO,KAAK,EAAmB,gBAAgB,EAAc,MAAM,sBAAsB,CAAC;AAI1F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAO7C,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,KAAK,EAAE,CAAC;IACrB,WAAW,EAAE,KAAK,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,KAAK;IACpB,MAAM,EAAE,KAAK,CAAC;IACd,MAAM,EAAE,KAAK,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,KAAK;IACpB,KAAK,EAAE,KAAK,EAAE,CAAC;IACf,KAAK,EAAE,KAAK,EAAE,CAAC;IACf,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;CACzB;AAgVD,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,EAAE,IAAI,EACV,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAO,GACzD,KAAK,CAIP;AAkpBD,wBAAgB,UAAU,CACxB,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,WAAW,EACnB,IAAI,EAAE,IAAI,EACV,OAAO,CAAC,EAAE,aAAa,GACtB,gBAAgB,GAAG,IAAI,CAgCzB"}
|
package/dist/charts/sankey.js
CHANGED
|
@@ -1,16 +1,94 @@
|
|
|
1
|
-
import { ordinalColorScale,
|
|
1
|
+
import { interpolateOklab, ordinalColorScale, parseColor, rgbaToCss } from "../color/index.js";
|
|
2
2
|
import { formatValue } from "../format/index.js";
|
|
3
3
|
import { fontString, measureText } from "../render/text.js";
|
|
4
4
|
import { roundedRect } from "../shape/index.js";
|
|
5
5
|
import { RoughPen } from "../rough/index.js";
|
|
6
6
|
import { resolveSketch } from "../spec/sketch.js";
|
|
7
7
|
import { accessor, toKey, toNumber } from "../util/data.js";
|
|
8
|
+
import { FOCUS_VEIL, hoverFocusEnabled, paintFocusVeil } from "../interaction/veil.js";
|
|
8
9
|
import { boxesOverlap, fitLabel } from "../render/labelLedger.js";
|
|
9
10
|
import { addOverlayText, drawTitleBlock } from "./chrome.js";
|
|
10
11
|
import { rowAlpha } from "./emphasis.js";
|
|
12
|
+
import { alphaForContrast, knownBackdrop } from "./legibility.js";
|
|
11
13
|
import { drawNoData } from "./noData.js";
|
|
12
14
|
import { spreadCenters } from "./labelLayout.js";
|
|
15
|
+
import { lerp, lerpValue, mixColor, morphInput, morphKeyed, stashMorphFrame } from "./morphFrame.js";
|
|
13
16
|
const center = (n) => (n.y0 + n.y1) / 2;
|
|
17
|
+
const LINK_ALPHA_LIGHT = 0.45;
|
|
18
|
+
const LINK_ALPHA_DARK = 0.58;
|
|
19
|
+
const LINK_CONTRAST_MIN = 1.6;
|
|
20
|
+
const LINK_HOVER_ALPHA = 0.75;
|
|
21
|
+
const LINK_GRADIENT_STOPS = [0, 0.25, 0.5, 0.75, 1];
|
|
22
|
+
const nodeRadius = (width, height) => Math.max(0, Math.min(2, width / 2, height / 2));
|
|
23
|
+
function heldNode(node, k, alpha) {
|
|
24
|
+
const mid = (node.y0 + node.y1) / 2;
|
|
25
|
+
return { ...node, y0: lerp(mid, node.y0, k), y1: lerp(mid, node.y1, k), alpha };
|
|
26
|
+
}
|
|
27
|
+
const NODE_MORPH = {
|
|
28
|
+
lerp: (a, b, t) => ({
|
|
29
|
+
key: b.key,
|
|
30
|
+
layer: b.layer,
|
|
31
|
+
x0: lerp(a.x0, b.x0, t),
|
|
32
|
+
y0: lerp(a.y0, b.y0, t),
|
|
33
|
+
y1: lerp(a.y1, b.y1, t),
|
|
34
|
+
width: lerp(a.width, b.width, t),
|
|
35
|
+
color: mixColor(a.color, b.color, t),
|
|
36
|
+
value: lerpValue(a.value, b.value, t),
|
|
37
|
+
emph: lerp(a.emph, b.emph, t),
|
|
38
|
+
alpha: lerp(a.alpha, b.alpha, t)
|
|
39
|
+
}),
|
|
40
|
+
enter: (b, t) => heldNode(b, t, t),
|
|
41
|
+
exit: (a, t) => heldNode(a, 1 - t, a.alpha * (1 - t))
|
|
42
|
+
};
|
|
43
|
+
const lerpLink = (a, b, t) => ({
|
|
44
|
+
key: b.key,
|
|
45
|
+
source: { key: b.source.key, x1: lerp(a.source.x1, b.source.x1, t), color: mixColor(a.source.color, b.source.color, t) },
|
|
46
|
+
target: { key: b.target.key, x0: lerp(a.target.x0, b.target.x0, t), color: mixColor(a.target.color, b.target.color, t) },
|
|
47
|
+
sy: lerp(a.sy, b.sy, t),
|
|
48
|
+
ty: lerp(a.ty, b.ty, t),
|
|
49
|
+
width: lerp(a.width, b.width, t),
|
|
50
|
+
sourceAlpha: lerp(a.sourceAlpha, b.sourceAlpha, t),
|
|
51
|
+
targetAlpha: lerp(a.targetAlpha, b.targetAlpha, t),
|
|
52
|
+
emph: lerp(a.emph, b.emph, t),
|
|
53
|
+
alpha: lerp(a.alpha, b.alpha, t)
|
|
54
|
+
});
|
|
55
|
+
const byNodeKey = (nodes) => new Map(nodes.map((node) => [node.key, node]));
|
|
56
|
+
function seatLink(link, laid, now) {
|
|
57
|
+
const along = (y, was, is) => {
|
|
58
|
+
if (!was || !is) return y;
|
|
59
|
+
const span = was.y1 - was.y0;
|
|
60
|
+
return is.y0 + (span > 0 ? (y - was.y0) / span : 0.5) * (is.y1 - is.y0);
|
|
61
|
+
};
|
|
62
|
+
const source = now.get(link.source.key);
|
|
63
|
+
const target = now.get(link.target.key);
|
|
64
|
+
return {
|
|
65
|
+
...link,
|
|
66
|
+
source: { ...link.source, x1: source ? source.x0 + source.width : link.source.x1 },
|
|
67
|
+
target: { ...link.target, x0: target ? target.x0 : link.target.x0 },
|
|
68
|
+
sy: along(link.sy, laid.get(link.source.key), source),
|
|
69
|
+
ty: along(link.ty, laid.get(link.target.key), target)
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function linkPlan(before, after, now) {
|
|
73
|
+
return {
|
|
74
|
+
lerp: lerpLink,
|
|
75
|
+
enter: (b, t) => seatLink({ ...b, width: b.width * t, alpha: t }, after, now),
|
|
76
|
+
exit: (a, t) => seatLink({ ...a, width: a.width * (1 - t), alpha: a.alpha * (1 - t) }, before, now)
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function sankeyMorph(context, finals) {
|
|
80
|
+
const morph = morphInput(context);
|
|
81
|
+
if (!morph) return { shown: finals, moved: null };
|
|
82
|
+
const nodes = morphKeyed(morph.from.nodes, finals.nodes, morph.t, NODE_MORPH);
|
|
83
|
+
const shownNodes = [...nodes.exits, ...nodes.items];
|
|
84
|
+
const plan = linkPlan(byNodeKey(morph.from.nodes), byNodeKey(finals.nodes), byNodeKey(shownNodes));
|
|
85
|
+
const links = morphKeyed(morph.from.links, finals.links, morph.t, plan);
|
|
86
|
+
return {
|
|
87
|
+
// Widest underneath by the widths on screen, so the stacking hands over as flows resize.
|
|
88
|
+
shown: { links: [...links.exits, ...links.items].sort((a, b) => b.width - a.width), nodes: shownNodes },
|
|
89
|
+
moved: nodes
|
|
90
|
+
};
|
|
91
|
+
}
|
|
14
92
|
function clamp(value, min, max) {
|
|
15
93
|
return Math.min(max, Math.max(min, value));
|
|
16
94
|
}
|
|
@@ -251,18 +329,35 @@ function computeLinkBreadths(graph) {
|
|
|
251
329
|
}
|
|
252
330
|
}
|
|
253
331
|
}
|
|
254
|
-
function
|
|
332
|
+
function linkPaint(ctx, link, sourceAlpha, targetAlpha) {
|
|
333
|
+
const a = parseColor(link.source.color);
|
|
334
|
+
const b = parseColor(link.target.color);
|
|
335
|
+
if (!a) return link.source.color;
|
|
336
|
+
const from = rgbaToCss({ ...a, a: a.a * sourceAlpha });
|
|
337
|
+
const x0 = link.source.x1;
|
|
338
|
+
const x1 = link.target.x0;
|
|
339
|
+
if (!b || !Number.isFinite(x0) || !Number.isFinite(x1) || Math.abs(x1 - x0) < 1e-6) return from;
|
|
340
|
+
const gradient = ctx.createLinearGradient?.(x0, 0, x1, 0);
|
|
341
|
+
if (!gradient || typeof gradient.addColorStop !== "function") return from;
|
|
342
|
+
const mix = interpolateOklab(a, b);
|
|
343
|
+
for (const t of LINK_GRADIENT_STOPS) {
|
|
344
|
+
const c = mix(t);
|
|
345
|
+
gradient.addColorStop(t, rgbaToCss({ ...c, a: c.a * (sourceAlpha + (targetAlpha - sourceAlpha) * t) }));
|
|
346
|
+
}
|
|
347
|
+
return gradient;
|
|
348
|
+
}
|
|
349
|
+
function strokeLink(ctx, l, alpha) {
|
|
255
350
|
const x0 = l.source.x1;
|
|
256
351
|
const x1 = l.target.x0;
|
|
257
352
|
const xm = (x0 + x1) / 2;
|
|
258
353
|
ctx.lineWidth = l.width;
|
|
259
|
-
ctx.strokeStyle =
|
|
354
|
+
ctx.strokeStyle = linkPaint(ctx, l, alpha, alpha);
|
|
260
355
|
ctx.beginPath();
|
|
261
356
|
ctx.moveTo(x0, l.sy);
|
|
262
357
|
ctx.bezierCurveTo(xm, l.sy, xm, l.ty, x1, l.ty);
|
|
263
358
|
ctx.stroke();
|
|
264
359
|
}
|
|
265
|
-
function paintLink(ctx, link,
|
|
360
|
+
function paintLink(ctx, link, sourceAlpha, targetAlpha, alpha = 1) {
|
|
266
361
|
const x0 = link.source.x1;
|
|
267
362
|
const x1 = link.target.x0;
|
|
268
363
|
const middle = (x0 + x1) / 2;
|
|
@@ -274,13 +369,9 @@ function paintLink(ctx, link, boundary, alpha = 1) {
|
|
|
274
369
|
ctx.lineTo(x1, link.ty + half);
|
|
275
370
|
ctx.bezierCurveTo(middle, link.ty + half, middle, link.sy + half, x0, link.sy + half);
|
|
276
371
|
ctx.closePath();
|
|
277
|
-
ctx.fillStyle = link
|
|
278
|
-
ctx.globalAlpha =
|
|
372
|
+
ctx.fillStyle = linkPaint(ctx, link, sourceAlpha, targetAlpha);
|
|
373
|
+
ctx.globalAlpha = alpha;
|
|
279
374
|
ctx.fill();
|
|
280
|
-
ctx.strokeStyle = boundary.color;
|
|
281
|
-
ctx.globalAlpha = boundary.opacity * alpha;
|
|
282
|
-
ctx.lineWidth = boundary.width;
|
|
283
|
-
ctx.stroke();
|
|
284
375
|
ctx.restore();
|
|
285
376
|
}
|
|
286
377
|
function prepareSankeyFrame(surface, sankey, tokens, size, graph) {
|
|
@@ -324,51 +415,111 @@ function sankeyProbes(sankey) {
|
|
|
324
415
|
link: (l) => ({ [sourceField]: l.sourceValue, [targetField]: l.targetValue })
|
|
325
416
|
};
|
|
326
417
|
}
|
|
327
|
-
function
|
|
418
|
+
function sankeyMarks(surface, tokens, graph, sorted, frame, alpha) {
|
|
419
|
+
const backdrop = knownBackdrop(surface, tokens);
|
|
420
|
+
const baseAlpha = tokens.dark ? LINK_ALPHA_DARK : LINK_ALPHA_LIGHT;
|
|
421
|
+
const linkAlphas = /* @__PURE__ */ new Map();
|
|
422
|
+
const linkAlpha = (color) => {
|
|
423
|
+
let a = linkAlphas.get(color);
|
|
424
|
+
if (a === void 0) {
|
|
425
|
+
a = alphaForContrast(color, baseAlpha, backdrop, LINK_CONTRAST_MIN);
|
|
426
|
+
linkAlphas.set(color, a);
|
|
427
|
+
}
|
|
428
|
+
return a;
|
|
429
|
+
};
|
|
430
|
+
return {
|
|
431
|
+
links: sorted.map((l) => ({
|
|
432
|
+
key: `${l.source.name}\0${l.target.name}`,
|
|
433
|
+
source: { key: l.source.name, x1: l.source.x1, color: l.source.color },
|
|
434
|
+
target: { key: l.target.name, x0: l.target.x0, color: l.target.color },
|
|
435
|
+
sy: l.sy,
|
|
436
|
+
ty: l.ty,
|
|
437
|
+
width: l.width,
|
|
438
|
+
sourceAlpha: linkAlpha(l.source.color),
|
|
439
|
+
targetAlpha: linkAlpha(l.target.color),
|
|
440
|
+
emph: alpha.link(l),
|
|
441
|
+
alpha: 1
|
|
442
|
+
})),
|
|
443
|
+
nodes: graph.nodes.map((n) => ({
|
|
444
|
+
key: n.name,
|
|
445
|
+
layer: n.layer,
|
|
446
|
+
x0: n.x0,
|
|
447
|
+
y0: n.y0,
|
|
448
|
+
y1: n.y1,
|
|
449
|
+
width: frame.nodeWidth,
|
|
450
|
+
color: n.color,
|
|
451
|
+
value: n.value,
|
|
452
|
+
emph: alpha.node(n),
|
|
453
|
+
alpha: 1
|
|
454
|
+
}))
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
function paintSankeyMarks(surface, spec, marks) {
|
|
328
458
|
const ctx = surface.marks.ctx;
|
|
329
459
|
ctx.save();
|
|
330
460
|
ctx.lineCap = "butt";
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
const a = alpha.link(l);
|
|
335
|
-
paintLink(ctx, l, boundary, a);
|
|
461
|
+
for (const l of marks.links) {
|
|
462
|
+
const a = l.emph * l.alpha;
|
|
463
|
+
paintLink(ctx, l, l.sourceAlpha, l.targetAlpha, a);
|
|
336
464
|
surface.evidence?.mark(
|
|
337
465
|
"link",
|
|
338
|
-
Math.min(l.source.x1, l.target.x0)
|
|
339
|
-
Math.min(l.sy, l.ty) - l.width / 2
|
|
340
|
-
Math.abs(l.target.x0 - l.source.x1)
|
|
341
|
-
Math.abs(l.ty - l.sy) + l.width
|
|
466
|
+
Math.min(l.source.x1, l.target.x0),
|
|
467
|
+
Math.min(l.sy, l.ty) - l.width / 2,
|
|
468
|
+
Math.abs(l.target.x0 - l.source.x1),
|
|
469
|
+
Math.abs(l.ty - l.sy) + l.width,
|
|
342
470
|
l.source.color,
|
|
343
|
-
|
|
471
|
+
l.sourceAlpha * a,
|
|
344
472
|
void 0,
|
|
345
473
|
1,
|
|
346
474
|
void 0,
|
|
347
|
-
a < 1
|
|
348
|
-
boundary
|
|
475
|
+
a < 1
|
|
349
476
|
);
|
|
350
477
|
}
|
|
351
|
-
const radius = Math.min(3, tokens.radius.sm);
|
|
352
478
|
const sketch = resolveSketch(spec);
|
|
353
479
|
const pen = sketch ? new RoughPen(ctx, sketch) : null;
|
|
354
|
-
for (const n of
|
|
355
|
-
const a =
|
|
480
|
+
for (const n of marks.nodes) {
|
|
481
|
+
const a = n.emph * n.alpha;
|
|
356
482
|
const prev = ctx.globalAlpha;
|
|
357
483
|
if (a !== 1) ctx.globalAlpha = a;
|
|
358
484
|
const h = Math.max(1, n.y1 - n.y0);
|
|
359
485
|
if (pen) {
|
|
360
|
-
pen.rect(n.x0, n.y0,
|
|
486
|
+
pen.rect(n.x0, n.y0, n.width, h, { fill: n.color, fillStyle: "solid" });
|
|
361
487
|
} else {
|
|
362
488
|
ctx.fillStyle = n.color;
|
|
363
489
|
ctx.beginPath();
|
|
364
|
-
roundedRect(ctx, n.x0, n.y0,
|
|
490
|
+
roundedRect(ctx, n.x0, n.y0, n.width, h, nodeRadius(n.width, h));
|
|
365
491
|
ctx.fill();
|
|
366
492
|
}
|
|
367
|
-
surface.evidence?.mark("node", n.x0, n.y0,
|
|
493
|
+
surface.evidence?.mark("node", n.x0, n.y0, n.width, h, n.color, a, void 0, 1, void 0, a < 1);
|
|
368
494
|
if (a !== 1) ctx.globalAlpha = prev;
|
|
369
495
|
}
|
|
370
496
|
ctx.restore();
|
|
371
|
-
|
|
497
|
+
}
|
|
498
|
+
function framedGraph(graph, moved) {
|
|
499
|
+
const shown = /* @__PURE__ */ new Map();
|
|
500
|
+
graph.nodes.forEach((n, i) => {
|
|
501
|
+
const m = moved.items[i];
|
|
502
|
+
shown.set(n, { ...n, x0: m.x0, x1: m.x0 + m.width, y0: m.y0, y1: m.y1, value: m.value });
|
|
503
|
+
});
|
|
504
|
+
const leaving = moved.exits.filter((m) => m.alpha >= 0.5).map((m, i) => ({
|
|
505
|
+
name: m.key,
|
|
506
|
+
index: graph.nodes.length + i,
|
|
507
|
+
layer: Math.min(m.layer, graph.maxLayer),
|
|
508
|
+
value: m.value,
|
|
509
|
+
x0: m.x0,
|
|
510
|
+
x1: m.x0 + m.width,
|
|
511
|
+
y0: m.y0,
|
|
512
|
+
y1: m.y1,
|
|
513
|
+
color: m.color,
|
|
514
|
+
sourceLinks: [],
|
|
515
|
+
targetLinks: []
|
|
516
|
+
}));
|
|
517
|
+
return {
|
|
518
|
+
...graph,
|
|
519
|
+
nodes: [...graph.nodes.filter((_n, i) => moved.items[i].alpha >= 0.5).map((n) => shown.get(n)), ...leaving],
|
|
520
|
+
// Arriving nodes still bound their neighbours' labels.
|
|
521
|
+
columns: graph.columns.map((col) => col.map((n) => shown.get(n)))
|
|
522
|
+
};
|
|
372
523
|
}
|
|
373
524
|
function buildSankeyLabelRows(graph, frame) {
|
|
374
525
|
const labelGap = 6;
|
|
@@ -556,19 +707,20 @@ function sankeyHitIndex(graph, content, sorted) {
|
|
|
556
707
|
};
|
|
557
708
|
return { nodeAt, linkAt };
|
|
558
709
|
}
|
|
559
|
-
function buildSankeyInteraction(sankey, graph, frame, sorted) {
|
|
710
|
+
function buildSankeyInteraction(sankey, graph, frame, sorted, tokens) {
|
|
560
711
|
const tt = sankey.tooltip;
|
|
561
712
|
if (tt === false || tt && typeof tt === "object" && tt.show === false) return;
|
|
562
713
|
const sourceField = sankey.encoding.source.field;
|
|
563
714
|
const targetField = sankey.encoding.target.field;
|
|
564
715
|
const valueLabel = sankey.encoding.value.title ?? sankey.encoding.value.field;
|
|
565
716
|
const { nodeAt, linkAt } = sankeyHitIndex(graph, frame.content, sorted);
|
|
717
|
+
const focus = hoverFocusEnabled(sankey) ? { area: frame.content, veil: sankey.background ?? tokens.color.background } : null;
|
|
566
718
|
return {
|
|
567
719
|
region: frame.content,
|
|
568
720
|
hitTest: (px, py) => {
|
|
569
721
|
const n = nodeAt(px, py);
|
|
570
|
-
if (!n) return sankeyLinkTooltip(px, py, linkAt(px, py), valueLabel, sankey);
|
|
571
|
-
return sankeyNodeTooltip(n, valueLabel, sankey);
|
|
722
|
+
if (!n) return sankeyLinkTooltip(px, py, linkAt(px, py), valueLabel, sankey, focus);
|
|
723
|
+
return sankeyNodeTooltip(n, valueLabel, sankey, focus);
|
|
572
724
|
},
|
|
573
725
|
pick: (px, py) => {
|
|
574
726
|
const n = nodeAt(px, py);
|
|
@@ -582,8 +734,28 @@ function buildSankeyInteraction(sankey, graph, frame, sorted) {
|
|
|
582
734
|
}
|
|
583
735
|
};
|
|
584
736
|
}
|
|
585
|
-
function
|
|
737
|
+
function paintSankeyNode(ictx, n) {
|
|
738
|
+
const h = Math.max(1, n.y1 - n.y0);
|
|
739
|
+
ictx.fillStyle = n.color;
|
|
740
|
+
ictx.beginPath();
|
|
741
|
+
roundedRect(ictx, n.x0, n.y0, n.x1 - n.x0, h, nodeRadius(n.x1 - n.x0, h));
|
|
742
|
+
ictx.fill();
|
|
743
|
+
}
|
|
744
|
+
function paintSankeyFlows(ictx, links, nodes, focus, strength) {
|
|
745
|
+
ictx.save();
|
|
746
|
+
if (focus) {
|
|
747
|
+
paintFocusVeil(ictx, focus.area, focus.veil, FOCUS_VEIL * strength);
|
|
748
|
+
for (const l of links) paintLink(ictx, l, LINK_HOVER_ALPHA, LINK_HOVER_ALPHA);
|
|
749
|
+
} else {
|
|
750
|
+
ictx.lineCap = "butt";
|
|
751
|
+
for (const l of links) strokeLink(ictx, l, LINK_HOVER_ALPHA);
|
|
752
|
+
}
|
|
753
|
+
for (const n of nodes) paintSankeyNode(ictx, n);
|
|
754
|
+
ictx.restore();
|
|
755
|
+
}
|
|
756
|
+
function sankeyLinkTooltip(px, py, link, valueLabel, sankey, focus) {
|
|
586
757
|
if (!link) return null;
|
|
758
|
+
const nodes = focus ? [link.source, link.target] : [];
|
|
587
759
|
return {
|
|
588
760
|
key: `${link.source.name}\0${link.target.name}`,
|
|
589
761
|
anchorX: px,
|
|
@@ -592,15 +764,11 @@ function sankeyLinkTooltip(px, py, link, valueLabel, sankey) {
|
|
|
592
764
|
title: `${link.source.name} \u2192 ${link.target.name}`,
|
|
593
765
|
rows: [{ swatch: link.source.color, label: valueLabel, value: formatValue(link.value, sankey.encoding.value.format) }]
|
|
594
766
|
},
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
ictx.lineCap = "butt";
|
|
598
|
-
strokeLink(ictx, link, withAlpha(link.source.color, 0.72));
|
|
599
|
-
ictx.restore();
|
|
600
|
-
}
|
|
767
|
+
paint: (ictx, state) => paintSankeyFlows(ictx, [link], nodes, focus, state.focus),
|
|
768
|
+
draw: (ictx) => paintSankeyFlows(ictx, [link], nodes, focus, 1)
|
|
601
769
|
};
|
|
602
770
|
}
|
|
603
|
-
function sankeyNodeTooltip(n, valueLabel, sankey) {
|
|
771
|
+
function sankeyNodeTooltip(n, valueLabel, sankey, focus) {
|
|
604
772
|
const out = n.sourceLinks.reduce((s, l) => s + l.value, 0);
|
|
605
773
|
const inc = n.targetLinks.reduce((s, l) => s + l.value, 0);
|
|
606
774
|
const rows = [
|
|
@@ -608,24 +776,15 @@ function sankeyNodeTooltip(n, valueLabel, sankey) {
|
|
|
608
776
|
];
|
|
609
777
|
if (inc > 0) rows.push({ label: "In", value: formatValue(inc, sankey.encoding.value.format), muted: true });
|
|
610
778
|
if (out > 0) rows.push({ label: "Out", value: formatValue(out, sankey.encoding.value.format), muted: true });
|
|
779
|
+
const links = [...n.sourceLinks, ...n.targetLinks];
|
|
780
|
+
const nodes = focus ? [.../* @__PURE__ */ new Set([n, ...n.sourceLinks.map((l) => l.target), ...n.targetLinks.map((l) => l.source)])] : [n];
|
|
611
781
|
return {
|
|
612
782
|
key: n.name,
|
|
613
783
|
anchorX: (n.x0 + n.x1) / 2,
|
|
614
784
|
anchorY: center(n),
|
|
615
785
|
content: { title: n.name, rows },
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
ictx.lineCap = "butt";
|
|
619
|
-
for (const l of [...n.sourceLinks, ...n.targetLinks]) {
|
|
620
|
-
strokeLink(ictx, l, withAlpha(l.source.color, 0.72));
|
|
621
|
-
}
|
|
622
|
-
const h = Math.max(1, n.y1 - n.y0);
|
|
623
|
-
ictx.fillStyle = n.color;
|
|
624
|
-
ictx.beginPath();
|
|
625
|
-
roundedRect(ictx, n.x0, n.y0, n.x1 - n.x0, h, Math.min(3, h / 2));
|
|
626
|
-
ictx.fill();
|
|
627
|
-
ictx.restore();
|
|
628
|
-
}
|
|
786
|
+
paint: (ictx, state) => paintSankeyFlows(ictx, links, nodes, focus, state.focus),
|
|
787
|
+
draw: (ictx) => paintSankeyFlows(ictx, links, nodes, focus, 1)
|
|
629
788
|
};
|
|
630
789
|
}
|
|
631
790
|
function drawSankey(surface, spec, tokens, size, context) {
|
|
@@ -638,6 +797,9 @@ function drawSankey(surface, spec, tokens, size, context) {
|
|
|
638
797
|
const { content } = frame;
|
|
639
798
|
if (graph.nodes.length === 0 || content.width <= 0 || content.height <= 0) {
|
|
640
799
|
drawNoData(surface, tokens, content);
|
|
800
|
+
const { shown: shown2 } = sankeyMorph(context, { links: [], nodes: [] });
|
|
801
|
+
if (shown2.links.length + shown2.nodes.length > 0) paintSankeyMarks(surface, spec, shown2);
|
|
802
|
+
stashMorphFrame(surface, "sankey", shown2);
|
|
641
803
|
return;
|
|
642
804
|
}
|
|
643
805
|
layout(graph, frame.area, frame.nodeWidth, frame.pad);
|
|
@@ -645,9 +807,13 @@ function drawSankey(surface, spec, tokens, size, context) {
|
|
|
645
807
|
const probes = sankeyProbes(sankey);
|
|
646
808
|
const nodeAlpha = (n) => rowAlpha(emphasis, probes.node(n));
|
|
647
809
|
const linkAlpha = (l) => rowAlpha(emphasis, probes.link(l));
|
|
648
|
-
const sorted =
|
|
649
|
-
|
|
650
|
-
|
|
810
|
+
const sorted = [...graph.links].sort((a, b) => b.width - a.width);
|
|
811
|
+
const finals = sankeyMarks(surface, tokens, graph, sorted, frame, { node: nodeAlpha, link: linkAlpha });
|
|
812
|
+
const { shown, moved } = sankeyMorph(context, finals);
|
|
813
|
+
paintSankeyMarks(surface, spec, shown);
|
|
814
|
+
stashMorphFrame(surface, "sankey", shown);
|
|
815
|
+
drawSankeyLabels(surface, tokens, sankey, moved ? framedGraph(graph, moved) : graph, frame);
|
|
816
|
+
return buildSankeyInteraction(sankey, graph, frame, sorted, tokens);
|
|
651
817
|
}
|
|
652
818
|
export {
|
|
653
819
|
buildSankeyLayout,
|