@voila.dev/chart 4.0.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/README.md +85 -0
- package/dist/brush/brush-layer.d.ts +16 -0
- package/dist/brush/brush-layer.d.ts.map +1 -0
- package/dist/brush/brush-layer.js +137 -0
- package/dist/brush/brush-layer.js.map +7 -0
- package/dist/brush/index.d.ts +10 -0
- package/dist/brush/index.d.ts.map +1 -0
- package/dist/brush/index.js +8 -0
- package/dist/brush/index.js.map +7 -0
- package/dist/canvas/canvas-renderer.d.ts +11 -0
- package/dist/canvas/canvas-renderer.d.ts.map +1 -0
- package/dist/canvas/canvas-renderer.js +49 -0
- package/dist/canvas/canvas-renderer.js.map +7 -0
- package/dist/canvas/index.d.ts +2 -0
- package/dist/canvas/index.d.ts.map +1 -0
- package/dist/canvas/index.js +5 -0
- package/dist/canvas/index.js.map +7 -0
- package/dist/canvas/paint-scene.d.ts +4 -0
- package/dist/canvas/paint-scene.d.ts.map +1 -0
- package/dist/canvas/paint-scene.js +130 -0
- package/dist/canvas/paint-scene.js.map +7 -0
- package/dist/canvas/resolve-color.d.ts +18 -0
- package/dist/canvas/resolve-color.d.ts.map +1 -0
- package/dist/canvas/resolve-color.js +52 -0
- package/dist/canvas/resolve-color.js.map +7 -0
- package/dist/contour/density.d.ts +20 -0
- package/dist/contour/density.d.ts.map +1 -0
- package/dist/contour/density.js +59 -0
- package/dist/contour/density.js.map +7 -0
- package/dist/contour/index.d.ts +2 -0
- package/dist/contour/index.d.ts.map +1 -0
- package/dist/contour/index.js +5 -0
- package/dist/contour/index.js.map +7 -0
- package/dist/core/channel.d.ts +11 -0
- package/dist/core/channel.d.ts.map +1 -0
- package/dist/core/channel.js +44 -0
- package/dist/core/channel.js.map +7 -0
- package/dist/core/compile.d.ts +22 -0
- package/dist/core/compile.d.ts.map +1 -0
- package/dist/core/compile.js +295 -0
- package/dist/core/compile.js.map +7 -0
- package/dist/core/define-chart.d.ts +96 -0
- package/dist/core/define-chart.d.ts.map +1 -0
- package/dist/core/define-chart.js +7 -0
- package/dist/core/define-chart.js.map +7 -0
- package/dist/core/facet.d.ts +7 -0
- package/dist/core/facet.d.ts.map +1 -0
- package/dist/core/facet.js +134 -0
- package/dist/core/facet.js.map +7 -0
- package/dist/core/format.d.ts +37 -0
- package/dist/core/format.d.ts.map +1 -0
- package/dist/core/format.js +108 -0
- package/dist/core/format.js.map +7 -0
- package/dist/core/guides/axes.d.ts +25 -0
- package/dist/core/guides/axes.d.ts.map +1 -0
- package/dist/core/guides/axes.js +204 -0
- package/dist/core/guides/axes.js.map +7 -0
- package/dist/core/marks/area.d.ts +23 -0
- package/dist/core/marks/area.d.ts.map +1 -0
- package/dist/core/marks/area.js +187 -0
- package/dist/core/marks/area.js.map +7 -0
- package/dist/core/marks/bar.d.ts +26 -0
- package/dist/core/marks/bar.d.ts.map +1 -0
- package/dist/core/marks/bar.js +200 -0
- package/dist/core/marks/bar.js.map +7 -0
- package/dist/core/marks/cell.d.ts +16 -0
- package/dist/core/marks/cell.d.ts.map +1 -0
- package/dist/core/marks/cell.js +98 -0
- package/dist/core/marks/cell.js.map +7 -0
- package/dist/core/marks/dot.d.ts +17 -0
- package/dist/core/marks/dot.d.ts.map +1 -0
- package/dist/core/marks/dot.js +125 -0
- package/dist/core/marks/dot.js.map +7 -0
- package/dist/core/marks/funnel-layout.d.ts +37 -0
- package/dist/core/marks/funnel-layout.d.ts.map +1 -0
- package/dist/core/marks/funnel-layout.js +61 -0
- package/dist/core/marks/funnel-layout.js.map +7 -0
- package/dist/core/marks/funnel.d.ts +19 -0
- package/dist/core/marks/funnel.d.ts.map +1 -0
- package/dist/core/marks/funnel.js +143 -0
- package/dist/core/marks/funnel.js.map +7 -0
- package/dist/core/marks/line.d.ts +17 -0
- package/dist/core/marks/line.d.ts.map +1 -0
- package/dist/core/marks/line.js +150 -0
- package/dist/core/marks/line.js.map +7 -0
- package/dist/core/marks/polar.d.ts +61 -0
- package/dist/core/marks/polar.d.ts.map +1 -0
- package/dist/core/marks/polar.js +436 -0
- package/dist/core/marks/polar.js.map +7 -0
- package/dist/core/marks/rect.d.ts +22 -0
- package/dist/core/marks/rect.d.ts.map +1 -0
- package/dist/core/marks/rect.js +119 -0
- package/dist/core/marks/rect.js.map +7 -0
- package/dist/core/marks/rule.d.ts +17 -0
- package/dist/core/marks/rule.d.ts.map +1 -0
- package/dist/core/marks/rule.js +73 -0
- package/dist/core/marks/rule.js.map +7 -0
- package/dist/core/marks/shared.d.ts +41 -0
- package/dist/core/marks/shared.d.ts.map +1 -0
- package/dist/core/marks/shared.js +74 -0
- package/dist/core/marks/shared.js.map +7 -0
- package/dist/core/marks/stack.d.ts +13 -0
- package/dist/core/marks/stack.d.ts.map +1 -0
- package/dist/core/marks/stack.js +28 -0
- package/dist/core/marks/stack.js.map +7 -0
- package/dist/core/marks/stats/difference.d.ts +20 -0
- package/dist/core/marks/stats/difference.d.ts.map +1 -0
- package/dist/core/marks/stats/difference.js +168 -0
- package/dist/core/marks/stats/difference.js.map +7 -0
- package/dist/core/marks/stats/distribution.d.ts +41 -0
- package/dist/core/marks/stats/distribution.d.ts.map +1 -0
- package/dist/core/marks/stats/distribution.js +354 -0
- package/dist/core/marks/stats/distribution.js.map +7 -0
- package/dist/core/marks/stats/dodge.d.ts +16 -0
- package/dist/core/marks/stats/dodge.d.ts.map +1 -0
- package/dist/core/marks/stats/dodge.js +104 -0
- package/dist/core/marks/stats/dodge.js.map +7 -0
- package/dist/core/marks/stats/hexbin.d.ts +26 -0
- package/dist/core/marks/stats/hexbin.d.ts.map +1 -0
- package/dist/core/marks/stats/hexbin.js +113 -0
- package/dist/core/marks/stats/hexbin.js.map +7 -0
- package/dist/core/marks/stats/histogram.d.ts +27 -0
- package/dist/core/marks/stats/histogram.d.ts.map +1 -0
- package/dist/core/marks/stats/histogram.js +70 -0
- package/dist/core/marks/stats/histogram.js.map +7 -0
- package/dist/core/marks/stats/regression.d.ts +13 -0
- package/dist/core/marks/stats/regression.d.ts.map +1 -0
- package/dist/core/marks/stats/regression.js +80 -0
- package/dist/core/marks/stats/regression.js.map +7 -0
- package/dist/core/marks/stats/statistics.d.ts +34 -0
- package/dist/core/marks/stats/statistics.d.ts.map +1 -0
- package/dist/core/marks/stats/statistics.js +111 -0
- package/dist/core/marks/stats/statistics.js.map +7 -0
- package/dist/core/marks/stats/waffle.d.ts +18 -0
- package/dist/core/marks/stats/waffle.d.ts.map +1 -0
- package/dist/core/marks/stats/waffle.js +117 -0
- package/dist/core/marks/stats/waffle.js.map +7 -0
- package/dist/core/marks/text.d.ts +19 -0
- package/dist/core/marks/text.d.ts.map +1 -0
- package/dist/core/marks/text.js +60 -0
- package/dist/core/marks/text.js.map +7 -0
- package/dist/core/motion/tween.d.ts +6 -0
- package/dist/core/motion/tween.d.ts.map +1 -0
- package/dist/core/motion/tween.js +98 -0
- package/dist/core/motion/tween.js.map +7 -0
- package/dist/core/nearest.d.ts +25 -0
- package/dist/core/nearest.d.ts.map +1 -0
- package/dist/core/nearest.js +151 -0
- package/dist/core/nearest.js.map +7 -0
- package/dist/core/paths.d.ts +65 -0
- package/dist/core/paths.d.ts.map +1 -0
- package/dist/core/paths.js +192 -0
- package/dist/core/paths.js.map +7 -0
- package/dist/core/scales/color.d.ts +11 -0
- package/dist/core/scales/color.d.ts.map +1 -0
- package/dist/core/scales/color.js +35 -0
- package/dist/core/scales/color.js.map +7 -0
- package/dist/core/scales/continuous.d.ts +18 -0
- package/dist/core/scales/continuous.d.ts.map +1 -0
- package/dist/core/scales/continuous.js +82 -0
- package/dist/core/scales/continuous.js.map +7 -0
- package/dist/core/scales/discrete.d.ts +22 -0
- package/dist/core/scales/discrete.d.ts.map +1 -0
- package/dist/core/scales/discrete.js +103 -0
- package/dist/core/scales/discrete.js.map +7 -0
- package/dist/core/scales/resolve.d.ts +18 -0
- package/dist/core/scales/resolve.d.ts.map +1 -0
- package/dist/core/scales/resolve.js +112 -0
- package/dist/core/scales/resolve.js.map +7 -0
- package/dist/core/scales/time.d.ts +4 -0
- package/dist/core/scales/time.d.ts.map +1 -0
- package/dist/core/scales/time.js +140 -0
- package/dist/core/scales/time.js.map +7 -0
- package/dist/core/text.d.ts +3 -0
- package/dist/core/text.d.ts.map +1 -0
- package/dist/core/text.js +29 -0
- package/dist/core/text.js.map +7 -0
- package/dist/core/theme.d.ts +8 -0
- package/dist/core/theme.d.ts.map +1 -0
- package/dist/core/theme.js +21 -0
- package/dist/core/theme.js.map +7 -0
- package/dist/core/ticks.d.ts +27 -0
- package/dist/core/ticks.d.ts.map +1 -0
- package/dist/core/ticks.js +76 -0
- package/dist/core/ticks.js.map +7 -0
- package/dist/core/types.d.ts +305 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +1 -0
- package/dist/core/types.js.map +7 -0
- package/dist/force/force.d.ts +30 -0
- package/dist/force/force.d.ts.map +1 -0
- package/dist/force/force.js +143 -0
- package/dist/force/force.js.map +7 -0
- package/dist/force/index.d.ts +2 -0
- package/dist/force/index.d.ts.map +1 -0
- package/dist/force/index.js +7 -0
- package/dist/force/index.js.map +7 -0
- package/dist/geo/geo.d.ts +59 -0
- package/dist/geo/geo.d.ts.map +1 -0
- package/dist/geo/geo.js +231 -0
- package/dist/geo/geo.js.map +7 -0
- package/dist/geo/index.d.ts +2 -0
- package/dist/geo/index.d.ts.map +1 -0
- package/dist/geo/index.js +13 -0
- package/dist/geo/index.js.map +7 -0
- package/dist/hierarchy/hierarchy.d.ts +24 -0
- package/dist/hierarchy/hierarchy.d.ts.map +1 -0
- package/dist/hierarchy/hierarchy.js +279 -0
- package/dist/hierarchy/hierarchy.js.map +7 -0
- package/dist/hierarchy/index.d.ts +2 -0
- package/dist/hierarchy/index.d.ts.map +1 -0
- package/dist/hierarchy/index.js +11 -0
- package/dist/hierarchy/index.js.map +7 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +96 -0
- package/dist/index.js.map +7 -0
- package/dist/react/brush-contract.d.ts +23 -0
- package/dist/react/brush-contract.d.ts.map +1 -0
- package/dist/react/brush-contract.js +7 -0
- package/dist/react/brush-contract.js.map +7 -0
- package/dist/react/chart-data-table.d.ts +18 -0
- package/dist/react/chart-data-table.d.ts.map +1 -0
- package/dist/react/chart-data-table.js +55 -0
- package/dist/react/chart-data-table.js.map +7 -0
- package/dist/react/chart-legend.d.ts +17 -0
- package/dist/react/chart-legend.d.ts.map +1 -0
- package/dist/react/chart-legend.js +126 -0
- package/dist/react/chart-legend.js.map +7 -0
- package/dist/react/chart-tooltip.d.ts +18 -0
- package/dist/react/chart-tooltip.d.ts.map +1 -0
- package/dist/react/chart-tooltip.js +95 -0
- package/dist/react/chart-tooltip.js.map +7 -0
- package/dist/react/chart.d.ts +45 -0
- package/dist/react/chart.d.ts.map +1 -0
- package/dist/react/chart.js +383 -0
- package/dist/react/chart.js.map +7 -0
- package/dist/react/focus-overlay.d.ts +14 -0
- package/dist/react/focus-overlay.d.ts.map +1 -0
- package/dist/react/focus-overlay.js +147 -0
- package/dist/react/focus-overlay.js.map +7 -0
- package/dist/react/index.d.ts +7 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +11 -0
- package/dist/react/index.js.map +7 -0
- package/dist/react/messages.d.ts +16 -0
- package/dist/react/messages.d.ts.map +1 -0
- package/dist/react/messages.js +33 -0
- package/dist/react/messages.js.map +7 -0
- package/dist/react/renderer.d.ts +14 -0
- package/dist/react/renderer.d.ts.map +1 -0
- package/dist/react/renderer.js +1 -0
- package/dist/react/renderer.js.map +7 -0
- package/dist/react/sr-only.d.ts +4 -0
- package/dist/react/sr-only.d.ts.map +1 -0
- package/dist/react/sr-only.js +15 -0
- package/dist/react/sr-only.js.map +7 -0
- package/dist/react/svg-renderer.d.ts +4 -0
- package/dist/react/svg-renderer.d.ts.map +1 -0
- package/dist/react/svg-renderer.js +20 -0
- package/dist/react/svg-renderer.js.map +7 -0
- package/dist/react/svg-scene.d.ts +8 -0
- package/dist/react/svg-scene.d.ts.map +1 -0
- package/dist/react/svg-scene.js +121 -0
- package/dist/react/svg-scene.js.map +7 -0
- package/dist/react/use-animated-scene.d.ts +10 -0
- package/dist/react/use-animated-scene.d.ts.map +1 -0
- package/dist/react/use-animated-scene.js +41 -0
- package/dist/react/use-animated-scene.js.map +7 -0
- package/dist/react/use-chart-focus.d.ts +29 -0
- package/dist/react/use-chart-focus.d.ts.map +1 -0
- package/dist/react/use-chart-focus.js +180 -0
- package/dist/react/use-chart-focus.js.map +7 -0
- package/dist/react/use-chart-width.d.ts +12 -0
- package/dist/react/use-chart-width.d.ts.map +1 -0
- package/dist/react/use-chart-width.js +23 -0
- package/dist/react/use-chart-width.js.map +7 -0
- package/dist/react/use-chart-zoom.d.ts +13 -0
- package/dist/react/use-chart-zoom.d.ts.map +1 -0
- package/dist/react/use-chart-zoom.js +52 -0
- package/dist/react/use-chart-zoom.js.map +7 -0
- package/dist/react/use-text-measurer.d.ts +8 -0
- package/dist/react/use-text-measurer.d.ts.map +1 -0
- package/dist/react/use-text-measurer.js +44 -0
- package/dist/react/use-text-measurer.js.map +7 -0
- package/dist/sankey/index.d.ts +2 -0
- package/dist/sankey/index.d.ts.map +1 -0
- package/dist/sankey/index.js +7 -0
- package/dist/sankey/index.js.map +7 -0
- package/dist/sankey/sankey.d.ts +29 -0
- package/dist/sankey/sankey.d.ts.map +1 -0
- package/dist/sankey/sankey.js +127 -0
- package/dist/sankey/sankey.js.map +7 -0
- package/dist/voronoi/index.d.ts +2 -0
- package/dist/voronoi/index.d.ts.map +1 -0
- package/dist/voronoi/index.js +5 -0
- package/dist/voronoi/index.js.map +7 -0
- package/dist/voronoi/voronoi.d.ts +16 -0
- package/dist/voronoi/voronoi.d.ts.map +1 -0
- package/dist/voronoi/voronoi.js +62 -0
- package/dist/voronoi/voronoi.js.map +7 -0
- package/package.json +74 -0
- package/src/brush/brush-layer.tsx +174 -0
- package/src/brush/index.ts +14 -0
- package/src/canvas/canvas-renderer.tsx +56 -0
- package/src/canvas/index.ts +1 -0
- package/src/canvas/paint-scene.ts +152 -0
- package/src/canvas/resolve-color.ts +65 -0
- package/src/contour/density.ts +90 -0
- package/src/contour/index.ts +1 -0
- package/src/core/channel.ts +53 -0
- package/src/core/compile.ts +436 -0
- package/src/core/define-chart.ts +128 -0
- package/src/core/facet.ts +189 -0
- package/src/core/format.ts +163 -0
- package/src/core/guides/axes.ts +283 -0
- package/src/core/marks/area.ts +238 -0
- package/src/core/marks/bar.ts +278 -0
- package/src/core/marks/cell.ts +123 -0
- package/src/core/marks/dot.ts +157 -0
- package/src/core/marks/funnel-layout.ts +113 -0
- package/src/core/marks/funnel.ts +172 -0
- package/src/core/marks/line.ts +206 -0
- package/src/core/marks/polar.ts +568 -0
- package/src/core/marks/rect.ts +156 -0
- package/src/core/marks/rule.ts +104 -0
- package/src/core/marks/shared.ts +134 -0
- package/src/core/marks/stack.ts +42 -0
- package/src/core/marks/stats/difference.ts +205 -0
- package/src/core/marks/stats/distribution.ts +425 -0
- package/src/core/marks/stats/dodge.ts +136 -0
- package/src/core/marks/stats/hexbin.ts +152 -0
- package/src/core/marks/stats/histogram.ts +108 -0
- package/src/core/marks/stats/regression.ts +108 -0
- package/src/core/marks/stats/statistics.ts +153 -0
- package/src/core/marks/stats/waffle.ts +141 -0
- package/src/core/marks/text.ts +97 -0
- package/src/core/motion/tween.ts +131 -0
- package/src/core/nearest.ts +223 -0
- package/src/core/paths.ts +323 -0
- package/src/core/scales/color.ts +51 -0
- package/src/core/scales/continuous.ts +111 -0
- package/src/core/scales/discrete.ts +156 -0
- package/src/core/scales/resolve.ts +158 -0
- package/src/core/scales/time.ts +180 -0
- package/src/core/text.ts +42 -0
- package/src/core/theme.ts +26 -0
- package/src/core/ticks.ts +118 -0
- package/src/core/types.ts +358 -0
- package/src/force/force.ts +200 -0
- package/src/force/index.ts +6 -0
- package/src/geo/geo.ts +346 -0
- package/src/geo/index.ts +10 -0
- package/src/hierarchy/hierarchy.ts +377 -0
- package/src/hierarchy/index.ts +6 -0
- package/src/index.ts +70 -0
- package/src/react/brush-contract.ts +35 -0
- package/src/react/chart-data-table.tsx +98 -0
- package/src/react/chart-legend.tsx +130 -0
- package/src/react/chart-tooltip.tsx +111 -0
- package/src/react/chart.tsx +489 -0
- package/src/react/focus-overlay.tsx +164 -0
- package/src/react/index.ts +9 -0
- package/src/react/messages.ts +49 -0
- package/src/react/renderer.ts +15 -0
- package/src/react/sr-only.ts +14 -0
- package/src/react/svg-renderer.tsx +18 -0
- package/src/react/svg-scene.tsx +146 -0
- package/src/react/use-animated-scene.ts +57 -0
- package/src/react/use-chart-focus.ts +245 -0
- package/src/react/use-chart-width.ts +27 -0
- package/src/react/use-chart-zoom.ts +67 -0
- package/src/react/use-text-measurer.ts +49 -0
- package/src/sankey/index.ts +6 -0
- package/src/sankey/sankey.ts +179 -0
- package/src/voronoi/index.ts +1 -0
- package/src/voronoi/voronoi.ts +84 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import {
|
|
2
|
+
channelLabel,
|
|
3
|
+
isPlaceable,
|
|
4
|
+
numericValue,
|
|
5
|
+
readChannel
|
|
6
|
+
} from "../../channel.js";
|
|
7
|
+
import {
|
|
8
|
+
colorChannel,
|
|
9
|
+
markId,
|
|
10
|
+
paint,
|
|
11
|
+
seriesResolver
|
|
12
|
+
} from "../shared.js";
|
|
13
|
+
import { categoryKey } from "../../scales/discrete.js";
|
|
14
|
+
const DEFAULT_COLUMNS = 10;
|
|
15
|
+
const CELL_GAP = 1.5;
|
|
16
|
+
function waffleY(data, options) {
|
|
17
|
+
const xs = readChannel(data, options.x);
|
|
18
|
+
const values = readChannel(data, options.y).map(numericValue);
|
|
19
|
+
const colors = colorChannel(data, options);
|
|
20
|
+
const unit = options.unit ?? 1;
|
|
21
|
+
const columns = options.columns ?? DEFAULT_COLUMNS;
|
|
22
|
+
const totals = /* @__PURE__ */ new Map();
|
|
23
|
+
const offsets = xs.map((x, index) => {
|
|
24
|
+
if (!isPlaceable(x)) return 0;
|
|
25
|
+
const key = categoryKey(x);
|
|
26
|
+
const before = totals.get(key) ?? 0;
|
|
27
|
+
totals.set(
|
|
28
|
+
key,
|
|
29
|
+
before + Math.max(0, Math.round((values[index] ?? 0) / unit))
|
|
30
|
+
);
|
|
31
|
+
return before;
|
|
32
|
+
});
|
|
33
|
+
const tallest = Math.max(0, ...totals.values());
|
|
34
|
+
return {
|
|
35
|
+
id: options.id,
|
|
36
|
+
coordinate: "cartesian",
|
|
37
|
+
focusOrder: "x",
|
|
38
|
+
channels: {
|
|
39
|
+
x: {
|
|
40
|
+
values: xs.filter(isPlaceable),
|
|
41
|
+
discrete: "band",
|
|
42
|
+
label: channelLabel(options.x)
|
|
43
|
+
},
|
|
44
|
+
y: {
|
|
45
|
+
values: [0, Math.ceil(tallest / columns) * columns * unit],
|
|
46
|
+
includeZero: true,
|
|
47
|
+
label: channelLabel(options.y) ?? options.label
|
|
48
|
+
},
|
|
49
|
+
color: colors.channel
|
|
50
|
+
},
|
|
51
|
+
render(context) {
|
|
52
|
+
const id = markId(options.id, "waffleY", context);
|
|
53
|
+
const { x: xScale, y: yScale } = context.scales;
|
|
54
|
+
if (xScale === void 0 || yScale === void 0) {
|
|
55
|
+
return { nodes: [] };
|
|
56
|
+
}
|
|
57
|
+
const seriesOf = seriesResolver(
|
|
58
|
+
colors.values,
|
|
59
|
+
{ ...options, fixedColor: options.fill },
|
|
60
|
+
id,
|
|
61
|
+
context
|
|
62
|
+
);
|
|
63
|
+
const cellWidth = xScale.bandwidth / columns;
|
|
64
|
+
const rowHeight = Math.abs(yScale.map(columns * unit) - yScale.map(0));
|
|
65
|
+
const nodes = [];
|
|
66
|
+
const points = [];
|
|
67
|
+
for (const [index, x] of xs.entries()) {
|
|
68
|
+
const value = values[index];
|
|
69
|
+
if (!isPlaceable(x) || value === void 0) continue;
|
|
70
|
+
const series = seriesOf(index);
|
|
71
|
+
const cells = Math.max(0, Math.round(value / unit));
|
|
72
|
+
let top = Number.POSITIVE_INFINITY;
|
|
73
|
+
for (let cell = 0; cell < cells; cell += 1) {
|
|
74
|
+
const position = offsets[index] + cell;
|
|
75
|
+
const column = position % columns;
|
|
76
|
+
const row = Math.floor(position / columns);
|
|
77
|
+
const y = yScale.map((row + 1) * columns * unit);
|
|
78
|
+
top = Math.min(top, y);
|
|
79
|
+
nodes.push({
|
|
80
|
+
kind: "rect",
|
|
81
|
+
key: `${id}:${index}:${cell}`,
|
|
82
|
+
series: series.key,
|
|
83
|
+
role: "mark",
|
|
84
|
+
x: xScale.map(x) + column * cellWidth + CELL_GAP / 2,
|
|
85
|
+
y: y + CELL_GAP / 2,
|
|
86
|
+
width: Math.max(0, cellWidth - CELL_GAP),
|
|
87
|
+
height: Math.max(0, rowHeight - CELL_GAP),
|
|
88
|
+
corners: [1.5, 1.5, 1.5, 1.5],
|
|
89
|
+
paint: paint({ fill: series.color, opacity: options.opacity })
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
if (options.tip !== false) {
|
|
93
|
+
points.push({
|
|
94
|
+
key: `${id}:${index}`,
|
|
95
|
+
markId: id,
|
|
96
|
+
index,
|
|
97
|
+
datum: data[index],
|
|
98
|
+
x: xScale.center(x),
|
|
99
|
+
y: Number.isFinite(top) ? top : yScale.map(0),
|
|
100
|
+
xValue: x,
|
|
101
|
+
yValue: value,
|
|
102
|
+
series: series.key,
|
|
103
|
+
seriesLabel: series.label,
|
|
104
|
+
color: series.color,
|
|
105
|
+
title: context.formatX(x),
|
|
106
|
+
value: context.formatY(value)
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return { nodes, points };
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
export {
|
|
115
|
+
waffleY
|
|
116
|
+
};
|
|
117
|
+
//# sourceMappingURL=waffle.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/core/marks/stats/waffle.ts"],
|
|
4
|
+
"sourcesContent": ["import {\n\tchannelLabel,\n\tisPlaceable,\n\tnumericValue,\n\treadChannel,\n} from \"#/core/channel.ts\";\nimport {\n\ttype ChartMarkOptions,\n\tcolorChannel,\n\tmarkId,\n\tpaint,\n\tseriesResolver,\n} from \"#/core/marks/shared.ts\";\nimport { categoryKey } from \"#/core/scales/discrete.ts\";\nimport type {\n\tChartAccessor,\n\tChartMark,\n\tChartPoint,\n\tChartValue,\n\tSceneNode,\n} from \"#/core/types.ts\";\n\nexport interface WaffleOptions<TDatum> extends ChartMarkOptions<TDatum> {\n\treadonly x: ChartAccessor<TDatum, ChartValue>;\n\treadonly y: ChartAccessor<TDatum, ChartValue>;\n\t/** What one cell stands for. */\n\treadonly unit?: number;\n\t/** Cells across a band. */\n\treadonly columns?: number;\n\treadonly fill?: string;\n}\n\nconst DEFAULT_COLUMNS = 10;\nconst CELL_GAP = 1.5;\n\n/**\n * Bars made of countable cells: \"12 missions\" reads as twelve squares. A\n * row of `columns` cells is `columns \u00D7 unit` on the y axis, so the waffle\n * still reads against it; series continue one another inside a column.\n */\nexport function waffleY<TDatum>(\n\tdata: ReadonlyArray<TDatum>,\n\toptions: WaffleOptions<TDatum>,\n): ChartMark {\n\tconst xs = readChannel(data, options.x);\n\tconst values = readChannel(data, options.y).map(numericValue);\n\tconst colors = colorChannel(data, options);\n\tconst unit = options.unit ?? 1;\n\tconst columns = options.columns ?? DEFAULT_COLUMNS;\n\tconst totals = new Map<string, number>();\n\tconst offsets = xs.map((x, index) => {\n\t\tif (!isPlaceable(x)) return 0;\n\t\tconst key = categoryKey(x);\n\t\tconst before = totals.get(key) ?? 0;\n\t\ttotals.set(\n\t\t\tkey,\n\t\t\tbefore + Math.max(0, Math.round((values[index] ?? 0) / unit)),\n\t\t);\n\t\treturn before;\n\t});\n\tconst tallest = Math.max(0, ...totals.values());\n\treturn {\n\t\tid: options.id,\n\t\tcoordinate: \"cartesian\",\n\t\tfocusOrder: \"x\",\n\t\tchannels: {\n\t\t\tx: {\n\t\t\t\tvalues: xs.filter(isPlaceable),\n\t\t\t\tdiscrete: \"band\",\n\t\t\t\tlabel: channelLabel(options.x),\n\t\t\t},\n\t\t\ty: {\n\t\t\t\tvalues: [0, Math.ceil(tallest / columns) * columns * unit],\n\t\t\t\tincludeZero: true,\n\t\t\t\tlabel: channelLabel(options.y) ?? options.label,\n\t\t\t},\n\t\t\tcolor: colors.channel,\n\t\t},\n\t\trender(context) {\n\t\t\tconst id = markId(options.id, \"waffleY\", 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 seriesOf = seriesResolver(\n\t\t\t\tcolors.values,\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 cellWidth = xScale.bandwidth / columns;\n\t\t\tconst rowHeight = Math.abs(yScale.map(columns * unit) - yScale.map(0));\n\t\t\tconst nodes: SceneNode[] = [];\n\t\t\tconst points: ChartPoint[] = [];\n\t\t\tfor (const [index, x] of xs.entries()) {\n\t\t\t\tconst value = values[index];\n\t\t\t\tif (!isPlaceable(x) || value === undefined) continue;\n\t\t\t\tconst series = seriesOf(index);\n\t\t\t\tconst cells = Math.max(0, Math.round(value / unit));\n\t\t\t\tlet top = Number.POSITIVE_INFINITY;\n\t\t\t\tfor (let cell = 0; cell < cells; cell += 1) {\n\t\t\t\t\tconst position = offsets[index] + cell;\n\t\t\t\t\tconst column = position % columns;\n\t\t\t\t\tconst row = Math.floor(position / columns);\n\t\t\t\t\tconst y = yScale.map((row + 1) * columns * unit);\n\t\t\t\t\ttop = Math.min(top, y);\n\t\t\t\t\tnodes.push({\n\t\t\t\t\t\tkind: \"rect\",\n\t\t\t\t\t\tkey: `${id}:${index}:${cell}`,\n\t\t\t\t\t\tseries: series.key,\n\t\t\t\t\t\trole: \"mark\",\n\t\t\t\t\t\tx: xScale.map(x) + column * cellWidth + CELL_GAP / 2,\n\t\t\t\t\t\ty: y + CELL_GAP / 2,\n\t\t\t\t\t\twidth: Math.max(0, cellWidth - CELL_GAP),\n\t\t\t\t\t\theight: Math.max(0, rowHeight - CELL_GAP),\n\t\t\t\t\t\tcorners: [1.5, 1.5, 1.5, 1.5],\n\t\t\t\t\t\tpaint: paint({ fill: series.color, opacity: options.opacity }),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tif (options.tip !== false) {\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: xScale.center(x),\n\t\t\t\t\t\ty: Number.isFinite(top) ? top : yScale.map(0),\n\t\t\t\t\t\txValue: x,\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(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\treturn { nodes, points };\n\t\t},\n\t};\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ChartAccessor, ChartMark, ChartTextPaint, ChartValue } from "../types.js";
|
|
2
|
+
export interface TextOptions<TDatum> {
|
|
3
|
+
readonly id?: string;
|
|
4
|
+
readonly x?: ChartAccessor<TDatum, ChartValue>;
|
|
5
|
+
readonly y?: ChartAccessor<TDatum, ChartValue>;
|
|
6
|
+
readonly text: ChartAccessor<TDatum, unknown>;
|
|
7
|
+
/** Pixel offsets from the anchor. */
|
|
8
|
+
readonly dx?: number;
|
|
9
|
+
readonly dy?: number;
|
|
10
|
+
readonly fill?: string;
|
|
11
|
+
readonly fontSize?: number;
|
|
12
|
+
readonly fontWeight?: number;
|
|
13
|
+
readonly textAnchor?: ChartTextPaint["textAnchor"];
|
|
14
|
+
readonly baseline?: ChartTextPaint["baseline"];
|
|
15
|
+
readonly opacity?: number;
|
|
16
|
+
}
|
|
17
|
+
/** A label per datum: values on bars, names on points, annotations. */
|
|
18
|
+
export declare function text<TDatum>(data: ReadonlyArray<TDatum>, options: TextOptions<TDatum>): ChartMark;
|
|
19
|
+
//# sourceMappingURL=text.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../../src/core/marks/text.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACX,aAAa,EACb,SAAS,EACT,cAAc,EACd,UAAU,EAEV,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,WAAW,CAAC,MAAM;IAClC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,CAAC,CAAC,EAAE,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC/C,QAAQ,CAAC,CAAC,CAAC,EAAE,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC/C,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9C,qCAAqC;IACrC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,CAAC,EAAE,cAAc,CAAC,YAAY,CAAC,CAAC;IACnD,QAAQ,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;IAC/C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,uEAAuE;AACvE,wBAAgB,IAAI,CAAC,MAAM,EAC1B,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,EAC3B,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,GAC1B,SAAS,CAkEX"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { channelLabel, isPlaceable, readChannel } from "../channel.js";
|
|
2
|
+
import { markId, paint, valueOn } from "./shared.js";
|
|
3
|
+
function text(data, options) {
|
|
4
|
+
const xs = readChannel(data, options.x);
|
|
5
|
+
const ys = readChannel(data, options.y);
|
|
6
|
+
const texts = readChannel(data, options.text);
|
|
7
|
+
return {
|
|
8
|
+
id: options.id,
|
|
9
|
+
coordinate: "cartesian",
|
|
10
|
+
channels: {
|
|
11
|
+
...options.x === void 0 ? {} : {
|
|
12
|
+
x: {
|
|
13
|
+
values: xs.filter(isPlaceable),
|
|
14
|
+
label: channelLabel(options.x)
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
...options.y === void 0 ? {} : {
|
|
18
|
+
y: {
|
|
19
|
+
values: ys.filter(isPlaceable),
|
|
20
|
+
label: channelLabel(options.y)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
render(context) {
|
|
25
|
+
const id = markId(options.id, "text", context);
|
|
26
|
+
const { scales, plot, theme } = context;
|
|
27
|
+
const nodes = [];
|
|
28
|
+
for (const [index, content] of texts.entries()) {
|
|
29
|
+
const xValue = scales.x && valueOn(scales.x, xs[index]);
|
|
30
|
+
const yValue = scales.y && valueOn(scales.y, ys[index]);
|
|
31
|
+
const x = scales.x && xValue !== void 0 ? scales.x.center(xValue) : plot.x + plot.width / 2;
|
|
32
|
+
const y = scales.y && yValue !== void 0 ? scales.y.center(yValue) : plot.y + plot.height / 2;
|
|
33
|
+
if (content === void 0 || content === null || content === "") {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
nodes.push({
|
|
37
|
+
kind: "text",
|
|
38
|
+
key: `${id}:${index}`,
|
|
39
|
+
role: "label",
|
|
40
|
+
x: x + (options.dx ?? 0),
|
|
41
|
+
y: y + (options.dy ?? 0),
|
|
42
|
+
text: typeof content === "number" ? context.formatY(content) : String(content),
|
|
43
|
+
paint: paint({
|
|
44
|
+
fill: options.fill ?? theme.foreground,
|
|
45
|
+
opacity: options.opacity,
|
|
46
|
+
fontSize: options.fontSize ?? theme.fontSize,
|
|
47
|
+
fontWeight: options.fontWeight,
|
|
48
|
+
textAnchor: options.textAnchor ?? "middle",
|
|
49
|
+
baseline: options.baseline ?? "middle"
|
|
50
|
+
})
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return { nodes };
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export {
|
|
58
|
+
text
|
|
59
|
+
};
|
|
60
|
+
//# sourceMappingURL=text.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/core/marks/text.ts"],
|
|
4
|
+
"sourcesContent": ["import { channelLabel, isPlaceable, readChannel } from \"#/core/channel.ts\";\nimport { markId, paint, valueOn } from \"#/core/marks/shared.ts\";\nimport type {\n\tChartAccessor,\n\tChartMark,\n\tChartTextPaint,\n\tChartValue,\n\tSceneNode,\n} from \"#/core/types.ts\";\n\nexport interface TextOptions<TDatum> {\n\treadonly id?: string;\n\treadonly x?: ChartAccessor<TDatum, ChartValue>;\n\treadonly y?: ChartAccessor<TDatum, ChartValue>;\n\treadonly text: ChartAccessor<TDatum, unknown>;\n\t/** Pixel offsets from the anchor. */\n\treadonly dx?: number;\n\treadonly dy?: number;\n\treadonly fill?: string;\n\treadonly fontSize?: number;\n\treadonly fontWeight?: number;\n\treadonly textAnchor?: ChartTextPaint[\"textAnchor\"];\n\treadonly baseline?: ChartTextPaint[\"baseline\"];\n\treadonly opacity?: number;\n}\n\n/** A label per datum: values on bars, names on points, annotations. */\nexport function text<TDatum>(\n\tdata: ReadonlyArray<TDatum>,\n\toptions: TextOptions<TDatum>,\n): ChartMark {\n\tconst xs = readChannel(data, options.x);\n\tconst ys = readChannel(data, options.y);\n\tconst texts = readChannel(data, options.text);\n\treturn {\n\t\tid: options.id,\n\t\tcoordinate: \"cartesian\",\n\t\tchannels: {\n\t\t\t...(options.x === undefined\n\t\t\t\t? {}\n\t\t\t\t: {\n\t\t\t\t\t\tx: {\n\t\t\t\t\t\t\tvalues: xs.filter(isPlaceable),\n\t\t\t\t\t\t\tlabel: channelLabel(options.x),\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t...(options.y === undefined\n\t\t\t\t? {}\n\t\t\t\t: {\n\t\t\t\t\t\ty: {\n\t\t\t\t\t\t\tvalues: ys.filter(isPlaceable),\n\t\t\t\t\t\t\tlabel: channelLabel(options.y),\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t},\n\t\trender(context) {\n\t\t\tconst id = markId(options.id, \"text\", context);\n\t\t\tconst { scales, plot, theme } = context;\n\t\t\tconst nodes: SceneNode[] = [];\n\t\t\tfor (const [index, content] of texts.entries()) {\n\t\t\t\tconst xValue = scales.x && valueOn(scales.x, xs[index]);\n\t\t\t\tconst yValue = scales.y && valueOn(scales.y, ys[index]);\n\t\t\t\tconst x =\n\t\t\t\t\tscales.x && xValue !== undefined\n\t\t\t\t\t\t? scales.x.center(xValue)\n\t\t\t\t\t\t: plot.x + plot.width / 2;\n\t\t\t\tconst y =\n\t\t\t\t\tscales.y && yValue !== undefined\n\t\t\t\t\t\t? scales.y.center(yValue)\n\t\t\t\t\t\t: plot.y + plot.height / 2;\n\t\t\t\tif (content === undefined || content === null || content === \"\") {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tnodes.push({\n\t\t\t\t\tkind: \"text\",\n\t\t\t\t\tkey: `${id}:${index}`,\n\t\t\t\t\trole: \"label\",\n\t\t\t\t\tx: x + (options.dx ?? 0),\n\t\t\t\t\ty: y + (options.dy ?? 0),\n\t\t\t\t\ttext:\n\t\t\t\t\t\ttypeof content === \"number\"\n\t\t\t\t\t\t\t? context.formatY(content)\n\t\t\t\t\t\t\t: String(content),\n\t\t\t\t\tpaint: paint({\n\t\t\t\t\t\tfill: options.fill ?? theme.foreground,\n\t\t\t\t\t\topacity: options.opacity,\n\t\t\t\t\t\tfontSize: options.fontSize ?? theme.fontSize,\n\t\t\t\t\t\tfontWeight: options.fontWeight,\n\t\t\t\t\t\ttextAnchor: options.textAnchor ?? \"middle\",\n\t\t\t\t\t\tbaseline: options.baseline ?? \"middle\",\n\t\t\t\t\t}),\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn { nodes };\n\t\t},\n\t};\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,cAAc,aAAa,mBAAmB;AACvD,SAAS,QAAQ,OAAO,eAAe;AA0BhC,SAAS,KACf,MACA,SACY;AACZ,QAAM,KAAK,YAAY,MAAM,QAAQ,CAAC;AACtC,QAAM,KAAK,YAAY,MAAM,QAAQ,CAAC;AACtC,QAAM,QAAQ,YAAY,MAAM,QAAQ,IAAI;AAC5C,SAAO;AAAA,IACN,IAAI,QAAQ;AAAA,IACZ,YAAY;AAAA,IACZ,UAAU;AAAA,MACT,GAAI,QAAQ,MAAM,SACf,CAAC,IACD;AAAA,QACA,GAAG;AAAA,UACF,QAAQ,GAAG,OAAO,WAAW;AAAA,UAC7B,OAAO,aAAa,QAAQ,CAAC;AAAA,QAC9B;AAAA,MACD;AAAA,MACF,GAAI,QAAQ,MAAM,SACf,CAAC,IACD;AAAA,QACA,GAAG;AAAA,UACF,QAAQ,GAAG,OAAO,WAAW;AAAA,UAC7B,OAAO,aAAa,QAAQ,CAAC;AAAA,QAC9B;AAAA,MACD;AAAA,IACH;AAAA,IACA,OAAO,SAAS;AACf,YAAM,KAAK,OAAO,QAAQ,IAAI,QAAQ,OAAO;AAC7C,YAAM,EAAE,QAAQ,MAAM,MAAM,IAAI;AAChC,YAAM,QAAqB,CAAC;AAC5B,iBAAW,CAAC,OAAO,OAAO,KAAK,MAAM,QAAQ,GAAG;AAC/C,cAAM,SAAS,OAAO,KAAK,QAAQ,OAAO,GAAG,GAAG,KAAK,CAAC;AACtD,cAAM,SAAS,OAAO,KAAK,QAAQ,OAAO,GAAG,GAAG,KAAK,CAAC;AACtD,cAAM,IACL,OAAO,KAAK,WAAW,SACpB,OAAO,EAAE,OAAO,MAAM,IACtB,KAAK,IAAI,KAAK,QAAQ;AAC1B,cAAM,IACL,OAAO,KAAK,WAAW,SACpB,OAAO,EAAE,OAAO,MAAM,IACtB,KAAK,IAAI,KAAK,SAAS;AAC3B,YAAI,YAAY,UAAa,YAAY,QAAQ,YAAY,IAAI;AAChE;AAAA,QACD;AACA,cAAM,KAAK;AAAA,UACV,MAAM;AAAA,UACN,KAAK,GAAG,EAAE,IAAI,KAAK;AAAA,UACnB,MAAM;AAAA,UACN,GAAG,KAAK,QAAQ,MAAM;AAAA,UACtB,GAAG,KAAK,QAAQ,MAAM;AAAA,UACtB,MACC,OAAO,YAAY,WAChB,QAAQ,QAAQ,OAAO,IACvB,OAAO,OAAO;AAAA,UAClB,OAAO,MAAM;AAAA,YACZ,MAAM,QAAQ,QAAQ,MAAM;AAAA,YAC5B,SAAS,QAAQ;AAAA,YACjB,UAAU,QAAQ,YAAY,MAAM;AAAA,YACpC,YAAY,QAAQ;AAAA,YACpB,YAAY,QAAQ,cAAc;AAAA,YAClC,UAAU,QAAQ,YAAY;AAAA,UAC/B,CAAC;AAAA,QACF,CAAC;AAAA,MACF;AACA,aAAO,EAAE,MAAM;AAAA,IAChB;AAAA,EACD;AACD;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ChartScene } from "../types.js";
|
|
2
|
+
/** `to`, `t` of the way from `from` (0 is `from`, 1 is `to`). */
|
|
3
|
+
export declare function tweenScene(from: ChartScene, to: ChartScene, t: number): ChartScene;
|
|
4
|
+
/** Fast out, slow in: the motion settles rather than stops. */
|
|
5
|
+
export declare function easeOutCubic(t: number): number;
|
|
6
|
+
//# sourceMappingURL=tween.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tween.d.ts","sourceRoot":"","sources":["../../../src/core/motion/tween.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,UAAU,EAAa,MAAM,iBAAiB,CAAC;AA+GzE,iEAAiE;AACjE,wBAAgB,UAAU,CACzB,IAAI,EAAE,UAAU,EAChB,EAAE,EAAE,UAAU,EACd,CAAC,EAAE,MAAM,GACP,UAAU,CASZ;AAED,+DAA+D;AAC/D,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE9C"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
const NUMBER = /-?\d*\.?\d+(?:e[-+]?\d+)?/gi;
|
|
2
|
+
function mix(from, to, t) {
|
|
3
|
+
return from + (to - from) * t;
|
|
4
|
+
}
|
|
5
|
+
function tweenPath(from, to, t) {
|
|
6
|
+
const fromNumbers = from.match(NUMBER);
|
|
7
|
+
const toNumbers = to.match(NUMBER);
|
|
8
|
+
if (fromNumbers === null || toNumbers === null || fromNumbers.length !== toNumbers.length || from.replace(NUMBER, "#") !== to.replace(NUMBER, "#")) {
|
|
9
|
+
return t < 1 ? from : to;
|
|
10
|
+
}
|
|
11
|
+
let index = 0;
|
|
12
|
+
return to.replace(NUMBER, () => {
|
|
13
|
+
const value = mix(Number(fromNumbers[index]), Number(toNumbers[index]), t);
|
|
14
|
+
index += 1;
|
|
15
|
+
return String(Math.round(value * 100) / 100);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
function fadeIn(paint, t) {
|
|
19
|
+
return { ...paint, opacity: (paint.opacity ?? 1) * t };
|
|
20
|
+
}
|
|
21
|
+
function tweenNode(from, to, t) {
|
|
22
|
+
if (from === void 0 || from.kind !== to.kind) {
|
|
23
|
+
return to.kind === "group" ? {
|
|
24
|
+
...to,
|
|
25
|
+
children: to.children.map((child) => tweenNode(void 0, child, t))
|
|
26
|
+
} : { ...to, paint: fadeIn(to.paint, t) };
|
|
27
|
+
}
|
|
28
|
+
switch (to.kind) {
|
|
29
|
+
case "group": {
|
|
30
|
+
const before = new Map(
|
|
31
|
+
from.children.map((child) => [child.key, child])
|
|
32
|
+
);
|
|
33
|
+
return {
|
|
34
|
+
...to,
|
|
35
|
+
children: to.children.map(
|
|
36
|
+
(child) => tweenNode(before.get(child.key), child, t)
|
|
37
|
+
)
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
case "rect": {
|
|
41
|
+
const previous = from;
|
|
42
|
+
return {
|
|
43
|
+
...to,
|
|
44
|
+
x: mix(previous.x, to.x, t),
|
|
45
|
+
y: mix(previous.y, to.y, t),
|
|
46
|
+
width: mix(previous.width, to.width, t),
|
|
47
|
+
height: mix(previous.height, to.height, t)
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
case "circle": {
|
|
51
|
+
const previous = from;
|
|
52
|
+
return {
|
|
53
|
+
...to,
|
|
54
|
+
cx: mix(previous.cx, to.cx, t),
|
|
55
|
+
cy: mix(previous.cy, to.cy, t),
|
|
56
|
+
r: mix(previous.r, to.r, t)
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
case "line": {
|
|
60
|
+
const previous = from;
|
|
61
|
+
return {
|
|
62
|
+
...to,
|
|
63
|
+
x1: mix(previous.x1, to.x1, t),
|
|
64
|
+
y1: mix(previous.y1, to.y1, t),
|
|
65
|
+
x2: mix(previous.x2, to.x2, t),
|
|
66
|
+
y2: mix(previous.y2, to.y2, t)
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
case "text": {
|
|
70
|
+
const previous = from;
|
|
71
|
+
return {
|
|
72
|
+
...to,
|
|
73
|
+
x: mix(previous.x, to.x, t),
|
|
74
|
+
y: mix(previous.y, to.y, t)
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
case "path":
|
|
78
|
+
return { ...to, d: tweenPath(from.d, to.d, t) };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function tweenScene(from, to, t) {
|
|
82
|
+
if (t >= 1) {
|
|
83
|
+
return to;
|
|
84
|
+
}
|
|
85
|
+
const before = new Map(from.nodes.map((node) => [node.key, node]));
|
|
86
|
+
return {
|
|
87
|
+
...to,
|
|
88
|
+
nodes: to.nodes.map((node) => tweenNode(before.get(node.key), node, t))
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function easeOutCubic(t) {
|
|
92
|
+
return 1 - (1 - t) ** 3;
|
|
93
|
+
}
|
|
94
|
+
export {
|
|
95
|
+
easeOutCubic,
|
|
96
|
+
tweenScene
|
|
97
|
+
};
|
|
98
|
+
//# sourceMappingURL=tween.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/core/motion/tween.ts"],
|
|
4
|
+
"sourcesContent": ["import type { ChartPaint, ChartScene, SceneNode } from \"#/core/types.ts\";\n\n/**\n * A scene part of the way from one scene to the next, keyed by node: what an\n * animated update draws on each frame. It lives in the core so the SVG and\n * Canvas renderers animate identically, and never feeds hit-testing or\n * focus: those always read the scene being animated towards.\n *\n * A node present on both sides moves; a new one fades in; one that left is\n * gone at once (fading out a bar the data no longer has would show a value\n * that is not true any more). A path tweens point by point when both shapes\n * share their commands, and swaps at the end when they do not.\n */\n\nconst NUMBER = /-?\\d*\\.?\\d+(?:e[-+]?\\d+)?/gi;\n\nfunction mix(from: number, to: number, t: number): number {\n\treturn from + (to - from) * t;\n}\n\n/** Two paths can tween when only their numbers differ. */\nfunction tweenPath(from: string, to: string, t: number): string {\n\tconst fromNumbers = from.match(NUMBER);\n\tconst toNumbers = to.match(NUMBER);\n\tif (\n\t\tfromNumbers === null ||\n\t\ttoNumbers === null ||\n\t\tfromNumbers.length !== toNumbers.length ||\n\t\tfrom.replace(NUMBER, \"#\") !== to.replace(NUMBER, \"#\")\n\t) {\n\t\treturn t < 1 ? from : to;\n\t}\n\tlet index = 0;\n\treturn to.replace(NUMBER, () => {\n\t\tconst value = mix(Number(fromNumbers[index]), Number(toNumbers[index]), t);\n\t\tindex += 1;\n\t\treturn String(Math.round(value * 100) / 100);\n\t});\n}\n\nfunction fadeIn<TPaint extends ChartPaint>(paint: TPaint, t: number): TPaint {\n\treturn { ...paint, opacity: (paint.opacity ?? 1) * t };\n}\n\nfunction tweenNode(\n\tfrom: SceneNode | undefined,\n\tto: SceneNode,\n\tt: number,\n): SceneNode {\n\tif (from === undefined || from.kind !== to.kind) {\n\t\treturn to.kind === \"group\"\n\t\t\t? {\n\t\t\t\t\t...to,\n\t\t\t\t\tchildren: to.children.map((child) => tweenNode(undefined, child, t)),\n\t\t\t\t}\n\t\t\t: { ...to, paint: fadeIn(to.paint, t) };\n\t}\n\tswitch (to.kind) {\n\t\tcase \"group\": {\n\t\t\tconst before = new Map(\n\t\t\t\t(from as typeof to).children.map((child) => [child.key, child]),\n\t\t\t);\n\t\t\treturn {\n\t\t\t\t...to,\n\t\t\t\tchildren: to.children.map((child) =>\n\t\t\t\t\ttweenNode(before.get(child.key), child, t),\n\t\t\t\t),\n\t\t\t};\n\t\t}\n\t\tcase \"rect\": {\n\t\t\tconst previous = from as typeof to;\n\t\t\treturn {\n\t\t\t\t...to,\n\t\t\t\tx: mix(previous.x, to.x, t),\n\t\t\t\ty: mix(previous.y, to.y, t),\n\t\t\t\twidth: mix(previous.width, to.width, t),\n\t\t\t\theight: mix(previous.height, to.height, t),\n\t\t\t};\n\t\t}\n\t\tcase \"circle\": {\n\t\t\tconst previous = from as typeof to;\n\t\t\treturn {\n\t\t\t\t...to,\n\t\t\t\tcx: mix(previous.cx, to.cx, t),\n\t\t\t\tcy: mix(previous.cy, to.cy, t),\n\t\t\t\tr: mix(previous.r, to.r, t),\n\t\t\t};\n\t\t}\n\t\tcase \"line\": {\n\t\t\tconst previous = from as typeof to;\n\t\t\treturn {\n\t\t\t\t...to,\n\t\t\t\tx1: mix(previous.x1, to.x1, t),\n\t\t\t\ty1: mix(previous.y1, to.y1, t),\n\t\t\t\tx2: mix(previous.x2, to.x2, t),\n\t\t\t\ty2: mix(previous.y2, to.y2, t),\n\t\t\t};\n\t\t}\n\t\tcase \"text\": {\n\t\t\tconst previous = from as typeof to;\n\t\t\treturn {\n\t\t\t\t...to,\n\t\t\t\tx: mix(previous.x, to.x, t),\n\t\t\t\ty: mix(previous.y, to.y, t),\n\t\t\t};\n\t\t}\n\t\tcase \"path\":\n\t\t\treturn { ...to, d: tweenPath((from as typeof to).d, to.d, t) };\n\t}\n}\n\n/** `to`, `t` of the way from `from` (0 is `from`, 1 is `to`). */\nexport function tweenScene(\n\tfrom: ChartScene,\n\tto: ChartScene,\n\tt: number,\n): ChartScene {\n\tif (t >= 1) {\n\t\treturn to;\n\t}\n\tconst before = new Map(from.nodes.map((node) => [node.key, node]));\n\treturn {\n\t\t...to,\n\t\tnodes: to.nodes.map((node) => tweenNode(before.get(node.key), node, t)),\n\t};\n}\n\n/** Fast out, slow in: the motion settles rather than stops. */\nexport function easeOutCubic(t: number): number {\n\treturn 1 - (1 - t) ** 3;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ChartHitShape, ChartPoint, ChartScene } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Focus and hit-testing, on the scene only, so SVG and Canvas behave the same.
|
|
4
|
+
* A stop is what one arrow-key press moves to: one point, or every point that
|
|
5
|
+
* shares a position (the column of a multi-series chart).
|
|
6
|
+
*/
|
|
7
|
+
export interface ChartFocusStop {
|
|
8
|
+
readonly key: string;
|
|
9
|
+
readonly points: ReadonlyArray<ChartPoint>;
|
|
10
|
+
/** Where the stop sits along its axis, in chart pixels. */
|
|
11
|
+
readonly at: number;
|
|
12
|
+
}
|
|
13
|
+
export declare function focusStops(scene: ChartScene): ReadonlyArray<ChartFocusStop>;
|
|
14
|
+
export declare function contains(hit: ChartHitShape, x: number, y: number): boolean;
|
|
15
|
+
export interface ChartHit {
|
|
16
|
+
readonly stop: number;
|
|
17
|
+
readonly point: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The stop and point under the pointer. Along a position axis the nearest
|
|
21
|
+
* column wins wherever the pointer is in the plot, the way a reader scrubs a
|
|
22
|
+
* time series; a scatter, pie or map needs the pointer on or near the shape.
|
|
23
|
+
*/
|
|
24
|
+
export declare function findNearest(scene: ChartScene, stops: ReadonlyArray<ChartFocusStop>, x: number, y: number): ChartHit | null;
|
|
25
|
+
//# sourceMappingURL=nearest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nearest.d.ts","sourceRoot":"","sources":["../../src/core/nearest.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEX,aAAa,EACb,UAAU,EACV,UAAU,EAEV,MAAM,iBAAiB,CAAC;AAEzB;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IAC3C,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACpB;AAYD,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,aAAa,CAAC,cAAc,CAAC,CAyC3E;AAQD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,aAAa,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAqB1E;AAUD,MAAM,WAAW,QAAQ;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACvB;AA2BD;;;;GAIG;AACH,wBAAgB,WAAW,CAC1B,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,aAAa,CAAC,cAAc,CAAC,EACpC,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,GACP,QAAQ,GAAG,IAAI,CAuEjB"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { categoryKey } from "./scales/discrete.js";
|
|
2
|
+
const POINT_REACH = 24;
|
|
3
|
+
function positionOf(point, order) {
|
|
4
|
+
return order === "y" ? point.yValue : point.xValue;
|
|
5
|
+
}
|
|
6
|
+
function focusStops(scene) {
|
|
7
|
+
const order = scene.focusOrder;
|
|
8
|
+
if (order === "point") {
|
|
9
|
+
return scene.points.map((point) => ({
|
|
10
|
+
key: point.key,
|
|
11
|
+
points: [point],
|
|
12
|
+
at: point.x
|
|
13
|
+
}));
|
|
14
|
+
}
|
|
15
|
+
const stops = /* @__PURE__ */ new Map();
|
|
16
|
+
for (const point of scene.points) {
|
|
17
|
+
const position = positionOf(point, order);
|
|
18
|
+
const within = point.facet === void 0 ? "" : `${point.facet}|`;
|
|
19
|
+
const key = `${within}${position === void 0 ? `px:${Math.round(order === "y" ? point.y : point.x)}` : categoryKey(position)}`;
|
|
20
|
+
const stop = stops.get(key);
|
|
21
|
+
if (stop === void 0) {
|
|
22
|
+
stops.set(key, {
|
|
23
|
+
key,
|
|
24
|
+
points: [point],
|
|
25
|
+
at: order === "y" ? point.y : point.x
|
|
26
|
+
});
|
|
27
|
+
} else {
|
|
28
|
+
stop.points.push(point);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const cellOrder = new Map(
|
|
32
|
+
(scene.cells ?? []).map((cell, index) => [cell.key, index])
|
|
33
|
+
);
|
|
34
|
+
const cellOf = (stop) => cellOrder.get(stop.points[0].facet ?? "") ?? 0;
|
|
35
|
+
return [...stops.values()].sort(
|
|
36
|
+
(left, right) => cellOf(left) - cellOf(right) || left.at - right.at
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
function bearing(dx, dy) {
|
|
40
|
+
const degrees = Math.atan2(dx, -dy) * 180 / Math.PI;
|
|
41
|
+
return degrees < 0 ? degrees + 360 : degrees;
|
|
42
|
+
}
|
|
43
|
+
function contains(hit, x, y) {
|
|
44
|
+
if (hit.kind === "rect") {
|
|
45
|
+
const { rect } = hit;
|
|
46
|
+
return x >= rect.x && x <= rect.x + rect.width && y >= rect.y && y <= rect.y + rect.height;
|
|
47
|
+
}
|
|
48
|
+
const dx = x - hit.cx;
|
|
49
|
+
const dy = y - hit.cy;
|
|
50
|
+
const radius = Math.hypot(dx, dy);
|
|
51
|
+
if (radius < hit.innerRadius || radius > hit.outerRadius) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
const angle = bearing(dx, dy);
|
|
55
|
+
const start = (hit.startAngle % 360 + 360) % 360;
|
|
56
|
+
const sweep = hit.endAngle - hit.startAngle;
|
|
57
|
+
const offset = (angle - start + 360) % 360;
|
|
58
|
+
return sweep >= 360 || offset <= sweep;
|
|
59
|
+
}
|
|
60
|
+
function hitArea(hit) {
|
|
61
|
+
if (hit.kind === "rect") {
|
|
62
|
+
return hit.rect.width * hit.rect.height;
|
|
63
|
+
}
|
|
64
|
+
const sweep = Math.min(360, hit.endAngle - hit.startAngle) / 360;
|
|
65
|
+
return Math.PI * (hit.outerRadius ** 2 - hit.innerRadius ** 2) * sweep;
|
|
66
|
+
}
|
|
67
|
+
function pickInStop(stop, x, y, order) {
|
|
68
|
+
const inside = stop.points.findIndex(
|
|
69
|
+
(point) => point.hit !== void 0 && contains(point.hit, x, y)
|
|
70
|
+
);
|
|
71
|
+
if (inside !== -1) {
|
|
72
|
+
return inside;
|
|
73
|
+
}
|
|
74
|
+
let best = 0;
|
|
75
|
+
let bestDistance = Number.POSITIVE_INFINITY;
|
|
76
|
+
for (const [index, point] of stop.points.entries()) {
|
|
77
|
+
const distance = order === "y" ? Math.abs(point.x - x) : Math.abs(point.y - y);
|
|
78
|
+
if (distance < bestDistance) {
|
|
79
|
+
best = index;
|
|
80
|
+
bestDistance = distance;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return best;
|
|
84
|
+
}
|
|
85
|
+
function findNearest(scene, stops, x, y) {
|
|
86
|
+
if (stops.length === 0) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
const order = scene.focusOrder;
|
|
90
|
+
if (order === "point") {
|
|
91
|
+
let inside = -1;
|
|
92
|
+
let insideArea = Number.POSITIVE_INFINITY;
|
|
93
|
+
for (const [index, stop] of stops.entries()) {
|
|
94
|
+
const hit = stop.points[0].hit;
|
|
95
|
+
if (hit !== void 0 && contains(hit, x, y)) {
|
|
96
|
+
const area = hitArea(hit);
|
|
97
|
+
if (area < insideArea) {
|
|
98
|
+
inside = index;
|
|
99
|
+
insideArea = area;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (inside !== -1) {
|
|
104
|
+
return { stop: inside, point: 0 };
|
|
105
|
+
}
|
|
106
|
+
let best2 = -1;
|
|
107
|
+
let bestDistance2 = POINT_REACH;
|
|
108
|
+
for (const [index, stop] of stops.entries()) {
|
|
109
|
+
const point = stop.points[0];
|
|
110
|
+
if (point.hit?.kind === "arc") {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
const distance = Math.hypot(point.x - x, point.y - y);
|
|
114
|
+
if (distance < bestDistance2) {
|
|
115
|
+
best2 = index;
|
|
116
|
+
bestDistance2 = distance;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return best2 === -1 ? null : { stop: best2, point: 0 };
|
|
120
|
+
}
|
|
121
|
+
const cell = scene.cells?.find(
|
|
122
|
+
(candidate) => x >= candidate.plot.x && x <= candidate.plot.x + candidate.plot.width && y >= candidate.plot.y && y <= candidate.plot.y + candidate.plot.height
|
|
123
|
+
);
|
|
124
|
+
if (scene.cells !== void 0 && cell === void 0) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
const plot = cell?.plot ?? scene.plot;
|
|
128
|
+
if (x < plot.x || x > plot.x + plot.width || y < plot.y || y > plot.y + plot.height) {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
const along = order === "y" ? y : x;
|
|
132
|
+
let best = 0;
|
|
133
|
+
let bestDistance = Number.POSITIVE_INFINITY;
|
|
134
|
+
for (const [index, stop] of stops.entries()) {
|
|
135
|
+
if (cell !== void 0 && stop.points[0].facet !== cell.key) {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
const distance = Math.abs(stop.at - along);
|
|
139
|
+
if (distance < bestDistance) {
|
|
140
|
+
best = index;
|
|
141
|
+
bestDistance = distance;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return { stop: best, point: pickInStop(stops[best], x, y, order) };
|
|
145
|
+
}
|
|
146
|
+
export {
|
|
147
|
+
contains,
|
|
148
|
+
findNearest,
|
|
149
|
+
focusStops
|
|
150
|
+
};
|
|
151
|
+
//# sourceMappingURL=nearest.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/core/nearest.ts"],
|
|
4
|
+
"sourcesContent": ["import { categoryKey } from \"#/core/scales/discrete.ts\";\nimport type {\n\tChartFocusOrder,\n\tChartHitShape,\n\tChartPoint,\n\tChartScene,\n\tChartValue,\n} from \"#/core/types.ts\";\n\n/**\n * Focus and hit-testing, on the scene only, so SVG and Canvas behave the same.\n * A stop is what one arrow-key press moves to: one point, or every point that\n * shares a position (the column of a multi-series chart).\n */\nexport interface ChartFocusStop {\n\treadonly key: string;\n\treadonly points: ReadonlyArray<ChartPoint>;\n\t/** Where the stop sits along its axis, in chart pixels. */\n\treadonly at: number;\n}\n\n/** How far from a dot the pointer may be and still hover it, in pixels. */\nconst POINT_REACH = 24;\n\nfunction positionOf(\n\tpoint: ChartPoint,\n\torder: ChartFocusOrder,\n): ChartValue | undefined {\n\treturn order === \"y\" ? point.yValue : point.xValue;\n}\n\nexport function focusStops(scene: ChartScene): ReadonlyArray<ChartFocusStop> {\n\tconst order = scene.focusOrder;\n\tif (order === \"point\") {\n\t\treturn scene.points.map((point) => ({\n\t\t\tkey: point.key,\n\t\t\tpoints: [point],\n\t\t\tat: point.x,\n\t\t}));\n\t}\n\tconst stops = new Map<\n\t\tstring,\n\t\t{ key: string; points: ChartPoint[]; at: number }\n\t>();\n\tfor (const point of scene.points) {\n\t\tconst position = positionOf(point, order);\n\t\t// On a faceted chart a column is a column of one cell, never across cells.\n\t\tconst within = point.facet === undefined ? \"\" : `${point.facet}|`;\n\t\tconst key = `${within}${\n\t\t\tposition === undefined\n\t\t\t\t? `px:${Math.round(order === \"y\" ? point.y : point.x)}`\n\t\t\t\t: categoryKey(position)\n\t\t}`;\n\t\tconst stop = stops.get(key);\n\t\tif (stop === undefined) {\n\t\t\tstops.set(key, {\n\t\t\t\tkey,\n\t\t\t\tpoints: [point],\n\t\t\t\tat: order === \"y\" ? point.y : point.x,\n\t\t\t});\n\t\t} else {\n\t\t\tstop.points.push(point);\n\t\t}\n\t}\n\tconst cellOrder = new Map(\n\t\t(scene.cells ?? []).map((cell, index) => [cell.key, index]),\n\t);\n\tconst cellOf = (stop: { points: ChartPoint[] }) =>\n\t\tcellOrder.get(stop.points[0].facet ?? \"\") ?? 0;\n\treturn [...stops.values()].sort(\n\t\t(left, right) => cellOf(left) - cellOf(right) || left.at - right.at,\n\t);\n}\n\n/** Degrees clockwise from twelve o'clock, in `[0, 360)`. */\nfunction bearing(dx: number, dy: number): number {\n\tconst degrees = (Math.atan2(dx, -dy) * 180) / Math.PI;\n\treturn degrees < 0 ? degrees + 360 : degrees;\n}\n\nexport function contains(hit: ChartHitShape, x: number, y: number): boolean {\n\tif (hit.kind === \"rect\") {\n\t\tconst { rect } = hit;\n\t\treturn (\n\t\t\tx >= rect.x &&\n\t\t\tx <= rect.x + rect.width &&\n\t\t\ty >= rect.y &&\n\t\t\ty <= rect.y + rect.height\n\t\t);\n\t}\n\tconst dx = x - hit.cx;\n\tconst dy = y - hit.cy;\n\tconst radius = Math.hypot(dx, dy);\n\tif (radius < hit.innerRadius || radius > hit.outerRadius) {\n\t\treturn false;\n\t}\n\tconst angle = bearing(dx, dy);\n\tconst start = ((hit.startAngle % 360) + 360) % 360;\n\tconst sweep = hit.endAngle - hit.startAngle;\n\tconst offset = (angle - start + 360) % 360;\n\treturn sweep >= 360 || offset <= sweep;\n}\n\nfunction hitArea(hit: ChartHitShape): number {\n\tif (hit.kind === \"rect\") {\n\t\treturn hit.rect.width * hit.rect.height;\n\t}\n\tconst sweep = Math.min(360, hit.endAngle - hit.startAngle) / 360;\n\treturn Math.PI * (hit.outerRadius ** 2 - hit.innerRadius ** 2) * sweep;\n}\n\nexport interface ChartHit {\n\treadonly stop: number;\n\treadonly point: number;\n}\n\nfunction pickInStop(\n\tstop: ChartFocusStop,\n\tx: number,\n\ty: number,\n\torder: ChartFocusOrder,\n): number {\n\tconst inside = stop.points.findIndex(\n\t\t(point) => point.hit !== undefined && contains(point.hit, x, y),\n\t);\n\tif (inside !== -1) {\n\t\treturn inside;\n\t}\n\tlet best = 0;\n\tlet bestDistance = Number.POSITIVE_INFINITY;\n\tfor (const [index, point] of stop.points.entries()) {\n\t\tconst distance =\n\t\t\torder === \"y\" ? Math.abs(point.x - x) : Math.abs(point.y - y);\n\t\tif (distance < bestDistance) {\n\t\t\tbest = index;\n\t\t\tbestDistance = distance;\n\t\t}\n\t}\n\treturn best;\n}\n\n/**\n * The stop and point under the pointer. Along a position axis the nearest\n * column wins wherever the pointer is in the plot, the way a reader scrubs a\n * time series; a scatter, pie or map needs the pointer on or near the shape.\n */\nexport function findNearest(\n\tscene: ChartScene,\n\tstops: ReadonlyArray<ChartFocusStop>,\n\tx: number,\n\ty: number,\n): ChartHit | null {\n\tif (stops.length === 0) {\n\t\treturn null;\n\t}\n\tconst order = scene.focusOrder;\n\tif (order === \"point\") {\n\t\t// Shapes can overlap (a dot over a region, regions' bounding boxes):\n\t\t// the smallest one under the pointer is the one meant.\n\t\tlet inside = -1;\n\t\tlet insideArea = Number.POSITIVE_INFINITY;\n\t\tfor (const [index, stop] of stops.entries()) {\n\t\t\tconst hit = stop.points[0].hit;\n\t\t\tif (hit !== undefined && contains(hit, x, y)) {\n\t\t\t\tconst area = hitArea(hit);\n\t\t\t\tif (area < insideArea) {\n\t\t\t\t\tinside = index;\n\t\t\t\t\tinsideArea = area;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (inside !== -1) {\n\t\t\treturn { stop: inside, point: 0 };\n\t\t}\n\t\tlet best = -1;\n\t\tlet bestDistance = POINT_REACH;\n\t\tfor (const [index, stop] of stops.entries()) {\n\t\t\tconst point = stop.points[0];\n\t\t\tif (point.hit?.kind === \"arc\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst distance = Math.hypot(point.x - x, point.y - y);\n\t\t\tif (distance < bestDistance) {\n\t\t\t\tbest = index;\n\t\t\t\tbestDistance = distance;\n\t\t\t}\n\t\t}\n\t\treturn best === -1 ? null : { stop: best, point: 0 };\n\t}\n\tconst cell = scene.cells?.find(\n\t\t(candidate) =>\n\t\t\tx >= candidate.plot.x &&\n\t\t\tx <= candidate.plot.x + candidate.plot.width &&\n\t\t\ty >= candidate.plot.y &&\n\t\t\ty <= candidate.plot.y + candidate.plot.height,\n\t);\n\tif (scene.cells !== undefined && cell === undefined) {\n\t\treturn null;\n\t}\n\tconst plot = cell?.plot ?? scene.plot;\n\tif (\n\t\tx < plot.x ||\n\t\tx > plot.x + plot.width ||\n\t\ty < plot.y ||\n\t\ty > plot.y + plot.height\n\t) {\n\t\treturn null;\n\t}\n\tconst along = order === \"y\" ? y : x;\n\tlet best = 0;\n\tlet bestDistance = Number.POSITIVE_INFINITY;\n\tfor (const [index, stop] of stops.entries()) {\n\t\tif (cell !== undefined && stop.points[0].facet !== cell.key) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst distance = Math.abs(stop.at - along);\n\t\tif (distance < bestDistance) {\n\t\t\tbest = index;\n\t\t\tbestDistance = distance;\n\t\t}\n\t}\n\treturn { stop: best, point: pickInStop(stops[best], x, y, order) };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["best", "bestDistance"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { ChartCorners, ChartCurve } from "./types.js";
|
|
2
|
+
/** A point in chart pixels. */
|
|
3
|
+
export interface ChartXY {
|
|
4
|
+
readonly x: number;
|
|
5
|
+
readonly y: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* An open path through `points`. Returns `""` for fewer than two points, which
|
|
9
|
+
* renders as nothing rather than as a stray dot.
|
|
10
|
+
*/
|
|
11
|
+
export declare function linePath(points: ReadonlyArray<ChartXY>, curve?: ChartCurve): string;
|
|
12
|
+
/**
|
|
13
|
+
* A closed band between a top edge and a bottom edge, both drawn with the same
|
|
14
|
+
* curve, so an area and its outline never disagree. The bottom edge runs under
|
|
15
|
+
* the same x positions as the top: a flat baseline, or the series below it in
|
|
16
|
+
* a stack.
|
|
17
|
+
*/
|
|
18
|
+
export declare function areaPath(top: ReadonlyArray<ChartXY>, bottom: ReadonlyArray<ChartXY>, curve?: ChartCurve): string;
|
|
19
|
+
/** A closed polygon — the radar mark's outline. */
|
|
20
|
+
export declare function polygonPath(points: ReadonlyArray<ChartXY>): string;
|
|
21
|
+
export interface RoundedBarOptions {
|
|
22
|
+
readonly x: number;
|
|
23
|
+
readonly y: number;
|
|
24
|
+
readonly width: number;
|
|
25
|
+
readonly height: number;
|
|
26
|
+
/** One radius, or `[topLeft, topRight, bottomRight, bottomLeft]`. */
|
|
27
|
+
readonly radius?: number | ChartCorners;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A rectangle with independently rounded corners — stacked bars round only the
|
|
31
|
+
* outer end of the stack, so the inner joins stay square.
|
|
32
|
+
*/
|
|
33
|
+
export declare function roundedBarPath(options: RoundedBarOptions): string;
|
|
34
|
+
/**
|
|
35
|
+
* Angles are degrees, clockwise, zero at twelve o'clock: the way a pie chart
|
|
36
|
+
* is described out loud.
|
|
37
|
+
*/
|
|
38
|
+
export declare function polarToCartesian(cx: number, cy: number, radius: number, angle: number): ChartXY;
|
|
39
|
+
export interface ArcOptions {
|
|
40
|
+
/** Centre of the circle the arc is cut from, in plot coordinates. */
|
|
41
|
+
readonly cx: number;
|
|
42
|
+
/** Centre of the circle the arc is cut from, in plot coordinates. */
|
|
43
|
+
readonly cy: number;
|
|
44
|
+
/** Inner edge. `0` makes the arc a pie wedge rather than a donut segment. */
|
|
45
|
+
readonly innerRadius: number;
|
|
46
|
+
/** Outer edge, in pixels from the centre. */
|
|
47
|
+
readonly outerRadius: number;
|
|
48
|
+
/** Degrees clockwise from twelve o'clock. */
|
|
49
|
+
readonly startAngle: number;
|
|
50
|
+
/** Degrees clockwise from twelve o'clock. A 360-degree sweep draws a full ring. */
|
|
51
|
+
readonly endAngle: number;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* A pie wedge (`innerRadius` 0) or a donut segment. A sweep of 360 degrees or
|
|
55
|
+
* more is drawn as two half arcs, because a single arc back to its own start
|
|
56
|
+
* point renders as nothing at all.
|
|
57
|
+
*/
|
|
58
|
+
export declare function arcPath(options: ArcOptions): string;
|
|
59
|
+
/**
|
|
60
|
+
* Path data for GeoJSON polygon rings already in chart pixels (what
|
|
61
|
+
* d3-contour returns): every ring closed, holes drawn with the even-odd rule
|
|
62
|
+
* the renderers default to.
|
|
63
|
+
*/
|
|
64
|
+
export declare function multiPolygonPath(polygons: ReadonlyArray<ReadonlyArray<ReadonlyArray<readonly [number, number] | ReadonlyArray<number>>>>, offsetX?: number, offsetY?: number): string;
|
|
65
|
+
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/core/paths.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAEhE,+BAA+B;AAC/B,MAAM,WAAW,OAAO;IACvB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;CACnB;AA2GD;;;GAGG;AACH,wBAAgB,QAAQ,CACvB,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,EAC9B,KAAK,GAAE,UAAqB,GAC1B,MAAM,CAKR;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CACvB,GAAG,EAAE,aAAa,CAAC,OAAO,CAAC,EAC3B,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,EAC9B,KAAK,GAAE,UAAqB,GAC1B,MAAM,CAOR;AAED,mDAAmD;AACnD,wBAAgB,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,GAAG,MAAM,CASlE;AAkBD,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,YAAY,CAAC;CACxC;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAgCjE;AAID;;;GAGG;AACH,wBAAgB,gBAAgB,CAC/B,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACX,OAAO,CAMT;AAED,MAAM,WAAW,UAAU;IAC1B,qEAAqE;IACrE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,6CAA6C;IAC7C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,6CAA6C;IAC7C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,mFAAmF;IACnF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC1B;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,UAAU,GAAG,MAAM,CAqCnD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC/B,QAAQ,EAAE,aAAa,CACtB,aAAa,CACZ,aAAa,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,CAChE,CACD,EACD,OAAO,SAAI,EACX,OAAO,SAAI,GACT,MAAM,CAUR"}
|