@voila.dev/chart 5.0.114 → 5.0.117
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/canvas/paint-scene.d.ts.map +1 -1
- package/dist/canvas/paint-scene.js +3 -1
- package/dist/canvas/paint-scene.js.map +2 -2
- package/dist/contour/density.d.ts.map +1 -1
- package/dist/contour/density.js +2 -0
- package/dist/contour/density.js.map +2 -2
- package/dist/core/marks/area.d.ts.map +1 -1
- package/dist/core/marks/area.js +27 -11
- package/dist/core/marks/area.js.map +2 -2
- package/dist/core/marks/bar.d.ts.map +1 -1
- package/dist/core/marks/bar.js +2 -0
- package/dist/core/marks/bar.js.map +2 -2
- package/dist/core/marks/line.d.ts.map +1 -1
- package/dist/core/marks/line.js +13 -4
- package/dist/core/marks/line.js.map +2 -2
- package/dist/core/marks/polar.d.ts.map +1 -1
- package/dist/core/marks/polar.js +22 -5
- package/dist/core/marks/polar.js.map +2 -2
- package/dist/core/marks/shared.d.ts +8 -1
- package/dist/core/marks/shared.d.ts.map +1 -1
- package/dist/core/marks/shared.js.map +2 -2
- package/dist/core/marks/stats/difference.d.ts.map +1 -1
- package/dist/core/marks/stats/difference.js +20 -7
- package/dist/core/marks/stats/difference.js.map +2 -2
- package/dist/core/motion/arc-motion.d.ts +9 -0
- package/dist/core/motion/arc-motion.d.ts.map +1 -0
- package/dist/core/motion/arc-motion.js +67 -0
- package/dist/core/motion/arc-motion.js.map +7 -0
- package/dist/core/motion/enter-exit.d.ts +6 -0
- package/dist/core/motion/enter-exit.d.ts.map +1 -0
- package/dist/core/motion/enter-exit.js +20 -0
- package/dist/core/motion/enter-exit.js.map +7 -0
- package/dist/core/motion/leaving.d.ts +12 -0
- package/dist/core/motion/leaving.d.ts.map +1 -0
- package/dist/core/motion/leaving.js +33 -0
- package/dist/core/motion/leaving.js.map +7 -0
- package/dist/core/motion/motion-store.d.ts +4 -1
- package/dist/core/motion/motion-store.d.ts.map +1 -1
- package/dist/core/motion/motion-store.js +251 -100
- package/dist/core/motion/motion-store.js.map +3 -3
- package/dist/core/motion/points-motion.d.ts +6 -0
- package/dist/core/motion/points-motion.d.ts.map +1 -0
- package/dist/core/motion/points-motion.js +140 -0
- package/dist/core/motion/points-motion.js.map +7 -0
- package/dist/core/motion/strings.d.ts +18 -0
- package/dist/core/motion/strings.d.ts.map +1 -0
- package/dist/core/motion/strings.js +34 -0
- package/dist/core/motion/strings.js.map +7 -0
- package/dist/core/types.d.ts +85 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/geo/geo.d.ts.map +1 -1
- package/dist/geo/geo.js +2 -0
- package/dist/geo/geo.js.map +2 -2
- package/dist/react/chart.d.ts.map +1 -1
- package/dist/react/chart.js +6 -2
- package/dist/react/chart.js.map +2 -2
- package/dist/react/svg-scene.d.ts.map +1 -1
- package/dist/react/svg-scene.js +3 -1
- package/dist/react/svg-scene.js.map +2 -2
- package/dist/react/use-animated-scene.d.ts.map +1 -1
- package/dist/react/use-animated-scene.js +14 -2
- package/dist/react/use-animated-scene.js.map +2 -2
- package/dist/voronoi/voronoi.d.ts.map +1 -1
- package/dist/voronoi/voronoi.js +2 -0
- package/dist/voronoi/voronoi.js.map +2 -2
- package/package.json +2 -2
- package/src/canvas/paint-scene.ts +5 -1
- package/src/contour/density.ts +2 -0
- package/src/core/marks/area.ts +28 -13
- package/src/core/marks/bar.ts +2 -0
- package/src/core/marks/line.ts +15 -8
- package/src/core/marks/polar.ts +22 -5
- package/src/core/marks/shared.ts +8 -0
- package/src/core/marks/stats/difference.ts +28 -8
- package/src/core/motion/arc-motion.ts +113 -0
- package/src/core/motion/enter-exit.ts +44 -0
- package/src/core/motion/leaving.ts +55 -0
- package/src/core/motion/motion-store.ts +332 -109
- package/src/core/motion/points-motion.ts +190 -0
- package/src/core/motion/strings.ts +68 -0
- package/src/core/types.ts +98 -0
- package/src/geo/geo.ts +2 -0
- package/src/react/chart.tsx +8 -2
- package/src/react/svg-scene.tsx +5 -1
- package/src/react/use-animated-scene.ts +18 -2
- package/src/voronoi/voronoi.ts +2 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/core/marks/polar.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n\tchannelLabel,\n\tisPlaceable,\n\tnumericValue,\n\treadChannel,\n} from \"#/core/channel.ts\";\nimport { formatPercentage } from \"#/core/format.ts\";\nimport {\n\ttype ChartMarkOptions,\n\tmarkId,\n\tpaint,\n\tseriesResolver,\n\ttips,\n} from \"#/core/marks/shared.ts\";\nimport {\n\tarcPath,\n\ttype ChartXY,\n\tpolarToCartesian,\n\tpolygonPath,\n} from \"#/core/paths.ts\";\nimport { categoryKey } from \"#/core/scales/discrete.ts\";\nimport { niceDomain } from \"#/core/ticks.ts\";\nimport type {\n\tChartAccessor,\n\tChartMark,\n\tChartMarkContext,\n\tChartPoint,\n\tChartRect,\n\tChartValue,\n\tSceneNode,\n} from \"#/core/types.ts\";\n\n/**\n * Round marks. They own the whole plot rather than sitting on x and y\n * scales; their slices are coloured by category, which is what the legend\n * lists. Angles are degrees clockwise from twelve o'clock.\n */\n\nconst FULL_TURN = 360;\nconst EDGE = 4;\n\ninterface Frame {\n\treadonly cx: number;\n\treadonly cy: number;\n\treadonly radius: number;\n}\n\n/**\n * The circle a round mark is drawn in: centred, as large as the plot allows\n * once `insetX` (left and right) and `insetY` (top and bottom) are kept clear\n * for labels drawn outside it.\n */\nfunction frameOf(plot: ChartRect, insetX = 0, insetY = insetX): Frame {\n\treturn {\n\t\tcx: plot.x + plot.width / 2,\n\t\tcy: plot.y + plot.height / 2,\n\t\tradius: Math.max(\n\t\t\t0,\n\t\t\tMath.min(plot.width / 2 - insetX, plot.height / 2 - insetY) - EDGE,\n\t\t),\n\t};\n}\n\ninterface SliceAngle {\n\treadonly startAngle: number;\n\treadonly endAngle: number;\n\treadonly fraction: number;\n}\n\n/**\n * Splits a turn between values, proportionally. Negative values count as\n * zero: a pie of signed numbers has no honest reading.\n */\nexport function sliceAngles(\n\tvalues: ReadonlyArray<number>,\n\tpadAngle = 0,\n\tstartAngle = 0,\n\tendAngle = FULL_TURN,\n): ReadonlyArray<SliceAngle> {\n\tconst positives = values.map((value) =>\n\t\tNumber.isFinite(value) && value > 0 ? value : 0,\n\t);\n\tconst total = positives.reduce((sum, value) => sum + value, 0);\n\tconst pads = positives.filter((value) => value > 0).length > 1 ? padAngle : 0;\n\tconst available = Math.max(\n\t\t0,\n\t\tendAngle - startAngle - pads * positives.length,\n\t);\n\tlet cursor = startAngle;\n\treturn positives.map((value) => {\n\t\tconst fraction = total === 0 ? 0 : value / total;\n\t\tconst sweep = available * fraction;\n\t\tconst slice = {\n\t\t\tstartAngle: cursor + pads / 2,\n\t\t\tendAngle: cursor + pads / 2 + sweep,\n\t\t\tfraction,\n\t\t};\n\t\tcursor += sweep + pads;\n\t\treturn slice;\n\t});\n}\n\nexport interface ArcOptions<TDatum> extends ChartMarkOptions<TDatum> {\n\t/** The slice's name: what the legend, tooltip and table call it. */\n\treadonly category: ChartAccessor<TDatum, ChartValue>;\n\treadonly value: ChartAccessor<TDatum, ChartValue>;\n\t/** Share of the radius left empty in the middle: 0 is a pie, 0.6 a donut. */\n\treadonly innerRadius?: number;\n\t/** Degrees of blank between two slices. */\n\treadonly padAngle?: number;\n\treadonly startAngle?: number;\n\treadonly endAngle?: number;\n\treadonly fill?: string;\n\treadonly stroke?: string;\n}\n\nconst DEFAULT_PAD_ANGLE = 1;\n\n/** A pie, or a donut with `innerRadius`: each datum a slice of the whole. */\nexport function arc<TDatum>(\n\tdata: ReadonlyArray<TDatum>,\n\toptions: ArcOptions<TDatum>,\n): ChartMark {\n\tconst categories = readChannel(data, options.category);\n\tconst values = readChannel(data, options.value).map(numericValue);\n\tconst colorAccessor = options.color ?? options.category;\n\tconst colors = readChannel(data, colorAccessor);\n\treturn {\n\t\tid: options.id,\n\t\tcoordinate: \"frame\",\n\t\ttitles: {\n\t\t\tcategory: channelLabel(options.category),\n\t\t\tvalue: channelLabel(options.value) ?? options.label,\n\t\t},\n\t\tchannels: {\n\t\t\tcolor: {\n\t\t\t\tvalues: colors.filter(isPlaceable),\n\t\t\t\tlabel: channelLabel(colorAccessor),\n\t\t\t},\n\t\t},\n\t\trender(context) {\n\t\t\tconst id = markId(options.id, \"arc\", context);\n\t\t\tconst { cx, cy, radius } = frameOf(context.plot);\n\t\t\tconst inner = radius * (options.innerRadius ?? 0);\n\t\t\tconst angles = sliceAngles(\n\t\t\t\tvalues.map((value) => value ?? 0),\n\t\t\t\toptions.padAngle ?? DEFAULT_PAD_ANGLE,\n\t\t\t\toptions.startAngle,\n\t\t\t\toptions.endAngle,\n\t\t\t);\n\t\t\tconst seriesOf = seriesResolver(\n\t\t\t\tcolors,\n\t\t\t\t{ ...options, color: colorAccessor, fixedColor: options.fill },\n\t\t\t\tid,\n\t\t\t\tcontext,\n\t\t\t);\n\t\t\tconst nodes: SceneNode[] = [];\n\t\t\tconst points: ChartPoint[] = [];\n\t\t\tfor (const [index, angle] of angles.entries()) {\n\t\t\t\tconst category = categories[index];\n\t\t\t\tconst value = values[index];\n\t\t\t\tif (\n\t\t\t\t\t!isPlaceable(category) ||\n\t\t\t\t\tvalue === undefined ||\n\t\t\t\t\tangle.fraction === 0\n\t\t\t\t) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst series = seriesOf(index);\n\t\t\t\tconst shape = {\n\t\t\t\t\tcx,\n\t\t\t\t\tcy,\n\t\t\t\t\tinnerRadius: inner,\n\t\t\t\t\touterRadius: radius,\n\t\t\t\t\tstartAngle: angle.startAngle,\n\t\t\t\t\tendAngle: angle.endAngle,\n\t\t\t\t};\n\t\t\t\tnodes.push({\n\t\t\t\t\tkind: \"path\",\n\t\t\t\t\tkey: `${id}:${categoryKey(category)}`,\n\t\t\t\t\tseries: series.key,\n\t\t\t\t\trole: \"mark\",\n\t\t\t\t\td: arcPath(shape),\n\t\t\t\t\tpaint: paint({\n\t\t\t\t\t\tfill: series.color,\n\t\t\t\t\t\tstroke: options.stroke ?? context.theme.background,\n\t\t\t\t\t\tstrokeWidth: options.stroke ? 1 : 0,\n\t\t\t\t\t\topacity: options.opacity,\n\t\t\t\t\t}),\n\t\t\t\t});\n\t\t\t\tif (!tips(options.tip, data[index], index)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst middle = (angle.startAngle + angle.endAngle) / 2;\n\t\t\t\tconst anchor = polarToCartesian(cx, cy, (inner + radius) / 2, middle);\n\t\t\t\tpoints.push({\n\t\t\t\t\tkey: `${id}:${categoryKey(category)}`,\n\t\t\t\t\tmarkId: id,\n\t\t\t\t\tindex,\n\t\t\t\t\tdatum: data[index],\n\t\t\t\t\tx: anchor.x,\n\t\t\t\t\ty: anchor.y,\n\t\t\t\t\txValue: category,\n\t\t\t\t\tyValue: value,\n\t\t\t\t\tseries: series.key,\n\t\t\t\t\tseriesLabel: options.label,\n\t\t\t\t\tcolor: series.color,\n\t\t\t\t\ttitle: series.label ?? context.formatX(category),\n\t\t\t\t\tvalue: `${context.formatY(value)} (${formatPercentage(angle.fraction, context.locale)})`,\n\t\t\t\t\thit: { kind: \"arc\", ...shape },\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn { nodes, points };\n\t\t},\n\t};\n}\n\n/** A ring of slices: `arc` with the middle left empty. */\nexport function donut<TDatum>(\n\tdata: ReadonlyArray<TDatum>,\n\toptions: ArcOptions<TDatum>,\n): ChartMark {\n\treturn arc(data, { innerRadius: 0.6, ...options });\n}\n\nexport interface RadialBarOptions<TDatum> extends ChartMarkOptions<TDatum> {\n\treadonly category: ChartAccessor<TDatum, ChartValue>;\n\treadonly value: ChartAccessor<TDatum, ChartValue>;\n\t/** The value a full turn stands for. Defaults to the largest value, rounded up. */\n\treadonly max?: number;\n\t/** Share of the radius left empty in the middle. */\n\treadonly innerRadius?: number;\n\t/** Share of each ring's thickness left empty between two rings. */\n\treadonly gap?: number;\n\t/** Draw each ring's empty remainder, so the bars read against a full turn. */\n\treadonly track?: boolean;\n\treadonly fill?: string;\n}\n\n/** Concentric rings, each filled as far round as its value: progress against a common maximum. */\nexport function radialBar<TDatum>(\n\tdata: ReadonlyArray<TDatum>,\n\toptions: RadialBarOptions<TDatum>,\n): ChartMark {\n\tconst categories = readChannel(data, options.category);\n\tconst values = readChannel(data, options.value).map(numericValue);\n\tconst colorAccessor = options.color ?? options.category;\n\tconst colors = readChannel(data, colorAccessor);\n\treturn {\n\t\tid: options.id,\n\t\tcoordinate: \"frame\",\n\t\ttitles: {\n\t\t\tcategory: channelLabel(options.category),\n\t\t\tvalue: channelLabel(options.value) ?? options.label,\n\t\t},\n\t\tchannels: {\n\t\t\tcolor: {\n\t\t\t\tvalues: colors.filter(isPlaceable),\n\t\t\t\tlabel: channelLabel(colorAccessor),\n\t\t\t},\n\t\t},\n\t\trender(context) {\n\t\t\tconst id = markId(options.id, \"radialBar\", context);\n\t\t\tconst { cx, cy, radius } = frameOf(context.plot);\n\t\t\tconst peak = Math.max(0, ...values.map((value) => value ?? 0));\n\t\t\tconst max = options.max ?? niceDomain(0, peak)[1];\n\t\t\tconst inner = radius * (options.innerRadius ?? 0.3);\n\t\t\tconst count = Math.max(1, data.length);\n\t\t\tconst ring = (radius - inner) / count;\n\t\t\tconst thickness = ring * (1 - (options.gap ?? 0.25));\n\t\t\tconst seriesOf = seriesResolver(\n\t\t\t\tcolors,\n\t\t\t\t{ ...options, color: colorAccessor, fixedColor: options.fill },\n\t\t\t\tid,\n\t\t\t\tcontext,\n\t\t\t);\n\t\t\tconst nodes: SceneNode[] = [];\n\t\t\tconst points: ChartPoint[] = [];\n\t\t\tfor (const [index, category] of categories.entries()) {\n\t\t\t\tconst value = values[index];\n\t\t\t\tif (!isPlaceable(category) || value === undefined) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst outerRadius = radius - index * ring;\n\t\t\t\tconst shape = {\n\t\t\t\t\tcx,\n\t\t\t\t\tcy,\n\t\t\t\t\tinnerRadius: outerRadius - thickness,\n\t\t\t\t\touterRadius,\n\t\t\t\t\tstartAngle: 0,\n\t\t\t\t\tendAngle: max <= 0 ? 0 : (Math.min(value, max) / max) * FULL_TURN,\n\t\t\t\t};\n\t\t\t\tconst series = seriesOf(index);\n\t\t\t\tif (options.track !== false) {\n\t\t\t\t\tnodes.push({\n\t\t\t\t\t\tkind: \"path\",\n\t\t\t\t\t\tkey: `${id}:track:${categoryKey(category)}`,\n\t\t\t\t\t\trole: \"track\",\n\t\t\t\t\t\td: arcPath({ ...shape, endAngle: FULL_TURN }),\n\t\t\t\t\t\tpaint: { fill: context.theme.grid, opacity: 0.5 },\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tnodes.push({\n\t\t\t\t\tkind: \"path\",\n\t\t\t\t\tkey: `${id}:${categoryKey(category)}`,\n\t\t\t\t\tseries: series.key,\n\t\t\t\t\trole: \"mark\",\n\t\t\t\t\td: arcPath(shape),\n\t\t\t\t\tpaint: paint({ fill: series.color, opacity: options.opacity }),\n\t\t\t\t});\n\t\t\t\tif (!tips(options.tip, data[index], index)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst anchor = polarToCartesian(\n\t\t\t\t\tcx,\n\t\t\t\t\tcy,\n\t\t\t\t\touterRadius - thickness / 2,\n\t\t\t\t\tMath.max(shape.endAngle, 1),\n\t\t\t\t);\n\t\t\t\tpoints.push({\n\t\t\t\t\tkey: `${id}:${categoryKey(category)}`,\n\t\t\t\t\tmarkId: id,\n\t\t\t\t\tindex,\n\t\t\t\t\tdatum: data[index],\n\t\t\t\t\tx: anchor.x,\n\t\t\t\t\ty: anchor.y,\n\t\t\t\t\txValue: category,\n\t\t\t\t\tyValue: value,\n\t\t\t\t\tseries: series.key,\n\t\t\t\t\tcolor: series.color,\n\t\t\t\t\ttitle: series.label ?? context.formatX(category),\n\t\t\t\t\tvalue: context.formatY(value),\n\t\t\t\t\thit: { kind: \"arc\", ...shape, endAngle: FULL_TURN },\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn { nodes, points };\n\t\t},\n\t};\n}\n\nexport interface RadarOptions<TDatum> extends ChartMarkOptions<TDatum> {\n\t/** The spoke a value sits on: \"Vitesse\", \"Endurance\"\u2026 */\n\treadonly axis: ChartAccessor<TDatum, ChartValue>;\n\treadonly value: ChartAccessor<TDatum, ChartValue>;\n\t/** The value the outer ring stands for. Defaults to the largest value, rounded up. */\n\treadonly max?: number;\n\t/** Grid rings between the centre and the outer ring. */\n\treadonly rings?: number;\n\treadonly fillOpacity?: number;\n\treadonly fill?: string;\n\t/** `false` leaves the grid and spoke labels to another radar on the same chart. */\n\treadonly grid?: boolean;\n}\n\nconst LABEL_GAP = 8;\n\nfunction anchorFor(angle: number): {\n\treadonly textAnchor: \"start\" | \"middle\" | \"end\";\n\treadonly baseline: \"top\" | \"middle\" | \"bottom\";\n} {\n\tconst normalized = ((angle % FULL_TURN) + FULL_TURN) % FULL_TURN;\n\tconst side = Math.sin((normalized * Math.PI) / 180);\n\tconst vertical = -Math.cos((normalized * Math.PI) / 180);\n\treturn {\n\t\ttextAnchor: Math.abs(side) < 0.2 ? \"middle\" : side > 0 ? \"start\" : \"end\",\n\t\tbaseline:\n\t\t\tMath.abs(vertical) < 0.2 ? \"middle\" : vertical > 0 ? \"top\" : \"bottom\",\n\t};\n}\n\nfunction radarGrid(\n\tid: string,\n\tcontext: ChartMarkContext,\n\tframe: Frame,\n\taxes: ReadonlyArray<ChartValue>,\n\trings: number,\n): SceneNode[] {\n\tconst { cx, cy, radius } = frame;\n\tconst { theme } = context;\n\tconst nodes: SceneNode[] = [];\n\tfor (let level = 1; level <= rings; level += 1) {\n\t\tconst ringRadius = (radius * level) / rings;\n\t\tnodes.push({\n\t\t\tkind: \"path\",\n\t\t\tkey: `${id}:ring:${level}`,\n\t\t\trole: \"grid\",\n\t\t\td: polygonPath(\n\t\t\t\taxes.map((_axis, index) =>\n\t\t\t\t\tpolarToCartesian(\n\t\t\t\t\t\tcx,\n\t\t\t\t\t\tcy,\n\t\t\t\t\t\tringRadius,\n\t\t\t\t\t\t(FULL_TURN * index) / axes.length,\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t),\n\t\t\tpaint: { stroke: theme.grid, strokeWidth: 1, fill: \"none\" },\n\t\t});\n\t}\n\tfor (const [index, axis] of axes.entries()) {\n\t\tconst angle = (FULL_TURN * index) / axes.length;\n\t\tconst end = polarToCartesian(cx, cy, radius, angle);\n\t\tconst label = polarToCartesian(cx, cy, radius + LABEL_GAP, angle);\n\t\tnodes.push(\n\t\t\t{\n\t\t\t\tkind: \"line\",\n\t\t\t\tkey: `${id}:spoke:${categoryKey(axis)}`,\n\t\t\t\trole: \"grid\",\n\t\t\t\tx1: cx,\n\t\t\t\ty1: cy,\n\t\t\t\tx2: end.x,\n\t\t\t\ty2: end.y,\n\t\t\t\tpaint: { stroke: theme.grid, strokeWidth: 1 },\n\t\t\t},\n\t\t\t{\n\t\t\t\tkind: \"text\",\n\t\t\t\tkey: `${id}:axis:${categoryKey(axis)}`,\n\t\t\t\trole: \"axis\",\n\t\t\t\tx: label.x,\n\t\t\t\ty: label.y,\n\t\t\t\ttext: context.formatX(axis),\n\t\t\t\tpaint: {\n\t\t\t\t\tfill: theme.muted,\n\t\t\t\t\tfontSize: theme.fontSize,\n\t\t\t\t\t...anchorFor(angle),\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t}\n\treturn nodes;\n}\n\n/** Values on spokes joined into a polygon, one per series: profiles compared at a glance. */\nexport function radar<TDatum>(\n\tdata: ReadonlyArray<TDatum>,\n\toptions: RadarOptions<TDatum>,\n): ChartMark {\n\tconst axisValues = readChannel(data, options.axis);\n\tconst values = readChannel(data, options.value).map(numericValue);\n\tconst colors = readChannel(data, options.color);\n\tconst axes = [\n\t\t...new Map(\n\t\t\taxisValues.filter(isPlaceable).map((axis) => [categoryKey(axis), axis]),\n\t\t).values(),\n\t];\n\treturn {\n\t\tid: options.id,\n\t\tcoordinate: \"frame\",\n\t\ttitles: {\n\t\t\tcategory: channelLabel(options.axis),\n\t\t\tvalue: channelLabel(options.value) ?? options.label,\n\t\t},\n\t\tchannels:\n\t\t\toptions.color === undefined\n\t\t\t\t? {}\n\t\t\t\t: {\n\t\t\t\t\t\tcolor: {\n\t\t\t\t\t\t\tvalues: colors.filter(isPlaceable),\n\t\t\t\t\t\t\tlabel: channelLabel(options.color),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\trender(context) {\n\t\t\tconst id = markId(options.id, \"radar\", context);\n\t\t\tconst widest = Math.max(\n\t\t\t\t0,\n\t\t\t\t...axes.map((axis) =>\n\t\t\t\t\tcontext.measureText(context.formatX(axis), context.theme.fontSize),\n\t\t\t\t),\n\t\t\t);\n\t\t\tconst frame = frameOf(\n\t\t\t\tcontext.plot,\n\t\t\t\twidest + LABEL_GAP,\n\t\t\t\tcontext.theme.fontSize + LABEL_GAP,\n\t\t\t);\n\t\t\tconst peak = Math.max(0, ...values.map((value) => value ?? 0));\n\t\t\tconst max = options.max ?? niceDomain(0, peak)[1];\n\t\t\tconst seriesOf = seriesResolver(\n\t\t\t\tcolors,\n\t\t\t\t{ ...options, fixedColor: options.fill },\n\t\t\t\tid,\n\t\t\t\tcontext,\n\t\t\t);\n\t\t\tconst nodes: SceneNode[] =\n\t\t\t\toptions.grid === false\n\t\t\t\t\t? []\n\t\t\t\t\t: radarGrid(id, context, frame, axes, options.rings ?? 4);\n\t\t\tconst points: ChartPoint[] = [];\n\t\t\tconst polygons = new Map<\n\t\t\t\tstring,\n\t\t\t\t{ color: string; vertices: Array<ChartXY | undefined> }\n\t\t\t>();\n\t\t\tfor (const [index, axis] of axisValues.entries()) {\n\t\t\t\tconst value = values[index];\n\t\t\t\tconst spoke = isPlaceable(axis)\n\t\t\t\t\t? axes.findIndex(\n\t\t\t\t\t\t\t(candidate) => categoryKey(candidate) === categoryKey(axis),\n\t\t\t\t\t\t)\n\t\t\t\t\t: -1;\n\t\t\t\tif (spoke === -1 || value === undefined || !isPlaceable(axis)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst series = seriesOf(index);\n\t\t\t\tconst angle = (FULL_TURN * spoke) / axes.length;\n\t\t\t\tconst vertex = polarToCartesian(\n\t\t\t\t\tframe.cx,\n\t\t\t\t\tframe.cy,\n\t\t\t\t\tmax <= 0 ? 0 : (Math.min(value, max) / max) * frame.radius,\n\t\t\t\t\tangle,\n\t\t\t\t);\n\t\t\t\tconst polygon = polygons.get(series.key) ?? {\n\t\t\t\t\tcolor: series.color,\n\t\t\t\t\tvertices: axes.map(() => undefined),\n\t\t\t\t};\n\t\t\t\tpolygon.vertices[spoke] = vertex;\n\t\t\t\tpolygons.set(series.key, polygon);\n\t\t\t\tif (tips(options.tip, data[index], index)) {\n\t\t\t\t\tpoints.push({\n\t\t\t\t\t\tkey: `${id}:${index}`,\n\t\t\t\t\t\tmarkId: id,\n\t\t\t\t\t\tindex,\n\t\t\t\t\t\tdatum: data[index],\n\t\t\t\t\t\tx: vertex.x,\n\t\t\t\t\t\ty: vertex.y,\n\t\t\t\t\t\txValue: axis,\n\t\t\t\t\t\tyValue: value,\n\t\t\t\t\t\tseries: series.key,\n\t\t\t\t\t\tseriesLabel: series.label,\n\t\t\t\t\t\tcolor: series.color,\n\t\t\t\t\t\ttitle: context.formatX(axis),\n\t\t\t\t\t\tvalue: context.formatY(value),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const [key, polygon] of polygons) {\n\t\t\t\tnodes.push({\n\t\t\t\t\tkind: \"path\",\n\t\t\t\t\tkey: `${id}:shape:${key}`,\n\t\t\t\t\tseries: key,\n\t\t\t\t\trole: \"mark\",\n\t\t\t\t\td: polygonPath(\n\t\t\t\t\t\tpolygon.vertices.map(\n\t\t\t\t\t\t\t(vertex) => vertex ?? { x: frame.cx, y: frame.cy },\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t\tpaint: paint({\n\t\t\t\t\t\tfill: polygon.color,\n\t\t\t\t\t\tfillOpacity: options.fillOpacity ?? 0.2,\n\t\t\t\t\t\tstroke: polygon.color,\n\t\t\t\t\t\tstrokeWidth: 2,\n\t\t\t\t\t\tstrokeLinejoin: \"round\",\n\t\t\t\t\t\topacity: options.opacity,\n\t\t\t\t\t}),\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst legend =\n\t\t\t\toptions.color === undefined && options.label !== undefined\n\t\t\t\t\t? [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tkey: id,\n\t\t\t\t\t\t\t\tlabel: options.label,\n\t\t\t\t\t\t\t\tcolor: options.fill ?? context.paletteColor(context.markIndex),\n\t\t\t\t\t\t\t\tshape: \"square\" as const,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t]\n\t\t\t\t\t: [];\n\t\t\treturn { nodes, points, legend };\n\t\t},\n\t};\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import {\n\tchannelLabel,\n\tisPlaceable,\n\tnumericValue,\n\treadChannel,\n} from \"#/core/channel.ts\";\nimport { formatPercentage } from \"#/core/format.ts\";\nimport {\n\ttype ChartMarkOptions,\n\tmarkId,\n\tpaint,\n\tseriesResolver,\n\ttips,\n} from \"#/core/marks/shared.ts\";\nimport { ARC_MOTION } from \"#/core/motion/arc-motion.ts\";\nimport { POINTS_MOTION } from \"#/core/motion/points-motion.ts\";\nimport {\n\tarcPath,\n\ttype ChartXY,\n\tpolarToCartesian,\n\tpolygonPath,\n} from \"#/core/paths.ts\";\nimport { categoryKey } from \"#/core/scales/discrete.ts\";\nimport { niceDomain } from \"#/core/ticks.ts\";\nimport type {\n\tChartAccessor,\n\tChartMark,\n\tChartMarkContext,\n\tChartPoint,\n\tChartRect,\n\tChartValue,\n\tSceneNode,\n} from \"#/core/types.ts\";\n\n/**\n * Round marks. They own the whole plot rather than sitting on x and y\n * scales; their slices are coloured by category, which is what the legend\n * lists. Angles are degrees clockwise from twelve o'clock.\n */\n\nconst FULL_TURN = 360;\nconst EDGE = 4;\n\ninterface Frame {\n\treadonly cx: number;\n\treadonly cy: number;\n\treadonly radius: number;\n}\n\n/**\n * The circle a round mark is drawn in: centred, as large as the plot allows\n * once `insetX` (left and right) and `insetY` (top and bottom) are kept clear\n * for labels drawn outside it.\n */\nfunction frameOf(plot: ChartRect, insetX = 0, insetY = insetX): Frame {\n\treturn {\n\t\tcx: plot.x + plot.width / 2,\n\t\tcy: plot.y + plot.height / 2,\n\t\tradius: Math.max(\n\t\t\t0,\n\t\t\tMath.min(plot.width / 2 - insetX, plot.height / 2 - insetY) - EDGE,\n\t\t),\n\t};\n}\n\ninterface SliceAngle {\n\treadonly startAngle: number;\n\treadonly endAngle: number;\n\treadonly fraction: number;\n}\n\n/**\n * Splits a turn between values, proportionally. Negative values count as\n * zero: a pie of signed numbers has no honest reading.\n */\nexport function sliceAngles(\n\tvalues: ReadonlyArray<number>,\n\tpadAngle = 0,\n\tstartAngle = 0,\n\tendAngle = FULL_TURN,\n): ReadonlyArray<SliceAngle> {\n\tconst positives = values.map((value) =>\n\t\tNumber.isFinite(value) && value > 0 ? value : 0,\n\t);\n\tconst total = positives.reduce((sum, value) => sum + value, 0);\n\tconst pads = positives.filter((value) => value > 0).length > 1 ? padAngle : 0;\n\tconst available = Math.max(\n\t\t0,\n\t\tendAngle - startAngle - pads * positives.length,\n\t);\n\tlet cursor = startAngle;\n\treturn positives.map((value) => {\n\t\tconst fraction = total === 0 ? 0 : value / total;\n\t\tconst sweep = available * fraction;\n\t\tconst slice = {\n\t\t\tstartAngle: cursor + pads / 2,\n\t\t\tendAngle: cursor + pads / 2 + sweep,\n\t\t\tfraction,\n\t\t};\n\t\tcursor += sweep + pads;\n\t\treturn slice;\n\t});\n}\n\nexport interface ArcOptions<TDatum> extends ChartMarkOptions<TDatum> {\n\t/** The slice's name: what the legend, tooltip and table call it. */\n\treadonly category: ChartAccessor<TDatum, ChartValue>;\n\treadonly value: ChartAccessor<TDatum, ChartValue>;\n\t/** Share of the radius left empty in the middle: 0 is a pie, 0.6 a donut. */\n\treadonly innerRadius?: number;\n\t/** Degrees of blank between two slices. */\n\treadonly padAngle?: number;\n\treadonly startAngle?: number;\n\treadonly endAngle?: number;\n\treadonly fill?: string;\n\treadonly stroke?: string;\n}\n\nconst DEFAULT_PAD_ANGLE = 1;\n\n/** A pie, or a donut with `innerRadius`: each datum a slice of the whole. */\nexport function arc<TDatum>(\n\tdata: ReadonlyArray<TDatum>,\n\toptions: ArcOptions<TDatum>,\n): ChartMark {\n\tconst categories = readChannel(data, options.category);\n\tconst values = readChannel(data, options.value).map(numericValue);\n\tconst colorAccessor = options.color ?? options.category;\n\tconst colors = readChannel(data, colorAccessor);\n\treturn {\n\t\tid: options.id,\n\t\tcoordinate: \"frame\",\n\t\ttitles: {\n\t\t\tcategory: channelLabel(options.category),\n\t\t\tvalue: channelLabel(options.value) ?? options.label,\n\t\t},\n\t\tchannels: {\n\t\t\tcolor: {\n\t\t\t\tvalues: colors.filter(isPlaceable),\n\t\t\t\tlabel: channelLabel(colorAccessor),\n\t\t\t},\n\t\t},\n\t\trender(context) {\n\t\t\tconst id = markId(options.id, \"arc\", context);\n\t\t\tconst { cx, cy, radius } = frameOf(context.plot);\n\t\t\tconst inner = radius * (options.innerRadius ?? 0);\n\t\t\tconst angles = sliceAngles(\n\t\t\t\tvalues.map((value) => value ?? 0),\n\t\t\t\toptions.padAngle ?? DEFAULT_PAD_ANGLE,\n\t\t\t\toptions.startAngle,\n\t\t\t\toptions.endAngle,\n\t\t\t);\n\t\t\tconst seriesOf = seriesResolver(\n\t\t\t\tcolors,\n\t\t\t\t{ ...options, color: colorAccessor, fixedColor: options.fill },\n\t\t\t\tid,\n\t\t\t\tcontext,\n\t\t\t);\n\t\t\tconst nodes: SceneNode[] = [];\n\t\t\tconst points: ChartPoint[] = [];\n\t\t\tfor (const [index, angle] of angles.entries()) {\n\t\t\t\tconst category = categories[index];\n\t\t\t\tconst value = values[index];\n\t\t\t\tif (\n\t\t\t\t\t!isPlaceable(category) ||\n\t\t\t\t\tvalue === undefined ||\n\t\t\t\t\tangle.fraction === 0\n\t\t\t\t) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst series = seriesOf(index);\n\t\t\t\tconst shape = {\n\t\t\t\t\tcx,\n\t\t\t\t\tcy,\n\t\t\t\t\tinnerRadius: inner,\n\t\t\t\t\touterRadius: radius,\n\t\t\t\t\tstartAngle: angle.startAngle,\n\t\t\t\t\tendAngle: angle.endAngle,\n\t\t\t\t};\n\t\t\t\tnodes.push({\n\t\t\t\t\tkind: \"path\",\n\t\t\t\t\tkey: `${id}:${categoryKey(category)}`,\n\t\t\t\t\tseries: series.key,\n\t\t\t\t\trole: \"mark\",\n\t\t\t\t\td: arcPath(shape),\n\t\t\t\t\tgeometry: { kind: \"arc\", ...shape },\n\t\t\t\t\tmotion: ARC_MOTION,\n\t\t\t\t\tenter: options.enter ?? \"grow\",\n\t\t\t\t\tpaint: paint({\n\t\t\t\t\t\tfill: series.color,\n\t\t\t\t\t\tstroke: options.stroke ?? context.theme.background,\n\t\t\t\t\t\tstrokeWidth: options.stroke ? 1 : 0,\n\t\t\t\t\t\topacity: options.opacity,\n\t\t\t\t\t}),\n\t\t\t\t});\n\t\t\t\tif (!tips(options.tip, data[index], index)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst middle = (angle.startAngle + angle.endAngle) / 2;\n\t\t\t\tconst anchor = polarToCartesian(cx, cy, (inner + radius) / 2, middle);\n\t\t\t\tpoints.push({\n\t\t\t\t\tkey: `${id}:${categoryKey(category)}`,\n\t\t\t\t\tmarkId: id,\n\t\t\t\t\tindex,\n\t\t\t\t\tdatum: data[index],\n\t\t\t\t\tx: anchor.x,\n\t\t\t\t\ty: anchor.y,\n\t\t\t\t\txValue: category,\n\t\t\t\t\tyValue: value,\n\t\t\t\t\tseries: series.key,\n\t\t\t\t\tseriesLabel: options.label,\n\t\t\t\t\tcolor: series.color,\n\t\t\t\t\ttitle: series.label ?? context.formatX(category),\n\t\t\t\t\tvalue: `${context.formatY(value)} (${formatPercentage(angle.fraction, context.locale)})`,\n\t\t\t\t\thit: { kind: \"arc\", ...shape },\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn { nodes, points };\n\t\t},\n\t};\n}\n\n/** A ring of slices: `arc` with the middle left empty. */\nexport function donut<TDatum>(\n\tdata: ReadonlyArray<TDatum>,\n\toptions: ArcOptions<TDatum>,\n): ChartMark {\n\treturn arc(data, { innerRadius: 0.6, ...options });\n}\n\nexport interface RadialBarOptions<TDatum> extends ChartMarkOptions<TDatum> {\n\treadonly category: ChartAccessor<TDatum, ChartValue>;\n\treadonly value: ChartAccessor<TDatum, ChartValue>;\n\t/** The value a full turn stands for. Defaults to the largest value, rounded up. */\n\treadonly max?: number;\n\t/** Share of the radius left empty in the middle. */\n\treadonly innerRadius?: number;\n\t/** Share of each ring's thickness left empty between two rings. */\n\treadonly gap?: number;\n\t/** Draw each ring's empty remainder, so the bars read against a full turn. */\n\treadonly track?: boolean;\n\treadonly fill?: string;\n}\n\n/** Concentric rings, each filled as far round as its value: progress against a common maximum. */\nexport function radialBar<TDatum>(\n\tdata: ReadonlyArray<TDatum>,\n\toptions: RadialBarOptions<TDatum>,\n): ChartMark {\n\tconst categories = readChannel(data, options.category);\n\tconst values = readChannel(data, options.value).map(numericValue);\n\tconst colorAccessor = options.color ?? options.category;\n\tconst colors = readChannel(data, colorAccessor);\n\treturn {\n\t\tid: options.id,\n\t\tcoordinate: \"frame\",\n\t\ttitles: {\n\t\t\tcategory: channelLabel(options.category),\n\t\t\tvalue: channelLabel(options.value) ?? options.label,\n\t\t},\n\t\tchannels: {\n\t\t\tcolor: {\n\t\t\t\tvalues: colors.filter(isPlaceable),\n\t\t\t\tlabel: channelLabel(colorAccessor),\n\t\t\t},\n\t\t},\n\t\trender(context) {\n\t\t\tconst id = markId(options.id, \"radialBar\", context);\n\t\t\tconst { cx, cy, radius } = frameOf(context.plot);\n\t\t\tconst peak = Math.max(0, ...values.map((value) => value ?? 0));\n\t\t\tconst max = options.max ?? niceDomain(0, peak)[1];\n\t\t\tconst inner = radius * (options.innerRadius ?? 0.3);\n\t\t\tconst count = Math.max(1, data.length);\n\t\t\tconst ring = (radius - inner) / count;\n\t\t\tconst thickness = ring * (1 - (options.gap ?? 0.25));\n\t\t\tconst seriesOf = seriesResolver(\n\t\t\t\tcolors,\n\t\t\t\t{ ...options, color: colorAccessor, fixedColor: options.fill },\n\t\t\t\tid,\n\t\t\t\tcontext,\n\t\t\t);\n\t\t\tconst nodes: SceneNode[] = [];\n\t\t\tconst points: ChartPoint[] = [];\n\t\t\tfor (const [index, category] of categories.entries()) {\n\t\t\t\tconst value = values[index];\n\t\t\t\tif (!isPlaceable(category) || value === undefined) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst outerRadius = radius - index * ring;\n\t\t\t\tconst shape = {\n\t\t\t\t\tcx,\n\t\t\t\t\tcy,\n\t\t\t\t\tinnerRadius: outerRadius - thickness,\n\t\t\t\t\touterRadius,\n\t\t\t\t\tstartAngle: 0,\n\t\t\t\t\tendAngle: max <= 0 ? 0 : (Math.min(value, max) / max) * FULL_TURN,\n\t\t\t\t};\n\t\t\t\tconst series = seriesOf(index);\n\t\t\t\tif (options.track !== false) {\n\t\t\t\t\tnodes.push({\n\t\t\t\t\t\tkind: \"path\",\n\t\t\t\t\t\tkey: `${id}:track:${categoryKey(category)}`,\n\t\t\t\t\t\trole: \"track\",\n\t\t\t\t\t\td: arcPath({ ...shape, endAngle: FULL_TURN }),\n\t\t\t\t\t\tpaint: { fill: context.theme.grid, opacity: 0.5 },\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tnodes.push({\n\t\t\t\t\tkind: \"path\",\n\t\t\t\t\tkey: `${id}:${categoryKey(category)}`,\n\t\t\t\t\tseries: series.key,\n\t\t\t\t\trole: \"mark\",\n\t\t\t\t\td: arcPath(shape),\n\t\t\t\t\tgeometry: { kind: \"arc\", ...shape },\n\t\t\t\t\tmotion: ARC_MOTION,\n\t\t\t\t\tenter: options.enter ?? \"grow\",\n\t\t\t\t\tpaint: paint({ fill: series.color, opacity: options.opacity }),\n\t\t\t\t});\n\t\t\t\tif (!tips(options.tip, data[index], index)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst anchor = polarToCartesian(\n\t\t\t\t\tcx,\n\t\t\t\t\tcy,\n\t\t\t\t\touterRadius - thickness / 2,\n\t\t\t\t\tMath.max(shape.endAngle, 1),\n\t\t\t\t);\n\t\t\t\tpoints.push({\n\t\t\t\t\tkey: `${id}:${categoryKey(category)}`,\n\t\t\t\t\tmarkId: id,\n\t\t\t\t\tindex,\n\t\t\t\t\tdatum: data[index],\n\t\t\t\t\tx: anchor.x,\n\t\t\t\t\ty: anchor.y,\n\t\t\t\t\txValue: category,\n\t\t\t\t\tyValue: value,\n\t\t\t\t\tseries: series.key,\n\t\t\t\t\tcolor: series.color,\n\t\t\t\t\ttitle: series.label ?? context.formatX(category),\n\t\t\t\t\tvalue: context.formatY(value),\n\t\t\t\t\thit: { kind: \"arc\", ...shape, endAngle: FULL_TURN },\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn { nodes, points };\n\t\t},\n\t};\n}\n\nexport interface RadarOptions<TDatum> extends ChartMarkOptions<TDatum> {\n\t/** The spoke a value sits on: \"Vitesse\", \"Endurance\"\u2026 */\n\treadonly axis: ChartAccessor<TDatum, ChartValue>;\n\treadonly value: ChartAccessor<TDatum, ChartValue>;\n\t/** The value the outer ring stands for. Defaults to the largest value, rounded up. */\n\treadonly max?: number;\n\t/** Grid rings between the centre and the outer ring. */\n\treadonly rings?: number;\n\treadonly fillOpacity?: number;\n\treadonly fill?: string;\n\t/** `false` leaves the grid and spoke labels to another radar on the same chart. */\n\treadonly grid?: boolean;\n}\n\nconst LABEL_GAP = 8;\n\nfunction anchorFor(angle: number): {\n\treadonly textAnchor: \"start\" | \"middle\" | \"end\";\n\treadonly baseline: \"top\" | \"middle\" | \"bottom\";\n} {\n\tconst normalized = ((angle % FULL_TURN) + FULL_TURN) % FULL_TURN;\n\tconst side = Math.sin((normalized * Math.PI) / 180);\n\tconst vertical = -Math.cos((normalized * Math.PI) / 180);\n\treturn {\n\t\ttextAnchor: Math.abs(side) < 0.2 ? \"middle\" : side > 0 ? \"start\" : \"end\",\n\t\tbaseline:\n\t\t\tMath.abs(vertical) < 0.2 ? \"middle\" : vertical > 0 ? \"top\" : \"bottom\",\n\t};\n}\n\nfunction radarGrid(\n\tid: string,\n\tcontext: ChartMarkContext,\n\tframe: Frame,\n\taxes: ReadonlyArray<ChartValue>,\n\trings: number,\n): SceneNode[] {\n\tconst { cx, cy, radius } = frame;\n\tconst { theme } = context;\n\tconst nodes: SceneNode[] = [];\n\tfor (let level = 1; level <= rings; level += 1) {\n\t\tconst ringRadius = (radius * level) / rings;\n\t\tnodes.push({\n\t\t\tkind: \"path\",\n\t\t\tkey: `${id}:ring:${level}`,\n\t\t\trole: \"grid\",\n\t\t\td: polygonPath(\n\t\t\t\taxes.map((_axis, index) =>\n\t\t\t\t\tpolarToCartesian(\n\t\t\t\t\t\tcx,\n\t\t\t\t\t\tcy,\n\t\t\t\t\t\tringRadius,\n\t\t\t\t\t\t(FULL_TURN * index) / axes.length,\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t),\n\t\t\tpaint: { stroke: theme.grid, strokeWidth: 1, fill: \"none\" },\n\t\t});\n\t}\n\tfor (const [index, axis] of axes.entries()) {\n\t\tconst angle = (FULL_TURN * index) / axes.length;\n\t\tconst end = polarToCartesian(cx, cy, radius, angle);\n\t\tconst label = polarToCartesian(cx, cy, radius + LABEL_GAP, angle);\n\t\tnodes.push(\n\t\t\t{\n\t\t\t\tkind: \"line\",\n\t\t\t\tkey: `${id}:spoke:${categoryKey(axis)}`,\n\t\t\t\trole: \"grid\",\n\t\t\t\tx1: cx,\n\t\t\t\ty1: cy,\n\t\t\t\tx2: end.x,\n\t\t\t\ty2: end.y,\n\t\t\t\tpaint: { stroke: theme.grid, strokeWidth: 1 },\n\t\t\t},\n\t\t\t{\n\t\t\t\tkind: \"text\",\n\t\t\t\tkey: `${id}:axis:${categoryKey(axis)}`,\n\t\t\t\trole: \"axis\",\n\t\t\t\tx: label.x,\n\t\t\t\ty: label.y,\n\t\t\t\ttext: context.formatX(axis),\n\t\t\t\tpaint: {\n\t\t\t\t\tfill: theme.muted,\n\t\t\t\t\tfontSize: theme.fontSize,\n\t\t\t\t\t...anchorFor(angle),\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t}\n\treturn nodes;\n}\n\n/** Values on spokes joined into a polygon, one per series: profiles compared at a glance. */\nexport function radar<TDatum>(\n\tdata: ReadonlyArray<TDatum>,\n\toptions: RadarOptions<TDatum>,\n): ChartMark {\n\tconst axisValues = readChannel(data, options.axis);\n\tconst values = readChannel(data, options.value).map(numericValue);\n\tconst colors = readChannel(data, options.color);\n\tconst axes = [\n\t\t...new Map(\n\t\t\taxisValues.filter(isPlaceable).map((axis) => [categoryKey(axis), axis]),\n\t\t).values(),\n\t];\n\treturn {\n\t\tid: options.id,\n\t\tcoordinate: \"frame\",\n\t\ttitles: {\n\t\t\tcategory: channelLabel(options.axis),\n\t\t\tvalue: channelLabel(options.value) ?? options.label,\n\t\t},\n\t\tchannels:\n\t\t\toptions.color === undefined\n\t\t\t\t? {}\n\t\t\t\t: {\n\t\t\t\t\t\tcolor: {\n\t\t\t\t\t\t\tvalues: colors.filter(isPlaceable),\n\t\t\t\t\t\t\tlabel: channelLabel(options.color),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\trender(context) {\n\t\t\tconst id = markId(options.id, \"radar\", context);\n\t\t\tconst widest = Math.max(\n\t\t\t\t0,\n\t\t\t\t...axes.map((axis) =>\n\t\t\t\t\tcontext.measureText(context.formatX(axis), context.theme.fontSize),\n\t\t\t\t),\n\t\t\t);\n\t\t\tconst frame = frameOf(\n\t\t\t\tcontext.plot,\n\t\t\t\twidest + LABEL_GAP,\n\t\t\t\tcontext.theme.fontSize + LABEL_GAP,\n\t\t\t);\n\t\t\tconst peak = Math.max(0, ...values.map((value) => value ?? 0));\n\t\t\tconst max = options.max ?? niceDomain(0, peak)[1];\n\t\t\tconst seriesOf = seriesResolver(\n\t\t\t\tcolors,\n\t\t\t\t{ ...options, fixedColor: options.fill },\n\t\t\t\tid,\n\t\t\t\tcontext,\n\t\t\t);\n\t\t\tconst nodes: SceneNode[] =\n\t\t\t\toptions.grid === false\n\t\t\t\t\t? []\n\t\t\t\t\t: radarGrid(id, context, frame, axes, options.rings ?? 4);\n\t\t\tconst points: ChartPoint[] = [];\n\t\t\tconst polygons = new Map<\n\t\t\t\tstring,\n\t\t\t\t{ color: string; vertices: Array<ChartXY | undefined> }\n\t\t\t>();\n\t\t\tfor (const [index, axis] of axisValues.entries()) {\n\t\t\t\tconst value = values[index];\n\t\t\t\tconst spoke = isPlaceable(axis)\n\t\t\t\t\t? axes.findIndex(\n\t\t\t\t\t\t\t(candidate) => categoryKey(candidate) === categoryKey(axis),\n\t\t\t\t\t\t)\n\t\t\t\t\t: -1;\n\t\t\t\tif (spoke === -1 || value === undefined || !isPlaceable(axis)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst series = seriesOf(index);\n\t\t\t\tconst angle = (FULL_TURN * spoke) / axes.length;\n\t\t\t\tconst vertex = polarToCartesian(\n\t\t\t\t\tframe.cx,\n\t\t\t\t\tframe.cy,\n\t\t\t\t\tmax <= 0 ? 0 : (Math.min(value, max) / max) * frame.radius,\n\t\t\t\t\tangle,\n\t\t\t\t);\n\t\t\t\tconst polygon = polygons.get(series.key) ?? {\n\t\t\t\t\tcolor: series.color,\n\t\t\t\t\tvertices: axes.map(() => undefined),\n\t\t\t\t};\n\t\t\t\tpolygon.vertices[spoke] = vertex;\n\t\t\t\tpolygons.set(series.key, polygon);\n\t\t\t\tif (tips(options.tip, data[index], index)) {\n\t\t\t\t\tpoints.push({\n\t\t\t\t\t\tkey: `${id}:${index}`,\n\t\t\t\t\t\tmarkId: id,\n\t\t\t\t\t\tindex,\n\t\t\t\t\t\tdatum: data[index],\n\t\t\t\t\t\tx: vertex.x,\n\t\t\t\t\t\ty: vertex.y,\n\t\t\t\t\t\txValue: axis,\n\t\t\t\t\t\tyValue: value,\n\t\t\t\t\t\tseries: series.key,\n\t\t\t\t\t\tseriesLabel: series.label,\n\t\t\t\t\t\tcolor: series.color,\n\t\t\t\t\t\ttitle: context.formatX(axis),\n\t\t\t\t\t\tvalue: context.formatY(value),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const [key, polygon] of polygons) {\n\t\t\t\tconst geometry = {\n\t\t\t\t\tkind: \"points\" as const,\n\t\t\t\t\tshape: \"polygon\" as const,\n\t\t\t\t\truns: [\n\t\t\t\t\t\tpolygon.vertices.map((vertex, spoke) => ({\n\t\t\t\t\t\t\tkey: categoryKey(axes[spoke] as ChartValue),\n\t\t\t\t\t\t\t...(vertex ?? { x: frame.cx, y: frame.cy }),\n\t\t\t\t\t\t})),\n\t\t\t\t\t],\n\t\t\t\t};\n\t\t\t\tnodes.push({\n\t\t\t\t\tkind: \"path\",\n\t\t\t\t\tkey: `${id}:shape:${key}`,\n\t\t\t\t\tseries: key,\n\t\t\t\t\trole: \"mark\",\n\t\t\t\t\td: polygonPath(geometry.runs[0]),\n\t\t\t\t\tgeometry,\n\t\t\t\t\tmotion: POINTS_MOTION,\n\t\t\t\t\tenter: options.enter,\n\t\t\t\t\tpaint: paint({\n\t\t\t\t\t\tfill: polygon.color,\n\t\t\t\t\t\tfillOpacity: options.fillOpacity ?? 0.2,\n\t\t\t\t\t\tstroke: polygon.color,\n\t\t\t\t\t\tstrokeWidth: 2,\n\t\t\t\t\t\tstrokeLinejoin: \"round\",\n\t\t\t\t\t\topacity: options.opacity,\n\t\t\t\t\t}),\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst legend =\n\t\t\t\toptions.color === undefined && options.label !== undefined\n\t\t\t\t\t? [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tkey: id,\n\t\t\t\t\t\t\t\tlabel: options.label,\n\t\t\t\t\t\t\t\tcolor: options.fill ?? context.paletteColor(context.markIndex),\n\t\t\t\t\t\t\t\tshape: \"square\" as const,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t]\n\t\t\t\t\t: [];\n\t\t\treturn { nodes, points, legend };\n\t\t},\n\t};\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ChartAccessor, ChartChannel, ChartMarkContext, ChartPaint, ChartPositionScale, ChartValue } from "../types.js";
|
|
1
|
+
import type { ChartAccessor, ChartChannel, ChartEnter, ChartMarkContext, ChartPaint, ChartPositionScale, ChartValue } from "../types.js";
|
|
2
2
|
/** Options every mark takes. */
|
|
3
3
|
export interface ChartMarkOptions<TDatum> {
|
|
4
4
|
/** Stable identity, so focus and legend state survive a data update. */
|
|
@@ -14,6 +14,13 @@ export interface ChartMarkOptions<TDatum> {
|
|
|
14
14
|
*/
|
|
15
15
|
readonly tip?: boolean | ((datum: TDatum, index: number) => boolean);
|
|
16
16
|
readonly opacity?: number;
|
|
17
|
+
/**
|
|
18
|
+
* How the mark appears when an update adds it: "grow" from its baseline
|
|
19
|
+
* or neighbour (the default for bars, areas and slices), "fade" (the
|
|
20
|
+
* default otherwise), "none". A line can also "draw": trace itself in on
|
|
21
|
+
* the first render.
|
|
22
|
+
*/
|
|
23
|
+
readonly enter?: ChartEnter;
|
|
17
24
|
}
|
|
18
25
|
export declare function markId(id: string | undefined, kind: string, context: ChartMarkContext): string;
|
|
19
26
|
/** A datum's series: its color value when there is a color channel, the mark otherwise. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/core/marks/shared.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACX,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,UAAU,EACV,kBAAkB,EAClB,UAAU,EACV,MAAM,iBAAiB,CAAC;AAEzB,gCAAgC;AAChC,MAAM,WAAW,gBAAgB,CAAC,MAAM;IACvC,wEAAwE;IACxE,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,mFAAmF;IACnF,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,qFAAqF;IACrF,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACnD;;;;OAIG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;IACrE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/core/marks/shared.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACX,aAAa,EACb,YAAY,EACZ,UAAU,EACV,gBAAgB,EAChB,UAAU,EACV,kBAAkB,EAClB,UAAU,EACV,MAAM,iBAAiB,CAAC;AAEzB,gCAAgC;AAChC,MAAM,WAAW,gBAAgB,CAAC,MAAM;IACvC,wEAAwE;IACxE,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,mFAAmF;IACnF,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,qFAAqF;IACrF,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACnD;;;;OAIG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;IACrE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;CAC5B;AAED,wBAAgB,MAAM,CACrB,EAAE,EAAE,MAAM,GAAG,SAAS,EACtB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,gBAAgB,GACvB,MAAM,CAER;AAED,2FAA2F;AAC3F,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,cAAc,CAAC,MAAM,EACpC,MAAM,EAAE,aAAa,CAAC,UAAU,GAAG,SAAS,CAAC,EAC7C,OAAO,EAAE,gBAAgB,CAAC,MAAM,CAAC,GAAG;IAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,EACpE,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,gBAAgB,GACvB,CAAC,KAAK,EAAE,MAAM,KAAK,UAAU,CAkB/B;AAED,wBAAgB,YAAY,CAAC,MAAM,EAClC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,EAC3B,OAAO,EAAE,gBAAgB,CAAC,MAAM,CAAC,GAC/B;IACF,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,UAAU,GAAG,SAAS,CAAC,CAAC;IACvD,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,SAAS,CAAC;CAC3C,CAYA;AAED,uFAAuF;AACvF,wBAAgB,KAAK,CAAC,MAAM,SAAS,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAIvE;AAED,qEAAqE;AACrE,wBAAgB,aAAa,CAC5B,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,UAAU,GACrC,WAAW,CACb,MAAM,EACN;IAAE,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAC3D,CAYA;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CACtB,KAAK,EAAE,kBAAkB,EACzB,GAAG,EAAE,OAAO,GACV,UAAU,GAAG,SAAS,CAQxB;AAED,wEAAwE;AACxE,wBAAgB,IAAI,CAAC,MAAM,EAC1B,GAAG,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,EACpC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACX,OAAO,CAET"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/core/marks/shared.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n\tchannelLabel,\n\tisPlaceable,\n\tnumericValue,\n\treadChannel,\n} from \"#/core/channel.ts\";\nimport { categoryKey } from \"#/core/scales/discrete.ts\";\nimport type {\n\tChartAccessor,\n\tChartChannel,\n\tChartMarkContext,\n\tChartPaint,\n\tChartPositionScale,\n\tChartValue,\n} from \"#/core/types.ts\";\n\n/** Options every mark takes. */\nexport interface ChartMarkOptions<TDatum> {\n\t/** Stable identity, so focus and legend state survive a data update. */\n\treadonly id?: string;\n\t/** The series' name when the mark has no color channel: legend, tooltip, table. */\n\treadonly label?: string;\n\t/** A field or function whose distinct values split the mark into coloured series. */\n\treadonly color?: ChartAccessor<TDatum, ChartValue>;\n\t/**\n\t * `false` keeps the mark out of focus, tooltips and the data table; a\n\t * function decides per datum (the point where a recorded line hands over\n\t * to its dashed projection belongs to one of the two, not both).\n\t */\n\treadonly tip?: boolean | ((datum: TDatum, index: number) => boolean);\n\treadonly opacity?: number;\n}\n\nexport function markId(\n\tid: string | undefined,\n\tkind: string,\n\tcontext: ChartMarkContext,\n): string {\n\treturn id ?? `${kind}-${context.markIndex}`;\n}\n\n/** A datum's series: its color value when there is a color channel, the mark otherwise. */\nexport interface SeriesInfo {\n\treadonly key: string;\n\treadonly label: string | undefined;\n\treadonly color: string;\n}\n\nexport function seriesResolver<TDatum>(\n\tcolors: ReadonlyArray<ChartValue | undefined>,\n\toptions: ChartMarkOptions<TDatum> & { readonly fixedColor?: string },\n\tid: string,\n\tcontext: ChartMarkContext,\n): (index: number) => SeriesInfo {\n\tconst fallback =\n\t\toptions.fixedColor ?? context.paletteColor(context.markIndex);\n\tconst single: SeriesInfo = { key: id, label: options.label, color: fallback };\n\tif (options.color === undefined) {\n\t\treturn () => single;\n\t}\n\treturn (index) => {\n\t\tconst value = colors[index];\n\t\tif (value === undefined) {\n\t\t\treturn single;\n\t\t}\n\t\treturn {\n\t\t\tkey: categoryKey(value),\n\t\t\tlabel: context.seriesLabel(value),\n\t\t\tcolor: options.fixedColor ?? context.colorOf(value),\n\t\t};\n\t};\n}\n\nexport function colorChannel<TDatum>(\n\tdata: ReadonlyArray<TDatum>,\n\toptions: ChartMarkOptions<TDatum>,\n): {\n\treadonly values: ReadonlyArray<ChartValue | undefined>;\n\treadonly channel: ChartChannel | undefined;\n} {\n\tconst values = readChannel(data, options.color);\n\treturn {\n\t\tvalues,\n\t\tchannel:\n\t\t\toptions.color === undefined\n\t\t\t\t? undefined\n\t\t\t\t: {\n\t\t\t\t\t\tvalues: values.filter(isPlaceable),\n\t\t\t\t\t\tlabel: channelLabel(options.color),\n\t\t\t\t\t},\n\t};\n}\n\n/** Keeps only the defined paint keys, so a scene snapshot shows what was asked for. */\nexport function paint<TPaint extends ChartPaint>(values: TPaint): TPaint {\n\treturn Object.fromEntries(\n\t\tObject.entries(values).filter(([, value]) => value !== undefined),\n\t) as TPaint;\n}\n\n/** Indices of `data` grouped by series, each group in data order. */\nexport function groupBySeries(\n\tcount: number,\n\tseriesOf: (index: number) => SeriesInfo,\n): ReadonlyMap<\n\tstring,\n\t{ readonly series: SeriesInfo; readonly indices: number[] }\n> {\n\tconst groups = new Map<string, { series: SeriesInfo; indices: number[] }>();\n\tfor (let index = 0; index < count; index += 1) {\n\t\tconst series = seriesOf(index);\n\t\tconst group = groups.get(series.key);\n\t\tif (group === undefined) {\n\t\t\tgroups.set(series.key, { series, indices: [index] });\n\t\t} else {\n\t\t\tgroup.indices.push(index);\n\t\t}\n\t}\n\treturn groups;\n}\n\n/**\n * A raw channel value as the scale can place it, or `undefined` for a gap. A\n * continuous scale reads numeric strings as numbers; a discrete one takes any\n * category as is.\n */\nexport function valueOn(\n\tscale: ChartPositionScale,\n\traw: unknown,\n): ChartValue | undefined {\n\tif (scale.kind === \"band\" || scale.kind === \"point\") {\n\t\treturn isPlaceable(raw) ? raw : undefined;\n\t}\n\tif (raw instanceof Date) {\n\t\treturn Number.isNaN(raw.getTime()) ? undefined : raw;\n\t}\n\treturn numericValue(raw);\n}\n\n/** Whether a datum takes part in focus, tooltips and the data table. */\nexport function tips<TDatum>(\n\ttip: ChartMarkOptions<TDatum>[\"tip\"],\n\tdatum: TDatum,\n\tindex: number,\n): boolean {\n\treturn typeof tip === \"function\" ? tip(datum, index) : tip !== false;\n}\n"],
|
|
5
|
-
"mappings": "AAAA;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,SAAS,mBAAmB;
|
|
4
|
+
"sourcesContent": ["import {\n\tchannelLabel,\n\tisPlaceable,\n\tnumericValue,\n\treadChannel,\n} from \"#/core/channel.ts\";\nimport { categoryKey } from \"#/core/scales/discrete.ts\";\nimport type {\n\tChartAccessor,\n\tChartChannel,\n\tChartEnter,\n\tChartMarkContext,\n\tChartPaint,\n\tChartPositionScale,\n\tChartValue,\n} from \"#/core/types.ts\";\n\n/** Options every mark takes. */\nexport interface ChartMarkOptions<TDatum> {\n\t/** Stable identity, so focus and legend state survive a data update. */\n\treadonly id?: string;\n\t/** The series' name when the mark has no color channel: legend, tooltip, table. */\n\treadonly label?: string;\n\t/** A field or function whose distinct values split the mark into coloured series. */\n\treadonly color?: ChartAccessor<TDatum, ChartValue>;\n\t/**\n\t * `false` keeps the mark out of focus, tooltips and the data table; a\n\t * function decides per datum (the point where a recorded line hands over\n\t * to its dashed projection belongs to one of the two, not both).\n\t */\n\treadonly tip?: boolean | ((datum: TDatum, index: number) => boolean);\n\treadonly opacity?: number;\n\t/**\n\t * How the mark appears when an update adds it: \"grow\" from its baseline\n\t * or neighbour (the default for bars, areas and slices), \"fade\" (the\n\t * default otherwise), \"none\". A line can also \"draw\": trace itself in on\n\t * the first render.\n\t */\n\treadonly enter?: ChartEnter;\n}\n\nexport function markId(\n\tid: string | undefined,\n\tkind: string,\n\tcontext: ChartMarkContext,\n): string {\n\treturn id ?? `${kind}-${context.markIndex}`;\n}\n\n/** A datum's series: its color value when there is a color channel, the mark otherwise. */\nexport interface SeriesInfo {\n\treadonly key: string;\n\treadonly label: string | undefined;\n\treadonly color: string;\n}\n\nexport function seriesResolver<TDatum>(\n\tcolors: ReadonlyArray<ChartValue | undefined>,\n\toptions: ChartMarkOptions<TDatum> & { readonly fixedColor?: string },\n\tid: string,\n\tcontext: ChartMarkContext,\n): (index: number) => SeriesInfo {\n\tconst fallback =\n\t\toptions.fixedColor ?? context.paletteColor(context.markIndex);\n\tconst single: SeriesInfo = { key: id, label: options.label, color: fallback };\n\tif (options.color === undefined) {\n\t\treturn () => single;\n\t}\n\treturn (index) => {\n\t\tconst value = colors[index];\n\t\tif (value === undefined) {\n\t\t\treturn single;\n\t\t}\n\t\treturn {\n\t\t\tkey: categoryKey(value),\n\t\t\tlabel: context.seriesLabel(value),\n\t\t\tcolor: options.fixedColor ?? context.colorOf(value),\n\t\t};\n\t};\n}\n\nexport function colorChannel<TDatum>(\n\tdata: ReadonlyArray<TDatum>,\n\toptions: ChartMarkOptions<TDatum>,\n): {\n\treadonly values: ReadonlyArray<ChartValue | undefined>;\n\treadonly channel: ChartChannel | undefined;\n} {\n\tconst values = readChannel(data, options.color);\n\treturn {\n\t\tvalues,\n\t\tchannel:\n\t\t\toptions.color === undefined\n\t\t\t\t? undefined\n\t\t\t\t: {\n\t\t\t\t\t\tvalues: values.filter(isPlaceable),\n\t\t\t\t\t\tlabel: channelLabel(options.color),\n\t\t\t\t\t},\n\t};\n}\n\n/** Keeps only the defined paint keys, so a scene snapshot shows what was asked for. */\nexport function paint<TPaint extends ChartPaint>(values: TPaint): TPaint {\n\treturn Object.fromEntries(\n\t\tObject.entries(values).filter(([, value]) => value !== undefined),\n\t) as TPaint;\n}\n\n/** Indices of `data` grouped by series, each group in data order. */\nexport function groupBySeries(\n\tcount: number,\n\tseriesOf: (index: number) => SeriesInfo,\n): ReadonlyMap<\n\tstring,\n\t{ readonly series: SeriesInfo; readonly indices: number[] }\n> {\n\tconst groups = new Map<string, { series: SeriesInfo; indices: number[] }>();\n\tfor (let index = 0; index < count; index += 1) {\n\t\tconst series = seriesOf(index);\n\t\tconst group = groups.get(series.key);\n\t\tif (group === undefined) {\n\t\t\tgroups.set(series.key, { series, indices: [index] });\n\t\t} else {\n\t\t\tgroup.indices.push(index);\n\t\t}\n\t}\n\treturn groups;\n}\n\n/**\n * A raw channel value as the scale can place it, or `undefined` for a gap. A\n * continuous scale reads numeric strings as numbers; a discrete one takes any\n * category as is.\n */\nexport function valueOn(\n\tscale: ChartPositionScale,\n\traw: unknown,\n): ChartValue | undefined {\n\tif (scale.kind === \"band\" || scale.kind === \"point\") {\n\t\treturn isPlaceable(raw) ? raw : undefined;\n\t}\n\tif (raw instanceof Date) {\n\t\treturn Number.isNaN(raw.getTime()) ? undefined : raw;\n\t}\n\treturn numericValue(raw);\n}\n\n/** Whether a datum takes part in focus, tooltips and the data table. */\nexport function tips<TDatum>(\n\ttip: ChartMarkOptions<TDatum>[\"tip\"],\n\tdatum: TDatum,\n\tindex: number,\n): boolean {\n\treturn typeof tip === \"function\" ? tip(datum, index) : tip !== false;\n}\n"],
|
|
5
|
+
"mappings": "AAAA;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,SAAS,mBAAmB;AAmCrB,SAAS,OACf,IACA,MACA,SACS;AACT,SAAO,MAAM,GAAG,IAAI,IAAI,QAAQ,SAAS;AAC1C;AASO,SAAS,eACf,QACA,SACA,IACA,SACgC;AAChC,QAAM,WACL,QAAQ,cAAc,QAAQ,aAAa,QAAQ,SAAS;AAC7D,QAAM,SAAqB,EAAE,KAAK,IAAI,OAAO,QAAQ,OAAO,OAAO,SAAS;AAC5E,MAAI,QAAQ,UAAU,QAAW;AAChC,WAAO,MAAM;AAAA,EACd;AACA,SAAO,CAAC,UAAU;AACjB,UAAM,QAAQ,OAAO,KAAK;AAC1B,QAAI,UAAU,QAAW;AACxB,aAAO;AAAA,IACR;AACA,WAAO;AAAA,MACN,KAAK,YAAY,KAAK;AAAA,MACtB,OAAO,QAAQ,YAAY,KAAK;AAAA,MAChC,OAAO,QAAQ,cAAc,QAAQ,QAAQ,KAAK;AAAA,IACnD;AAAA,EACD;AACD;AAEO,SAAS,aACf,MACA,SAIC;AACD,QAAM,SAAS,YAAY,MAAM,QAAQ,KAAK;AAC9C,SAAO;AAAA,IACN;AAAA,IACA,SACC,QAAQ,UAAU,SACf,SACA;AAAA,MACA,QAAQ,OAAO,OAAO,WAAW;AAAA,MACjC,OAAO,aAAa,QAAQ,KAAK;AAAA,IAClC;AAAA,EACJ;AACD;AAGO,SAAS,MAAiC,QAAwB;AACxE,SAAO,OAAO;AAAA,IACb,OAAO,QAAQ,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,KAAK,MAAM,UAAU,MAAS;AAAA,EACjE;AACD;AAGO,SAAS,cACf,OACA,UAIC;AACD,QAAM,SAAS,oBAAI,IAAuD;AAC1E,WAAS,QAAQ,GAAG,QAAQ,OAAO,SAAS,GAAG;AAC9C,UAAM,SAAS,SAAS,KAAK;AAC7B,UAAM,QAAQ,OAAO,IAAI,OAAO,GAAG;AACnC,QAAI,UAAU,QAAW;AACxB,aAAO,IAAI,OAAO,KAAK,EAAE,QAAQ,SAAS,CAAC,KAAK,EAAE,CAAC;AAAA,IACpD,OAAO;AACN,YAAM,QAAQ,KAAK,KAAK;AAAA,IACzB;AAAA,EACD;AACA,SAAO;AACR;AAOO,SAAS,QACf,OACA,KACyB;AACzB,MAAI,MAAM,SAAS,UAAU,MAAM,SAAS,SAAS;AACpD,WAAO,YAAY,GAAG,IAAI,MAAM;AAAA,EACjC;AACA,MAAI,eAAe,MAAM;AACxB,WAAO,OAAO,MAAM,IAAI,QAAQ,CAAC,IAAI,SAAY;AAAA,EAClD;AACA,SAAO,aAAa,GAAG;AACxB;AAGO,SAAS,KACf,KACA,OACA,OACU;AACV,SAAO,OAAO,QAAQ,aAAa,IAAI,OAAO,KAAK,IAAI,QAAQ;AAChE;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"difference.d.ts","sourceRoot":"","sources":["../../../../src/core/marks/stats/difference.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"difference.d.ts","sourceRoot":"","sources":["../../../../src/core/marks/stats/difference.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACX,aAAa,EACb,SAAS,EAET,UAAU,EAIV,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,iBAAiB,CAAC,MAAM;IACxC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,CAAC,EAAE,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC9C,yEAAyE;IACzE,QAAQ,CAAC,EAAE,EAAE,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC/C,QAAQ,CAAC,EAAE,EAAE,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC/C,0EAA0E;IAC1E,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,gDAAgD;IAChD,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC9B;AAyDD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EACjC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,EAC3B,OAAO,EAAE,iBAAiB,CAAC,MAAM,CAAC,GAChC,SAAS,CAgIX"}
|
|
@@ -5,7 +5,23 @@ import {
|
|
|
5
5
|
readChannel
|
|
6
6
|
} from "../../channel.js";
|
|
7
7
|
import { markId, paint, valueOn } from "../shared.js";
|
|
8
|
-
import {
|
|
8
|
+
import { POINTS_MOTION, pointsPath } from "../../motion/points-motion.js";
|
|
9
|
+
import { areaPath } from "../../paths.js";
|
|
10
|
+
import { categoryKey } from "../../scales/discrete.js";
|
|
11
|
+
function keyedLine(samples, edge) {
|
|
12
|
+
const geometry = {
|
|
13
|
+
kind: "points",
|
|
14
|
+
shape: "line",
|
|
15
|
+
runs: [
|
|
16
|
+
samples.map((sample) => ({
|
|
17
|
+
key: sample.key,
|
|
18
|
+
x: sample.x,
|
|
19
|
+
y: sample[edge]
|
|
20
|
+
}))
|
|
21
|
+
]
|
|
22
|
+
};
|
|
23
|
+
return { d: pointsPath(geometry), geometry, motion: POINTS_MOTION };
|
|
24
|
+
}
|
|
9
25
|
function signRuns(samples) {
|
|
10
26
|
const runs = [];
|
|
11
27
|
for (const [index, sample] of samples.entries()) {
|
|
@@ -67,6 +83,7 @@ function differenceY(data, options) {
|
|
|
67
83
|
const b = second[index];
|
|
68
84
|
if (x === void 0 || a === void 0 || b === void 0) continue;
|
|
69
85
|
const sample = {
|
|
86
|
+
key: categoryKey(x),
|
|
70
87
|
x: xScale.center(x),
|
|
71
88
|
top: yScale.map(a),
|
|
72
89
|
bottom: yScale.map(b)
|
|
@@ -116,9 +133,7 @@ function differenceY(data, options) {
|
|
|
116
133
|
key: `${id}:line:y1`,
|
|
117
134
|
series: `${id}:y1`,
|
|
118
135
|
role: "mark",
|
|
119
|
-
|
|
120
|
-
samples.map((sample) => ({ x: sample.x, y: sample.top }))
|
|
121
|
-
),
|
|
136
|
+
...keyedLine(samples, "top"),
|
|
122
137
|
paint: {
|
|
123
138
|
fill: "none",
|
|
124
139
|
stroke: context.theme.foreground,
|
|
@@ -130,9 +145,7 @@ function differenceY(data, options) {
|
|
|
130
145
|
key: `${id}:line:y2`,
|
|
131
146
|
series: `${id}:y2`,
|
|
132
147
|
role: "mark",
|
|
133
|
-
|
|
134
|
-
samples.map((sample) => ({ x: sample.x, y: sample.bottom }))
|
|
135
|
-
),
|
|
148
|
+
...keyedLine(samples, "bottom"),
|
|
136
149
|
paint: {
|
|
137
150
|
fill: "none",
|
|
138
151
|
stroke: context.theme.muted,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/core/marks/stats/difference.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n\tchannelLabel,\n\tisPlaceable,\n\tnumericValue,\n\treadChannel,\n} from \"#/core/channel.ts\";\nimport { markId, paint, valueOn } from \"#/core/marks/shared.ts\";\nimport { areaPath, type ChartXY
|
|
5
|
-
"mappings": "AAAA;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,SAAS,QAAQ,OAAO,eAAe;AACvC,SAAS,
|
|
4
|
+
"sourcesContent": ["import {\n\tchannelLabel,\n\tisPlaceable,\n\tnumericValue,\n\treadChannel,\n} from \"#/core/channel.ts\";\nimport { markId, paint, valueOn } from \"#/core/marks/shared.ts\";\nimport { POINTS_MOTION, pointsPath } from \"#/core/motion/points-motion.ts\";\nimport { areaPath, type ChartXY } from \"#/core/paths.ts\";\nimport { categoryKey } from \"#/core/scales/discrete.ts\";\nimport type {\n\tChartAccessor,\n\tChartMark,\n\tChartPoint,\n\tChartValue,\n\tGeometryMotion,\n\tSceneGeometry,\n\tSceneNode,\n} from \"#/core/types.ts\";\n\nexport interface DifferenceOptions<TDatum> {\n\treadonly id?: string;\n\treadonly x: ChartAccessor<TDatum, ChartValue>;\n\t/** The series read against the other: the fill says when it is above. */\n\treadonly y1: ChartAccessor<TDatum, ChartValue>;\n\treadonly y2: ChartAccessor<TDatum, ChartValue>;\n\t/** Names of the two series, for the tooltip, the table and the legend. */\n\treadonly labels?: readonly [string, string];\n\t/** Fill where `y1` is above `y2`, and below. */\n\treadonly positiveFill?: string;\n\treadonly negativeFill?: string;\n\treadonly fillOpacity?: number;\n}\n\ninterface Sample {\n\treadonly x: number;\n\treadonly top: number;\n\treadonly bottom: number;\n}\n\n/** One of the two lines, keyed by x so an update slides it like any line. */\nfunction keyedLine(\n\tsamples: readonly (Sample & { readonly key: string })[],\n\tedge: \"top\" | \"bottom\",\n): { d: string; geometry: SceneGeometry; motion: GeometryMotion } {\n\tconst geometry: SceneGeometry = {\n\t\tkind: \"points\",\n\t\tshape: \"line\",\n\t\truns: [\n\t\t\tsamples.map((sample) => ({\n\t\t\t\tkey: sample.key,\n\t\t\t\tx: sample.x,\n\t\t\t\ty: sample[edge],\n\t\t\t})),\n\t\t],\n\t};\n\treturn { d: pointsPath(geometry), geometry, motion: POINTS_MOTION };\n}\n\n/** Splits the band between two lines into runs of one sign, cut where they cross. */\nfunction signRuns(\n\tsamples: ReadonlyArray<Sample>,\n): Array<{ positive: boolean; samples: Sample[] }> {\n\tconst runs: Array<{ positive: boolean; samples: Sample[] }> = [];\n\tfor (const [index, sample] of samples.entries()) {\n\t\t// Pixels: a smaller y is higher, so y1 above y2 means top < bottom.\n\t\tconst positive = sample.top <= sample.bottom;\n\t\tconst current = runs[runs.length - 1];\n\t\tif (current === undefined) {\n\t\t\truns.push({ positive, samples: [sample] });\n\t\t\tcontinue;\n\t\t}\n\t\tif (current.positive === positive) {\n\t\t\tcurrent.samples.push(sample);\n\t\t\tcontinue;\n\t\t}\n\t\tconst previous = samples[index - 1];\n\t\tconst before = previous.top - previous.bottom;\n\t\tconst after = sample.top - sample.bottom;\n\t\tconst share = before / (before - after);\n\t\tconst crossX = previous.x + (sample.x - previous.x) * share;\n\t\tconst crossY = previous.top + (sample.top - previous.top) * share;\n\t\tconst cross = { x: crossX, top: crossY, bottom: crossY };\n\t\tcurrent.samples.push(cross);\n\t\truns.push({ positive, samples: [cross, sample] });\n\t}\n\treturn runs;\n}\n\n/**\n * Two series and the band between them, filled by which one is ahead:\n * revenue against costs, this year against last.\n */\nexport function differenceY<TDatum>(\n\tdata: ReadonlyArray<TDatum>,\n\toptions: DifferenceOptions<TDatum>,\n): ChartMark {\n\tconst xs = readChannel(data, options.x);\n\tconst first = readChannel(data, options.y1).map(numericValue);\n\tconst second = readChannel(data, options.y2).map(numericValue);\n\tconst [firstLabel, secondLabel] = options.labels ?? [\n\t\tchannelLabel(options.y1) ?? \"y1\",\n\t\tchannelLabel(options.y2) ?? \"y2\",\n\t];\n\treturn {\n\t\tid: options.id,\n\t\tcoordinate: \"cartesian\",\n\t\tfocusOrder: \"x\",\n\t\tchannels: {\n\t\t\tx: { values: xs.filter(isPlaceable), label: channelLabel(options.x) },\n\t\t\ty: {\n\t\t\t\tvalues: [...first, ...second].filter(\n\t\t\t\t\t(value): value is number => value !== undefined,\n\t\t\t\t),\n\t\t\t},\n\t\t},\n\t\trender(context) {\n\t\t\tconst id = markId(options.id, \"differenceY\", context);\n\t\t\tconst { x: xScale, y: yScale } = context.scales;\n\t\t\tif (xScale === undefined || yScale === undefined) {\n\t\t\t\treturn { nodes: [] };\n\t\t\t}\n\t\t\tconst positive = options.positiveFill ?? context.paletteColor(1);\n\t\t\tconst negative = options.negativeFill ?? context.paletteColor(0);\n\t\t\tconst samples: Array<Sample & { readonly key: string }> = [];\n\t\t\tconst points: ChartPoint[] = [];\n\t\t\tfor (const [index, raw] of xs.entries()) {\n\t\t\t\tconst x = valueOn(xScale, raw);\n\t\t\t\tconst a = first[index];\n\t\t\t\tconst b = second[index];\n\t\t\t\tif (x === undefined || a === undefined || b === undefined) continue;\n\t\t\t\tconst sample = {\n\t\t\t\t\tkey: categoryKey(x),\n\t\t\t\t\tx: xScale.center(x),\n\t\t\t\t\ttop: yScale.map(a),\n\t\t\t\t\tbottom: yScale.map(b),\n\t\t\t\t};\n\t\t\t\tsamples.push(sample);\n\t\t\t\tfor (const [series, value, pixel, label] of [\n\t\t\t\t\t[\"y1\", a, sample.top, firstLabel],\n\t\t\t\t\t[\"y2\", b, sample.bottom, secondLabel],\n\t\t\t\t] as const) {\n\t\t\t\t\tpoints.push({\n\t\t\t\t\t\tkey: `${id}:${series}:${index}`,\n\t\t\t\t\t\tmarkId: id,\n\t\t\t\t\t\tindex,\n\t\t\t\t\t\tdatum: data[index],\n\t\t\t\t\t\tx: sample.x,\n\t\t\t\t\t\ty: pixel,\n\t\t\t\t\t\txValue: x,\n\t\t\t\t\t\tyValue: value,\n\t\t\t\t\t\tseries: `${id}:${series}`,\n\t\t\t\t\t\tseriesLabel: label,\n\t\t\t\t\t\tcolor:\n\t\t\t\t\t\t\tseries === \"y1\" ? context.theme.foreground : context.theme.muted,\n\t\t\t\t\t\ttitle: context.formatX(x),\n\t\t\t\t\t\tvalue: context.formatY(value),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst nodes: SceneNode[] = signRuns(samples).map((run, index) => ({\n\t\t\t\tkind: \"path\",\n\t\t\t\tkey: `${id}:band:${index}`,\n\t\t\t\trole: \"mark\",\n\t\t\t\td: areaPath(\n\t\t\t\t\trun.samples.map(\n\t\t\t\t\t\t(sample): ChartXY => ({ x: sample.x, y: sample.top }),\n\t\t\t\t\t),\n\t\t\t\t\trun.samples.map(\n\t\t\t\t\t\t(sample): ChartXY => ({ x: sample.x, y: sample.bottom }),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t\tpaint: paint({\n\t\t\t\t\tfill: run.positive ? positive : negative,\n\t\t\t\t\tfillOpacity: options.fillOpacity ?? 0.35,\n\t\t\t\t}),\n\t\t\t}));\n\t\t\tnodes.push(\n\t\t\t\t{\n\t\t\t\t\tkind: \"path\",\n\t\t\t\t\tkey: `${id}:line:y1`,\n\t\t\t\t\tseries: `${id}:y1`,\n\t\t\t\t\trole: \"mark\",\n\t\t\t\t\t...keyedLine(samples, \"top\"),\n\t\t\t\t\tpaint: {\n\t\t\t\t\t\tfill: \"none\",\n\t\t\t\t\t\tstroke: context.theme.foreground,\n\t\t\t\t\t\tstrokeWidth: 1.5,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tkind: \"path\",\n\t\t\t\t\tkey: `${id}:line:y2`,\n\t\t\t\t\tseries: `${id}:y2`,\n\t\t\t\t\trole: \"mark\",\n\t\t\t\t\t...keyedLine(samples, \"bottom\"),\n\t\t\t\t\tpaint: {\n\t\t\t\t\t\tfill: \"none\",\n\t\t\t\t\t\tstroke: context.theme.muted,\n\t\t\t\t\t\tstrokeWidth: 1.5,\n\t\t\t\t\t\tstrokeDasharray: \"4 3\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tnodes,\n\t\t\t\tpoints,\n\t\t\t\tlegend: [\n\t\t\t\t\t{\n\t\t\t\t\t\tkey: `${id}:y1`,\n\t\t\t\t\t\tlabel: firstLabel,\n\t\t\t\t\t\tcolor: context.theme.foreground,\n\t\t\t\t\t\tshape: \"line\",\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tkey: `${id}:y2`,\n\t\t\t\t\t\tlabel: secondLabel,\n\t\t\t\t\t\tcolor: context.theme.muted,\n\t\t\t\t\t\tshape: \"dashed\",\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t};\n\t\t},\n\t};\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { GeometryMotion } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Slices and radial bars: their angles and radii move, and the arc is
|
|
4
|
+
* rebuilt each frame, so its large-arc flag is never wrong. A new slice opens
|
|
5
|
+
* from the boundary where it will sit; a removed one closes onto the slice
|
|
6
|
+
* after it.
|
|
7
|
+
*/
|
|
8
|
+
export declare const ARC_MOTION: GeometryMotion;
|
|
9
|
+
//# sourceMappingURL=arc-motion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arc-motion.d.ts","sourceRoot":"","sources":["../../../src/core/motion/arc-motion.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,cAAc,EAKd,MAAM,iBAAiB,CAAC;AA4EzB;;;;;GAKG;AACH,eAAO,MAAM,UAAU,EAAE,cAuBxB,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { arcPath } from "../paths.js";
|
|
2
|
+
const ARC_FIELDS = [
|
|
3
|
+
"cx",
|
|
4
|
+
"cy",
|
|
5
|
+
"innerRadius",
|
|
6
|
+
"outerRadius",
|
|
7
|
+
"startAngle",
|
|
8
|
+
"endAngle"
|
|
9
|
+
];
|
|
10
|
+
function arcOf(node) {
|
|
11
|
+
return node?.kind === "path" && node.geometry?.kind === "arc" ? node.geometry : void 0;
|
|
12
|
+
}
|
|
13
|
+
function sameRing(a, b) {
|
|
14
|
+
return a.cx === b.cx && a.cy === b.cy && a.innerRadius === b.innerRadius && a.outerRadius === b.outerRadius;
|
|
15
|
+
}
|
|
16
|
+
function withAngle(node, arc, angle) {
|
|
17
|
+
return { ...node, geometry: { ...arc, startAngle: angle, endAngle: angle } };
|
|
18
|
+
}
|
|
19
|
+
function boundary(node, order, known, arc, afterFirst) {
|
|
20
|
+
const index = order.findIndex((other) => other.key === node.key);
|
|
21
|
+
function scan(step) {
|
|
22
|
+
for (let at = index + step; at >= 0 && at < order.length; at += step) {
|
|
23
|
+
const other = arcOf(known.get(order[at].key));
|
|
24
|
+
if (other && sameRing(other, arc)) {
|
|
25
|
+
return step === 1 ? other.startAngle : other.endAngle;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return void 0;
|
|
29
|
+
}
|
|
30
|
+
const [first, second] = afterFirst ? [1, -1] : [-1, 1];
|
|
31
|
+
return scan(first) ?? scan(second) ?? arc.startAngle;
|
|
32
|
+
}
|
|
33
|
+
function arcPlan(from, to) {
|
|
34
|
+
function record(arc) {
|
|
35
|
+
return new Map(
|
|
36
|
+
ARC_FIELDS.map((field) => [field, arc[field]])
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
starts: record(from),
|
|
41
|
+
targets: record(to),
|
|
42
|
+
build(value) {
|
|
43
|
+
const arc = { kind: "arc" };
|
|
44
|
+
for (const field of ARC_FIELDS) arc[field] = value(field);
|
|
45
|
+
return arc;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const ARC_MOTION = {
|
|
50
|
+
plan: (from, to) => from.kind === "arc" && to.kind === "arc" ? arcPlan(from, to) : void 0,
|
|
51
|
+
draw: (geometry) => geometry.kind === "arc" ? arcPath({
|
|
52
|
+
...geometry,
|
|
53
|
+
endAngle: Math.max(geometry.startAngle, geometry.endAngle)
|
|
54
|
+
}) : "",
|
|
55
|
+
enter(node, siblings, before) {
|
|
56
|
+
const arc = arcOf(node);
|
|
57
|
+
return node.enter === "grow" && arc ? withAngle(node, arc, boundary(node, siblings, before, arc, false)) : void 0;
|
|
58
|
+
},
|
|
59
|
+
exit(node, siblings, after) {
|
|
60
|
+
const arc = arcOf(node);
|
|
61
|
+
return node.enter === "grow" && arc ? withAngle(node, arc, boundary(node, siblings, after, arc, true)) : void 0;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
export {
|
|
65
|
+
ARC_MOTION
|
|
66
|
+
};
|
|
67
|
+
//# sourceMappingURL=arc-motion.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/core/motion/arc-motion.ts"],
|
|
4
|
+
"sourcesContent": ["import { arcPath } from \"#/core/paths.ts\";\nimport type {\n\tGeometryMotion,\n\tGeometryPlan,\n\tSceneGeometry,\n\tSceneNode,\n\tScenePath,\n} from \"#/core/types.ts\";\n\ntype Arc = Extract<SceneGeometry, { kind: \"arc\" }>;\n\nconst ARC_FIELDS = [\n\t\"cx\",\n\t\"cy\",\n\t\"innerRadius\",\n\t\"outerRadius\",\n\t\"startAngle\",\n\t\"endAngle\",\n] as const;\n\nfunction arcOf(node: SceneNode | undefined): Arc | undefined {\n\treturn node?.kind === \"path\" && node.geometry?.kind === \"arc\"\n\t\t? node.geometry\n\t\t: undefined;\n}\n\n/** Slices of one ring tile it: those are the neighbours a slice opens from and closes onto. */\nfunction sameRing(a: Arc, b: Arc): boolean {\n\treturn (\n\t\ta.cx === b.cx &&\n\t\ta.cy === b.cy &&\n\t\ta.innerRadius === b.innerRadius &&\n\t\ta.outerRadius === b.outerRadius\n\t);\n}\n\nfunction withAngle(node: ScenePath, arc: Arc, angle: number): ScenePath {\n\treturn { ...node, geometry: { ...arc, startAngle: angle, endAngle: angle } };\n}\n\n/**\n * The boundary angle next to `node` among the slices of its ring in `order`,\n * read from `known`: the end of the nearest one before it, or the start of the\n * nearest one after it, whichever side `afterFirst` looks at first.\n */\nfunction boundary(\n\tnode: SceneNode,\n\torder: readonly SceneNode[],\n\tknown: ReadonlyMap<string, SceneNode>,\n\tarc: Arc,\n\tafterFirst: boolean,\n): number {\n\tconst index = order.findIndex((other) => other.key === node.key);\n\tfunction scan(step: -1 | 1): number | undefined {\n\t\tfor (let at = index + step; at >= 0 && at < order.length; at += step) {\n\t\t\tconst other = arcOf(known.get((order[at] as SceneNode).key));\n\t\t\tif (other && sameRing(other, arc)) {\n\t\t\t\treturn step === 1 ? other.startAngle : other.endAngle;\n\t\t\t}\n\t\t}\n\t\treturn undefined;\n\t}\n\tconst [first, second] = afterFirst ? ([1, -1] as const) : ([-1, 1] as const);\n\treturn scan(first) ?? scan(second) ?? arc.startAngle;\n}\n\nfunction arcPlan(from: Arc, to: Arc): GeometryPlan {\n\tfunction record(arc: Arc) {\n\t\treturn new Map<string, number>(\n\t\t\tARC_FIELDS.map((field) => [field, arc[field]]),\n\t\t);\n\t}\n\treturn {\n\t\tstarts: record(from),\n\t\ttargets: record(to),\n\t\tbuild(value) {\n\t\t\tconst arc = { kind: \"arc\" } as Record<string, unknown>;\n\t\t\tfor (const field of ARC_FIELDS) arc[field] = value(field);\n\t\t\treturn arc as Arc;\n\t\t},\n\t};\n}\n\n/**\n * Slices and radial bars: their angles and radii move, and the arc is\n * rebuilt each frame, so its large-arc flag is never wrong. A new slice opens\n * from the boundary where it will sit; a removed one closes onto the slice\n * after it.\n */\nexport const ARC_MOTION: GeometryMotion = {\n\tplan: (from, to) =>\n\t\tfrom.kind === \"arc\" && to.kind === \"arc\" ? arcPlan(from, to) : undefined,\n\tdraw: (geometry) =>\n\t\tgeometry.kind === \"arc\"\n\t\t\t? arcPath({\n\t\t\t\t\t...geometry,\n\t\t\t\t\tendAngle: Math.max(geometry.startAngle, geometry.endAngle),\n\t\t\t\t})\n\t\t\t: \"\",\n\tenter(node, siblings, before) {\n\t\tconst arc = arcOf(node);\n\t\treturn node.enter === \"grow\" && arc\n\t\t\t? withAngle(node, arc, boundary(node, siblings, before, arc, false))\n\t\t\t: undefined;\n\t},\n\texit(node, siblings, after) {\n\t\tconst arc = arcOf(node);\n\t\t// A slice closes onto the one after it, so it takes that one's new start.\n\t\treturn node.enter === \"grow\" && arc\n\t\t\t? withAngle(node, arc, boundary(node, siblings, after, arc, true))\n\t\t\t: undefined;\n\t},\n};\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,eAAe;AAWxB,MAAM,aAAa;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD;AAEA,SAAS,MAAM,MAA8C;AAC5D,SAAO,MAAM,SAAS,UAAU,KAAK,UAAU,SAAS,QACrD,KAAK,WACL;AACJ;AAGA,SAAS,SAAS,GAAQ,GAAiB;AAC1C,SACC,EAAE,OAAO,EAAE,MACX,EAAE,OAAO,EAAE,MACX,EAAE,gBAAgB,EAAE,eACpB,EAAE,gBAAgB,EAAE;AAEtB;AAEA,SAAS,UAAU,MAAiB,KAAU,OAA0B;AACvE,SAAO,EAAE,GAAG,MAAM,UAAU,EAAE,GAAG,KAAK,YAAY,OAAO,UAAU,MAAM,EAAE;AAC5E;AAOA,SAAS,SACR,MACA,OACA,OACA,KACA,YACS;AACT,QAAM,QAAQ,MAAM,UAAU,CAAC,UAAU,MAAM,QAAQ,KAAK,GAAG;AAC/D,WAAS,KAAK,MAAkC;AAC/C,aAAS,KAAK,QAAQ,MAAM,MAAM,KAAK,KAAK,MAAM,QAAQ,MAAM,MAAM;AACrE,YAAM,QAAQ,MAAM,MAAM,IAAK,MAAM,EAAE,EAAgB,GAAG,CAAC;AAC3D,UAAI,SAAS,SAAS,OAAO,GAAG,GAAG;AAClC,eAAO,SAAS,IAAI,MAAM,aAAa,MAAM;AAAA,MAC9C;AAAA,IACD;AACA,WAAO;AAAA,EACR;AACA,QAAM,CAAC,OAAO,MAAM,IAAI,aAAc,CAAC,GAAG,EAAE,IAAe,CAAC,IAAI,CAAC;AACjE,SAAO,KAAK,KAAK,KAAK,KAAK,MAAM,KAAK,IAAI;AAC3C;AAEA,SAAS,QAAQ,MAAW,IAAuB;AAClD,WAAS,OAAO,KAAU;AACzB,WAAO,IAAI;AAAA,MACV,WAAW,IAAI,CAAC,UAAU,CAAC,OAAO,IAAI,KAAK,CAAC,CAAC;AAAA,IAC9C;AAAA,EACD;AACA,SAAO;AAAA,IACN,QAAQ,OAAO,IAAI;AAAA,IACnB,SAAS,OAAO,EAAE;AAAA,IAClB,MAAM,OAAO;AACZ,YAAM,MAAM,EAAE,MAAM,MAAM;AAC1B,iBAAW,SAAS,WAAY,KAAI,KAAK,IAAI,MAAM,KAAK;AACxD,aAAO;AAAA,IACR;AAAA,EACD;AACD;AAQO,MAAM,aAA6B;AAAA,EACzC,MAAM,CAAC,MAAM,OACZ,KAAK,SAAS,SAAS,GAAG,SAAS,QAAQ,QAAQ,MAAM,EAAE,IAAI;AAAA,EAChE,MAAM,CAAC,aACN,SAAS,SAAS,QACf,QAAQ;AAAA,IACR,GAAG;AAAA,IACH,UAAU,KAAK,IAAI,SAAS,YAAY,SAAS,QAAQ;AAAA,EAC1D,CAAC,IACA;AAAA,EACJ,MAAM,MAAM,UAAU,QAAQ;AAC7B,UAAM,MAAM,MAAM,IAAI;AACtB,WAAO,KAAK,UAAU,UAAU,MAC7B,UAAU,MAAM,KAAK,SAAS,MAAM,UAAU,QAAQ,KAAK,KAAK,CAAC,IACjE;AAAA,EACJ;AAAA,EACA,KAAK,MAAM,UAAU,OAAO;AAC3B,UAAM,MAAM,MAAM,IAAI;AAEtB,WAAO,KAAK,UAAU,UAAU,MAC7B,UAAU,MAAM,KAAK,SAAS,MAAM,UAAU,OAAO,KAAK,IAAI,CAAC,IAC/D;AAAA,EACJ;AACD;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { SceneNode } from "../types.js";
|
|
2
|
+
/** Where a node an update adds starts from. `siblings` is the list it sits in, `before` the old scene. */
|
|
3
|
+
export declare function enterFrom(node: SceneNode, siblings: readonly SceneNode[], before: ReadonlyMap<string, SceneNode>): SceneNode | undefined;
|
|
4
|
+
/** Where a node an update removes collapses to. `siblings` is the old list it sat in, `after` the new scene. */
|
|
5
|
+
export declare function exitTo(node: SceneNode, siblings: readonly SceneNode[], after: ReadonlyMap<string, SceneNode>): SceneNode | undefined;
|
|
6
|
+
//# sourceMappingURL=enter-exit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enter-exit.d.ts","sourceRoot":"","sources":["../../../src/core/motion/enter-exit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAa,MAAM,iBAAiB,CAAC;AAmB5D,0GAA0G;AAC1G,wBAAgB,SAAS,CACxB,IAAI,EAAE,SAAS,EACf,QAAQ,EAAE,SAAS,SAAS,EAAE,EAC9B,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,GACpC,SAAS,GAAG,SAAS,CAMvB;AAED,gHAAgH;AAChH,wBAAgB,MAAM,CACrB,IAAI,EAAE,SAAS,EACf,QAAQ,EAAE,SAAS,SAAS,EAAE,EAC9B,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,GACnC,SAAS,GAAG,SAAS,CAMvB"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
function collapsedBar(node) {
|
|
2
|
+
const { baseline } = node;
|
|
3
|
+
if (!baseline) return void 0;
|
|
4
|
+
return baseline.axis === "y" ? { ...node, y: baseline.at, height: 0 } : { ...node, x: baseline.at, width: 0 };
|
|
5
|
+
}
|
|
6
|
+
function enterFrom(node, siblings, before) {
|
|
7
|
+
if (node.enter !== "grow") return void 0;
|
|
8
|
+
if (node.kind === "rect") return collapsedBar(node);
|
|
9
|
+
return node.kind === "path" ? node.motion?.enter?.(node, siblings, before) : void 0;
|
|
10
|
+
}
|
|
11
|
+
function exitTo(node, siblings, after) {
|
|
12
|
+
if (node.enter !== "grow") return void 0;
|
|
13
|
+
if (node.kind === "rect") return collapsedBar(node);
|
|
14
|
+
return node.kind === "path" ? node.motion?.exit?.(node, siblings, after) : void 0;
|
|
15
|
+
}
|
|
16
|
+
export {
|
|
17
|
+
enterFrom,
|
|
18
|
+
exitTo
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=enter-exit.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/core/motion/enter-exit.ts"],
|
|
4
|
+
"sourcesContent": ["import type { SceneNode, SceneRect } from \"#/core/types.ts\";\n\n/**\n * The shape a data mark grows from when an update adds it, and collapses to\n * when an update removes it. Always a node of the same kind, so the store\n * springs it like any other change. Undefined when the mark has no such\n * shape: it fades in, and is gone at once when it leaves. A bar has its\n * baseline; a path asks the motion its mark brought (a slice opens from its\n * neighbour, an area grows from its lower edge).\n */\n\nfunction collapsedBar(node: SceneRect): SceneRect | undefined {\n\tconst { baseline } = node;\n\tif (!baseline) return undefined;\n\treturn baseline.axis === \"y\"\n\t\t? { ...node, y: baseline.at, height: 0 }\n\t\t: { ...node, x: baseline.at, width: 0 };\n}\n\n/** Where a node an update adds starts from. `siblings` is the list it sits in, `before` the old scene. */\nexport function enterFrom(\n\tnode: SceneNode,\n\tsiblings: readonly SceneNode[],\n\tbefore: ReadonlyMap<string, SceneNode>,\n): SceneNode | undefined {\n\tif (node.enter !== \"grow\") return undefined;\n\tif (node.kind === \"rect\") return collapsedBar(node);\n\treturn node.kind === \"path\"\n\t\t? node.motion?.enter?.(node, siblings, before)\n\t\t: undefined;\n}\n\n/** Where a node an update removes collapses to. `siblings` is the old list it sat in, `after` the new scene. */\nexport function exitTo(\n\tnode: SceneNode,\n\tsiblings: readonly SceneNode[],\n\tafter: ReadonlyMap<string, SceneNode>,\n): SceneNode | undefined {\n\tif (node.enter !== \"grow\") return undefined;\n\tif (node.kind === \"rect\") return collapsedBar(node);\n\treturn node.kind === \"path\"\n\t\t? node.motion?.exit?.(node, siblings, after)\n\t\t: undefined;\n}\n"],
|
|
5
|
+
"mappings": "AAWA,SAAS,aAAa,MAAwC;AAC7D,QAAM,EAAE,SAAS,IAAI;AACrB,MAAI,CAAC,SAAU,QAAO;AACtB,SAAO,SAAS,SAAS,MACtB,EAAE,GAAG,MAAM,GAAG,SAAS,IAAI,QAAQ,EAAE,IACrC,EAAE,GAAG,MAAM,GAAG,SAAS,IAAI,OAAO,EAAE;AACxC;AAGO,SAAS,UACf,MACA,UACA,QACwB;AACxB,MAAI,KAAK,UAAU,OAAQ,QAAO;AAClC,MAAI,KAAK,SAAS,OAAQ,QAAO,aAAa,IAAI;AAClD,SAAO,KAAK,SAAS,SAClB,KAAK,QAAQ,QAAQ,MAAM,UAAU,MAAM,IAC3C;AACJ;AAGO,SAAS,OACf,MACA,UACA,OACwB;AACxB,MAAI,KAAK,UAAU,OAAQ,QAAO;AAClC,MAAI,KAAK,SAAS,OAAQ,QAAO,aAAa,IAAI;AAClD,SAAO,KAAK,SAAS,SAClB,KAAK,QAAQ,OAAO,MAAM,UAAU,KAAK,IACzC;AACJ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { SceneNode } from "../types.js";
|
|
2
|
+
export interface LeavingGuide {
|
|
3
|
+
readonly node: SceneNode;
|
|
4
|
+
/** The group it sat in, or null at the root. */
|
|
5
|
+
readonly parent: string | null;
|
|
6
|
+
readonly index: number;
|
|
7
|
+
}
|
|
8
|
+
/** The guides of `nodes` whose keys `stays` no longer has. */
|
|
9
|
+
export declare function leavingGuides(nodes: readonly SceneNode[], stays: (key: string) => boolean, parent?: string | null, into?: LeavingGuide[]): LeavingGuide[];
|
|
10
|
+
/** `children` with the leaving guides of `parent` back at their places, faded to `opacity`. */
|
|
11
|
+
export declare function withLeaving(children: readonly SceneNode[], parent: string | null, leaving: ReadonlyArray<readonly [LeavingGuide, number]>): SceneNode[];
|
|
12
|
+
//# sourceMappingURL=leaving.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"leaving.d.ts","sourceRoot":"","sources":["../../../src/core/motion/leaving.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AASjD,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,gDAAgD;IAChD,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACvB;AAED,8DAA8D;AAC9D,wBAAgB,aAAa,CAC5B,KAAK,EAAE,SAAS,SAAS,EAAE,EAC3B,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,EAC/B,MAAM,GAAE,MAAM,GAAG,IAAW,EAC5B,IAAI,GAAE,YAAY,EAAO,GACvB,YAAY,EAAE,CAWhB;AAED,+FAA+F;AAC/F,wBAAgB,WAAW,CAC1B,QAAQ,EAAE,SAAS,SAAS,EAAE,EAC9B,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,GACrD,SAAS,EAAE,CAcb"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const GUIDE_ROLES = /* @__PURE__ */ new Set(["grid", "axis"]);
|
|
2
|
+
function leavingGuides(nodes, stays, parent = null, into = []) {
|
|
3
|
+
for (const [index, node] of nodes.entries()) {
|
|
4
|
+
if (node.kind === "group") {
|
|
5
|
+
leavingGuides(node.children, stays, node.key, into);
|
|
6
|
+
continue;
|
|
7
|
+
}
|
|
8
|
+
if (!stays(node.key) && GUIDE_ROLES.has(node.role ?? "")) {
|
|
9
|
+
into.push({ node, parent, index });
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return into;
|
|
13
|
+
}
|
|
14
|
+
function withLeaving(children, parent, leaving) {
|
|
15
|
+
const placed = [...children];
|
|
16
|
+
for (const [guide, opacity] of leaving) {
|
|
17
|
+
if (guide.parent !== parent || guide.node.kind === "group") continue;
|
|
18
|
+
const faded = {
|
|
19
|
+
...guide.node,
|
|
20
|
+
paint: {
|
|
21
|
+
...guide.node.paint,
|
|
22
|
+
opacity: (guide.node.paint.opacity ?? 1) * opacity
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
placed.splice(Math.min(guide.index, placed.length), 0, faded);
|
|
26
|
+
}
|
|
27
|
+
return placed;
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
leavingGuides,
|
|
31
|
+
withLeaving
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=leaving.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/core/motion/leaving.ts"],
|
|
4
|
+
"sourcesContent": ["import type { SceneNode } from \"#/core/types.ts\";\n\n/**\n * Guides that left the scene still fade out where they stood: a tick label\n * or a grid line vanishing at once reads as a glitch. Data marks never\n * linger \u2014 a bar the data no longer has would show a value that isn't true.\n */\nconst GUIDE_ROLES = new Set([\"grid\", \"axis\"]);\n\nexport interface LeavingGuide {\n\treadonly node: SceneNode;\n\t/** The group it sat in, or null at the root. */\n\treadonly parent: string | null;\n\treadonly index: number;\n}\n\n/** The guides of `nodes` whose keys `stays` no longer has. */\nexport function leavingGuides(\n\tnodes: readonly SceneNode[],\n\tstays: (key: string) => boolean,\n\tparent: string | null = null,\n\tinto: LeavingGuide[] = [],\n): LeavingGuide[] {\n\tfor (const [index, node] of nodes.entries()) {\n\t\tif (node.kind === \"group\") {\n\t\t\tleavingGuides(node.children, stays, node.key, into);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!stays(node.key) && GUIDE_ROLES.has(node.role ?? \"\")) {\n\t\t\tinto.push({ node, parent, index });\n\t\t}\n\t}\n\treturn into;\n}\n\n/** `children` with the leaving guides of `parent` back at their places, faded to `opacity`. */\nexport function withLeaving(\n\tchildren: readonly SceneNode[],\n\tparent: string | null,\n\tleaving: ReadonlyArray<readonly [LeavingGuide, number]>,\n): SceneNode[] {\n\tconst placed = [...children];\n\tfor (const [guide, opacity] of leaving) {\n\t\tif (guide.parent !== parent || guide.node.kind === \"group\") continue;\n\t\tconst faded = {\n\t\t\t...guide.node,\n\t\t\tpaint: {\n\t\t\t\t...guide.node.paint,\n\t\t\t\topacity: (guide.node.paint.opacity ?? 1) * opacity,\n\t\t\t},\n\t\t} as SceneNode;\n\t\tplaced.splice(Math.min(guide.index, placed.length), 0, faded);\n\t}\n\treturn placed;\n}\n"],
|
|
5
|
+
"mappings": "AAOA,MAAM,cAAc,oBAAI,IAAI,CAAC,QAAQ,MAAM,CAAC;AAUrC,SAAS,cACf,OACA,OACA,SAAwB,MACxB,OAAuB,CAAC,GACP;AACjB,aAAW,CAAC,OAAO,IAAI,KAAK,MAAM,QAAQ,GAAG;AAC5C,QAAI,KAAK,SAAS,SAAS;AAC1B,oBAAc,KAAK,UAAU,OAAO,KAAK,KAAK,IAAI;AAClD;AAAA,IACD;AACA,QAAI,CAAC,MAAM,KAAK,GAAG,KAAK,YAAY,IAAI,KAAK,QAAQ,EAAE,GAAG;AACzD,WAAK,KAAK,EAAE,MAAM,QAAQ,MAAM,CAAC;AAAA,IAClC;AAAA,EACD;AACA,SAAO;AACR;AAGO,SAAS,YACf,UACA,QACA,SACc;AACd,QAAM,SAAS,CAAC,GAAG,QAAQ;AAC3B,aAAW,CAAC,OAAO,OAAO,KAAK,SAAS;AACvC,QAAI,MAAM,WAAW,UAAU,MAAM,KAAK,SAAS,QAAS;AAC5D,UAAM,QAAQ;AAAA,MACb,GAAG,MAAM;AAAA,MACT,OAAO;AAAA,QACN,GAAG,MAAM,KAAK;AAAA,QACd,UAAU,MAAM,KAAK,MAAM,WAAW,KAAK;AAAA,MAC5C;AAAA,IACD;AACA,WAAO,OAAO,KAAK,IAAI,MAAM,OAAO,OAAO,MAAM,GAAG,GAAG,KAAK;AAAA,EAC7D;AACA,SAAO;AACR;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -6,10 +6,13 @@ import type { ChartScene } from "../types.js";
|
|
|
6
6
|
* the speed it has, so an update landing mid-flight bends the motion instead
|
|
7
7
|
* of kinking it. Pure and clocked from outside (milliseconds), so the SVG and
|
|
8
8
|
* Canvas renderers move identically and tests step it by hand. Hit-testing
|
|
9
|
-
* and focus never read it: they read the scene it moves towards
|
|
9
|
+
* and focus never read it: they read the scene it moves towards, which never
|
|
10
|
+
* holds the marks collapsing or the guides fading on their way out.
|
|
10
11
|
*/
|
|
11
12
|
export interface MotionStore {
|
|
12
13
|
retarget(scene: ChartScene, now: number): void;
|
|
14
|
+
/** The first render: lines that `enter: "draw"` trace themselves in. False when none does. */
|
|
15
|
+
introduce(now: number): boolean;
|
|
13
16
|
/** Jumps to `scene`: a resize, or reduced motion. */
|
|
14
17
|
snap(scene: ChartScene): void;
|
|
15
18
|
frame(now: number): ChartScene;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"motion-store.d.ts","sourceRoot":"","sources":["../../../src/core/motion/motion-store.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"motion-store.d.ts","sourceRoot":"","sources":["../../../src/core/motion/motion-store.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,KAAK,WAAW,EAAgB,MAAM,yBAAyB,CAAC;AACzE,OAAO,KAAK,EACX,UAAU,EAIV,MAAM,iBAAiB,CAAC;AAEzB;;;;;;;;GAQG;AACH,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/C,8FAA8F;IAC9F,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAChC,qDAAqD;IACrD,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAAC;IAC/B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAC9B;AA4ED,wBAAgB,iBAAiB,CAChC,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,WAAW,GACjB,WAAW,CA0Ub"}
|