@voila.dev/chart 5.0.113 → 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.
Files changed (103) hide show
  1. package/dist/canvas/paint-scene.d.ts.map +1 -1
  2. package/dist/canvas/paint-scene.js +3 -1
  3. package/dist/canvas/paint-scene.js.map +2 -2
  4. package/dist/contour/density.d.ts.map +1 -1
  5. package/dist/contour/density.js +2 -0
  6. package/dist/contour/density.js.map +2 -2
  7. package/dist/core/marks/area.d.ts.map +1 -1
  8. package/dist/core/marks/area.js +27 -11
  9. package/dist/core/marks/area.js.map +2 -2
  10. package/dist/core/marks/bar.d.ts.map +1 -1
  11. package/dist/core/marks/bar.js +2 -0
  12. package/dist/core/marks/bar.js.map +2 -2
  13. package/dist/core/marks/line.d.ts.map +1 -1
  14. package/dist/core/marks/line.js +13 -4
  15. package/dist/core/marks/line.js.map +2 -2
  16. package/dist/core/marks/polar.d.ts.map +1 -1
  17. package/dist/core/marks/polar.js +22 -5
  18. package/dist/core/marks/polar.js.map +2 -2
  19. package/dist/core/marks/shared.d.ts +8 -1
  20. package/dist/core/marks/shared.d.ts.map +1 -1
  21. package/dist/core/marks/shared.js.map +2 -2
  22. package/dist/core/marks/stats/difference.d.ts.map +1 -1
  23. package/dist/core/marks/stats/difference.js +20 -7
  24. package/dist/core/marks/stats/difference.js.map +2 -2
  25. package/dist/core/motion/arc-motion.d.ts +9 -0
  26. package/dist/core/motion/arc-motion.d.ts.map +1 -0
  27. package/dist/core/motion/arc-motion.js +67 -0
  28. package/dist/core/motion/arc-motion.js.map +7 -0
  29. package/dist/core/motion/enter-exit.d.ts +6 -0
  30. package/dist/core/motion/enter-exit.d.ts.map +1 -0
  31. package/dist/core/motion/enter-exit.js +20 -0
  32. package/dist/core/motion/enter-exit.js.map +7 -0
  33. package/dist/core/motion/leaving.d.ts +12 -0
  34. package/dist/core/motion/leaving.d.ts.map +1 -0
  35. package/dist/core/motion/leaving.js +33 -0
  36. package/dist/core/motion/leaving.js.map +7 -0
  37. package/dist/core/motion/motion-store.d.ts +22 -0
  38. package/dist/core/motion/motion-store.d.ts.map +1 -0
  39. package/dist/core/motion/motion-store.js +325 -0
  40. package/dist/core/motion/motion-store.js.map +7 -0
  41. package/dist/core/motion/points-motion.d.ts +6 -0
  42. package/dist/core/motion/points-motion.d.ts.map +1 -0
  43. package/dist/core/motion/points-motion.js +140 -0
  44. package/dist/core/motion/points-motion.js.map +7 -0
  45. package/dist/core/motion/strings.d.ts +18 -0
  46. package/dist/core/motion/strings.d.ts.map +1 -0
  47. package/dist/core/motion/strings.js +34 -0
  48. package/dist/core/motion/strings.js.map +7 -0
  49. package/dist/core/motion/timing.d.ts +30 -0
  50. package/dist/core/motion/timing.d.ts.map +1 -0
  51. package/dist/core/motion/timing.js +25 -0
  52. package/dist/core/motion/timing.js.map +7 -0
  53. package/dist/core/motion/tween.d.ts +2 -0
  54. package/dist/core/motion/tween.d.ts.map +1 -1
  55. package/dist/core/motion/tween.js +1 -0
  56. package/dist/core/motion/tween.js.map +2 -2
  57. package/dist/core/types.d.ts +85 -0
  58. package/dist/core/types.d.ts.map +1 -1
  59. package/dist/geo/geo.d.ts.map +1 -1
  60. package/dist/geo/geo.js +2 -0
  61. package/dist/geo/geo.js.map +2 -2
  62. package/dist/index.d.ts +2 -0
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +8 -0
  65. package/dist/index.js.map +2 -2
  66. package/dist/react/chart.d.ts +7 -2
  67. package/dist/react/chart.d.ts.map +1 -1
  68. package/dist/react/chart.js +12 -7
  69. package/dist/react/chart.js.map +2 -2
  70. package/dist/react/svg-scene.d.ts.map +1 -1
  71. package/dist/react/svg-scene.js +3 -1
  72. package/dist/react/svg-scene.js.map +2 -2
  73. package/dist/react/use-animated-scene.d.ts +6 -5
  74. package/dist/react/use-animated-scene.d.ts.map +1 -1
  75. package/dist/react/use-animated-scene.js +36 -24
  76. package/dist/react/use-animated-scene.js.map +2 -2
  77. package/dist/voronoi/voronoi.d.ts.map +1 -1
  78. package/dist/voronoi/voronoi.js +2 -0
  79. package/dist/voronoi/voronoi.js.map +2 -2
  80. package/package.json +2 -1
  81. package/src/canvas/paint-scene.ts +5 -1
  82. package/src/contour/density.ts +2 -0
  83. package/src/core/marks/area.ts +28 -13
  84. package/src/core/marks/bar.ts +2 -0
  85. package/src/core/marks/line.ts +15 -8
  86. package/src/core/marks/polar.ts +22 -5
  87. package/src/core/marks/shared.ts +8 -0
  88. package/src/core/marks/stats/difference.ts +28 -8
  89. package/src/core/motion/arc-motion.ts +113 -0
  90. package/src/core/motion/enter-exit.ts +44 -0
  91. package/src/core/motion/leaving.ts +55 -0
  92. package/src/core/motion/motion-store.ts +449 -0
  93. package/src/core/motion/points-motion.ts +190 -0
  94. package/src/core/motion/strings.ts +68 -0
  95. package/src/core/motion/timing.ts +63 -0
  96. package/src/core/motion/tween.ts +1 -1
  97. package/src/core/types.ts +98 -0
  98. package/src/geo/geo.ts +2 -0
  99. package/src/index.ts +9 -0
  100. package/src/react/chart.tsx +21 -9
  101. package/src/react/svg-scene.tsx +5 -1
  102. package/src/react/use-animated-scene.ts +49 -33
  103. package/src/voronoi/voronoi.ts +2 -0
@@ -0,0 +1,325 @@
1
+ import { spring, tween } from "@voila.dev/motion";
2
+ import { enterFrom, exitTo } from "./enter-exit.js";
3
+ import {
4
+ leavingGuides,
5
+ withLeaving
6
+ } from "./leaving.js";
7
+ import {
8
+ STRING_CHANNELS,
9
+ stringMixer,
10
+ stringOf,
11
+ withStrings
12
+ } from "./strings.js";
13
+ import { staggerDelay } from "./timing.js";
14
+ const FIELDS = {
15
+ rect: ["x", "y", "width", "height"],
16
+ circle: ["cx", "cy", "r"],
17
+ line: ["x1", "y1", "x2", "y2"],
18
+ text: ["x", "y"],
19
+ path: [],
20
+ group: []
21
+ };
22
+ const OPACITY = "opacity";
23
+ const DRAWN = "drawn";
24
+ const SEPARATOR = "\0";
25
+ function walk(nodes, visit) {
26
+ for (const node of nodes) {
27
+ visit(node, nodes);
28
+ if (node.kind === "group") walk(node.children, visit);
29
+ }
30
+ }
31
+ function index(nodes) {
32
+ const byKey = /* @__PURE__ */ new Map();
33
+ const siblings = /* @__PURE__ */ new Map();
34
+ walk(nodes, (node, list) => {
35
+ byKey.set(node.key, node);
36
+ siblings.set(node.key, list);
37
+ });
38
+ return { byKey, siblings };
39
+ }
40
+ function channelKey(node, channel) {
41
+ return `${node}${SEPARATOR}${channel}`;
42
+ }
43
+ function elapsed(motion, now) {
44
+ return Math.max(0, (now - motion.start) / 1e3);
45
+ }
46
+ function done(motion, now) {
47
+ return elapsed(motion, now) >= motion.generator.duration;
48
+ }
49
+ function clamp(value) {
50
+ return Math.min(1, Math.max(0, value));
51
+ }
52
+ function createMotionStore(initial, timing) {
53
+ let target = initial;
54
+ const numbers = /* @__PURE__ */ new Map();
55
+ const strings = /* @__PURE__ */ new Map();
56
+ const guides = /* @__PURE__ */ new Map();
57
+ const shapes = /* @__PURE__ */ new Map();
58
+ const leaving = /* @__PURE__ */ new Map();
59
+ function generator(from, to, velocity) {
60
+ return timing.type === "spring" ? spring({
61
+ from,
62
+ to,
63
+ velocity,
64
+ duration: timing.duration,
65
+ bounce: timing.bounce
66
+ }) : tween({
67
+ keyframes: [from, to],
68
+ duration: timing.duration,
69
+ ease: timing.easing
70
+ });
71
+ }
72
+ function value(node, channel, fallback, now) {
73
+ const motion = numbers.get(channelKey(node, channel));
74
+ return motion ? motion.generator.at(elapsed(motion, now)).value : fallback;
75
+ }
76
+ function spring1(node, channel, from, to, start, now) {
77
+ const key = channelKey(node, channel);
78
+ const running = numbers.get(key);
79
+ const velocity = running ? running.generator.at(elapsed(running, now)).velocity : 0;
80
+ numbers.delete(key);
81
+ if (from === to && velocity === 0) return;
82
+ numbers.set(key, { generator: generator(from, to, velocity), start });
83
+ }
84
+ function mixString(from, to, channel, start) {
85
+ const key = channelKey(to.key, channel);
86
+ strings.delete(key);
87
+ const was = stringOf(from, channel);
88
+ const goal = stringOf(to, channel);
89
+ if (was === void 0 || goal === void 0 || was === goal) return;
90
+ strings.set(key, {
91
+ generator: generator(0, 1, 0),
92
+ start,
93
+ mix: stringMixer(to, channel, was, goal)
94
+ });
95
+ }
96
+ function drop(node) {
97
+ for (const motions of [numbers, strings]) {
98
+ for (const key of motions.keys()) {
99
+ if (key.startsWith(node + SEPARATOR)) motions.delete(key);
100
+ }
101
+ }
102
+ shapes.delete(node);
103
+ leaving.delete(node);
104
+ }
105
+ function move(from, to, start, now) {
106
+ const key = to.key;
107
+ for (const field of FIELDS[to.kind]) {
108
+ const was = from[field];
109
+ spring1(
110
+ key,
111
+ field,
112
+ was,
113
+ to[field],
114
+ start,
115
+ now
116
+ );
117
+ }
118
+ mixString(from, to, "fill", start);
119
+ mixString(from, to, "stroke", start);
120
+ if (to.kind !== "path" || from.kind !== "path") return;
121
+ const previous = shapes.get(key);
122
+ shapes.delete(key);
123
+ strings.delete(channelKey(key, "d"));
124
+ const motion = to.motion;
125
+ const plan = motion && from.geometry && to.geometry ? motion.plan(from.geometry, to.geometry) : void 0;
126
+ if (!plan) {
127
+ mixString(from, to, "d", start);
128
+ return;
129
+ }
130
+ for (const channel of previous?.plan.targets.keys() ?? []) {
131
+ if (!plan.targets.has(channel)) {
132
+ numbers.delete(channelKey(key, `g${channel}`));
133
+ }
134
+ }
135
+ for (const [channel, goal] of plan.targets) {
136
+ const was = plan.starts.get(channel) ?? goal;
137
+ spring1(key, `g${channel}`, was, goal, start, now);
138
+ }
139
+ shapes.set(key, { plan, motion });
140
+ }
141
+ function paintNode(node, now) {
142
+ if (node.kind === "group") {
143
+ return { ...node, children: paintList(node.children, node.key, now) };
144
+ }
145
+ const key = node.key;
146
+ const moved = {};
147
+ for (const field of FIELDS[node.kind]) {
148
+ moved[field] = value(
149
+ key,
150
+ field,
151
+ node[field],
152
+ now
153
+ );
154
+ }
155
+ let next = { ...node, ...moved };
156
+ const shape = shapes.get(key);
157
+ if (next.kind === "path" && shape) {
158
+ const geometry2 = shape.plan.build(
159
+ (channel) => value(key, `g${channel}`, shape.plan.targets.get(channel) ?? 0, now)
160
+ );
161
+ next = { ...next, geometry: geometry2, d: shape.motion.draw(geometry2) };
162
+ }
163
+ const mixed = {};
164
+ for (const channel of STRING_CHANNELS) {
165
+ const motion = strings.get(channelKey(key, channel));
166
+ if (motion) {
167
+ mixed[channel] = motion.mix(
168
+ motion.generator.at(elapsed(motion, now)).value
169
+ );
170
+ }
171
+ }
172
+ next = withStrings(next, mixed);
173
+ const opacity = value(key, OPACITY, 1, now);
174
+ const drawn = value(key, DRAWN, 1, now);
175
+ if (opacity === 1 && drawn === 1) return next;
176
+ const geometry = next.kind === "path" ? next.geometry : void 0;
177
+ const length = geometry && next.kind === "path" ? next.motion?.length?.(geometry) : 0;
178
+ return {
179
+ ...next,
180
+ paint: {
181
+ ...next.paint,
182
+ opacity: (next.paint.opacity ?? 1) * clamp(opacity),
183
+ ...drawn < 1 && geometry ? {
184
+ drawn: {
185
+ fraction: clamp(drawn),
186
+ length: length ?? 0
187
+ }
188
+ } : {}
189
+ }
190
+ };
191
+ }
192
+ function paintList(list, parent, now) {
193
+ const out = [];
194
+ function place(after) {
195
+ for (const [key, exit] of leaving) {
196
+ if (exit.after !== after) continue;
197
+ out.push(paintNode(exit.node, now));
198
+ place(key);
199
+ }
200
+ }
201
+ if (parent === null) place(void 0);
202
+ for (const node of list) {
203
+ out.push(paintNode(node, now));
204
+ place(node.key);
205
+ }
206
+ if (guides.size === 0) return out;
207
+ const fading = [...guides.values()].map(
208
+ (motion) => [
209
+ motion.guide,
210
+ Math.max(0, motion.generator.at(elapsed(motion, now)).value)
211
+ ]
212
+ );
213
+ return withLeaving(out, parent, fading);
214
+ }
215
+ function prune(now) {
216
+ for (const motions of [numbers, strings, guides]) {
217
+ for (const [key, motion] of motions) {
218
+ if (done(motion, now)) motions.delete(key);
219
+ }
220
+ }
221
+ const moving = new Set(
222
+ [...numbers.keys(), ...strings.keys()].map(
223
+ (key) => key.split(SEPARATOR)[0]
224
+ )
225
+ );
226
+ for (const key of shapes.keys()) {
227
+ if (!moving.has(key)) shapes.delete(key);
228
+ }
229
+ for (const key of leaving.keys()) {
230
+ if (!moving.has(key)) leaving.delete(key);
231
+ }
232
+ }
233
+ return {
234
+ retarget(scene, now) {
235
+ const before = index(target.nodes);
236
+ const after = index(scene.nodes);
237
+ function painted(key) {
238
+ const node = before.byKey.get(key) ?? leaving.get(key)?.node ?? guides.get(key)?.guide.node;
239
+ return node && paintNode(node, now);
240
+ }
241
+ for (const guide of leavingGuides(
242
+ target.nodes,
243
+ (key) => after.byKey.has(key)
244
+ )) {
245
+ guides.set(guide.node.key, {
246
+ generator: generator(value(guide.node.key, OPACITY, 1, now), 0, 0),
247
+ start: now,
248
+ guide
249
+ });
250
+ }
251
+ for (const key of /* @__PURE__ */ new Set([...before.byKey.keys(), ...leaving.keys()])) {
252
+ if (after.byKey.has(key)) continue;
253
+ const base = before.byKey.get(key) ?? leaving.get(key)?.node;
254
+ const siblings = before.siblings.get(key) ?? leaving.get(key)?.siblings ?? [];
255
+ const goal = exitTo(base, siblings, after.byKey);
256
+ const from = painted(key);
257
+ if (!goal || !from) {
258
+ drop(key);
259
+ continue;
260
+ }
261
+ move(from, goal, now, now);
262
+ const at = siblings.findIndex((node) => node.key === key);
263
+ leaving.set(key, {
264
+ node: goal,
265
+ after: siblings[at - 1]?.key,
266
+ siblings
267
+ });
268
+ }
269
+ const marks = [...after.byKey.values()].filter(
270
+ (node) => node.role === "mark"
271
+ );
272
+ const order = new Map(marks.map((node, at) => [node.key, at]));
273
+ for (const node of after.byKey.values()) {
274
+ if (node.kind === "group") continue;
275
+ const start = now + staggerDelay(timing, order.get(node.key) ?? 0, marks.length) * 1e3;
276
+ const old = painted(node.key);
277
+ leaving.delete(node.key);
278
+ const from = old ?? enterFrom(node, after.siblings.get(node.key) ?? [], before.byKey);
279
+ if (from) move(from, node, start, now);
280
+ const fading = numbers.get(channelKey(node.key, OPACITY));
281
+ const returning = guides.get(node.key);
282
+ if (!from && node.enter !== "none" || fading || returning) {
283
+ const shown = returning ? Math.max(0, returning.generator.at(elapsed(returning, now)).value) : fading ? value(node.key, OPACITY, 1, now) : 0;
284
+ spring1(node.key, OPACITY, shown, 1, start, now);
285
+ }
286
+ }
287
+ for (const key of after.byKey.keys()) guides.delete(key);
288
+ target = scene;
289
+ },
290
+ introduce(now) {
291
+ let any = false;
292
+ walk(target.nodes, (node) => {
293
+ if (node.kind !== "path" || node.enter !== "draw") return;
294
+ if (node.geometry?.kind !== "points" || node.paint.strokeDasharray) {
295
+ return;
296
+ }
297
+ spring1(node.key, DRAWN, 0, 1, now, now);
298
+ any = true;
299
+ });
300
+ return any;
301
+ },
302
+ snap(scene) {
303
+ for (const motions of [numbers, strings, guides, shapes, leaving]) {
304
+ motions.clear();
305
+ }
306
+ target = scene;
307
+ },
308
+ frame(now) {
309
+ prune(now);
310
+ if (numbers.size + strings.size + guides.size === 0) return target;
311
+ return { ...target, nodes: paintList(target.nodes, null, now) };
312
+ },
313
+ settled(now) {
314
+ return [
315
+ ...numbers.values(),
316
+ ...strings.values(),
317
+ ...guides.values()
318
+ ].every((motion) => done(motion, now));
319
+ }
320
+ };
321
+ }
322
+ export {
323
+ createMotionStore
324
+ };
325
+ //# sourceMappingURL=motion-store.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/core/motion/motion-store.ts"],
4
+ "sourcesContent": ["import { type Generator, spring, tween } from \"@voila.dev/motion\";\nimport { enterFrom, exitTo } from \"#/core/motion/enter-exit.ts\";\nimport {\n\ttype LeavingGuide,\n\tleavingGuides,\n\twithLeaving,\n} from \"#/core/motion/leaving.ts\";\nimport {\n\tSTRING_CHANNELS,\n\ttype StringChannel,\n\tstringMixer,\n\tstringOf,\n\twithStrings,\n} from \"#/core/motion/strings.ts\";\nimport { type ChartTiming, staggerDelay } from \"#/core/motion/timing.ts\";\nimport type {\n\tChartScene,\n\tGeometryMotion,\n\tGeometryPlan,\n\tSceneNode,\n} from \"#/core/types.ts\";\n\n/**\n * Every moving number of a chart, as springs keyed by node and channel. A\n * new scene retargets them: each channel starts from where it is painted, at\n * the speed it has, so an update landing mid-flight bends the motion instead\n * of kinking it. Pure and clocked from outside (milliseconds), so the SVG and\n * Canvas renderers move identically and tests step it by hand. Hit-testing\n * and focus never read it: they read the scene it moves towards, which never\n * holds the marks collapsing or the guides fading on their way out.\n */\nexport interface MotionStore {\n\tretarget(scene: ChartScene, now: number): void;\n\t/** The first render: lines that `enter: \"draw\"` trace themselves in. False when none does. */\n\tintroduce(now: number): boolean;\n\t/** Jumps to `scene`: a resize, or reduced motion. */\n\tsnap(scene: ChartScene): void;\n\tframe(now: number): ChartScene;\n\tsettled(now: number): boolean;\n}\n\ninterface Motion {\n\treadonly generator: Generator;\n\t/** Milliseconds, the delay included. */\n\treadonly start: number;\n}\n\n/** A path or a colour: a progress from 0 to 1, and the mix it reads. */\ninterface StringMotion extends Motion {\n\treadonly mix: (progress: number) => string;\n}\n\n/** A guide fading out where it stood. */\ninterface GuideMotion extends Motion {\n\treadonly guide: LeavingGuide;\n}\n\n/** A data mark collapsing on its way out: drawn after its old sibling until its springs settle. */\ninterface Leaving {\n\treadonly node: SceneNode;\n\treadonly after: string | undefined;\n\treadonly siblings: readonly SceneNode[];\n}\n\ntype Numeric = Record<string, number>;\n\nconst FIELDS: Record<SceneNode[\"kind\"], readonly string[]> = {\n\trect: [\"x\", \"y\", \"width\", \"height\"],\n\tcircle: [\"cx\", \"cy\", \"r\"],\n\tline: [\"x1\", \"y1\", \"x2\", \"y2\"],\n\ttext: [\"x\", \"y\"],\n\tpath: [],\n\tgroup: [],\n};\n\nconst OPACITY = \"opacity\";\nconst DRAWN = \"drawn\";\nconst SEPARATOR = \"\\u0000\";\n\nfunction walk(\n\tnodes: readonly SceneNode[],\n\tvisit: (node: SceneNode, siblings: readonly SceneNode[]) => void,\n) {\n\tfor (const node of nodes) {\n\t\tvisit(node, nodes);\n\t\tif (node.kind === \"group\") walk(node.children, visit);\n\t}\n}\n\nfunction index(nodes: readonly SceneNode[]) {\n\tconst byKey = new Map<string, SceneNode>();\n\tconst siblings = new Map<string, readonly SceneNode[]>();\n\twalk(nodes, (node, list) => {\n\t\tbyKey.set(node.key, node);\n\t\tsiblings.set(node.key, list);\n\t});\n\treturn { byKey, siblings };\n}\n\nfunction channelKey(node: string, channel: string): string {\n\treturn `${node}${SEPARATOR}${channel}`;\n}\n\nfunction elapsed(motion: Motion, now: number): number {\n\treturn Math.max(0, (now - motion.start) / 1000);\n}\n\nfunction done(motion: Motion, now: number): boolean {\n\treturn elapsed(motion, now) >= motion.generator.duration;\n}\n\nfunction clamp(value: number): number {\n\treturn Math.min(1, Math.max(0, value));\n}\n\nexport function createMotionStore(\n\tinitial: ChartScene,\n\ttiming: ChartTiming,\n): MotionStore {\n\tlet target = initial;\n\tconst numbers = new Map<string, Motion>();\n\tconst strings = new Map<string, StringMotion>();\n\tconst guides = new Map<string, GuideMotion>();\n\tconst shapes = new Map<\n\t\tstring,\n\t\t{ readonly plan: GeometryPlan; readonly motion: GeometryMotion }\n\t>();\n\tconst leaving = new Map<string, Leaving>();\n\n\tfunction generator(from: number, to: number, velocity: number): Generator {\n\t\treturn timing.type === \"spring\"\n\t\t\t? spring({\n\t\t\t\t\tfrom,\n\t\t\t\t\tto,\n\t\t\t\t\tvelocity,\n\t\t\t\t\tduration: timing.duration,\n\t\t\t\t\tbounce: timing.bounce,\n\t\t\t\t})\n\t\t\t: tween({\n\t\t\t\t\tkeyframes: [from, to],\n\t\t\t\t\tduration: timing.duration,\n\t\t\t\t\tease: timing.easing,\n\t\t\t\t});\n\t}\n\n\tfunction value(node: string, channel: string, fallback: number, now: number) {\n\t\tconst motion = numbers.get(channelKey(node, channel));\n\t\treturn motion ? motion.generator.at(elapsed(motion, now)).value : fallback;\n\t}\n\n\t/** Springs one channel from `from` to `to`, at the speed it already has. */\n\tfunction spring1(\n\t\tnode: string,\n\t\tchannel: string,\n\t\tfrom: number,\n\t\tto: number,\n\t\tstart: number,\n\t\tnow: number,\n\t) {\n\t\tconst key = channelKey(node, channel);\n\t\tconst running = numbers.get(key);\n\t\tconst velocity = running\n\t\t\t? running.generator.at(elapsed(running, now)).velocity\n\t\t\t: 0;\n\t\tnumbers.delete(key);\n\t\tif (from === to && velocity === 0) return;\n\t\tnumbers.set(key, { generator: generator(from, to, velocity), start });\n\t}\n\n\t/** Mixes one string channel from what is painted to what `to` asks for. */\n\tfunction mixString(\n\t\tfrom: SceneNode,\n\t\tto: SceneNode,\n\t\tchannel: StringChannel,\n\t\tstart: number,\n\t) {\n\t\tconst key = channelKey(to.key, channel);\n\t\tstrings.delete(key);\n\t\tconst was = stringOf(from, channel);\n\t\tconst goal = stringOf(to, channel);\n\t\tif (was === undefined || goal === undefined || was === goal) return;\n\t\tstrings.set(key, {\n\t\t\tgenerator: generator(0, 1, 0),\n\t\t\tstart,\n\t\t\tmix: stringMixer(to, channel, was, goal),\n\t\t});\n\t}\n\n\tfunction drop(node: string) {\n\t\tfor (const motions of [numbers, strings]) {\n\t\t\tfor (const key of motions.keys()) {\n\t\t\t\tif (key.startsWith(node + SEPARATOR)) motions.delete(key);\n\t\t\t}\n\t\t}\n\t\tshapes.delete(node);\n\t\tleaving.delete(node);\n\t}\n\n\t/** `from` is the node as painted now (or the shape it enters from), `to` where it goes. */\n\tfunction move(from: SceneNode, to: SceneNode, start: number, now: number) {\n\t\tconst key = to.key;\n\t\tfor (const field of FIELDS[to.kind]) {\n\t\t\tconst was = (from as unknown as Numeric)[field] as number;\n\t\t\tspring1(\n\t\t\t\tkey,\n\t\t\t\tfield,\n\t\t\t\twas,\n\t\t\t\t(to as unknown as Numeric)[field] as number,\n\t\t\t\tstart,\n\t\t\t\tnow,\n\t\t\t);\n\t\t}\n\t\tmixString(from, to, \"fill\", start);\n\t\tmixString(from, to, \"stroke\", start);\n\t\tif (to.kind !== \"path\" || from.kind !== \"path\") return;\n\t\tconst previous = shapes.get(key);\n\t\tshapes.delete(key);\n\t\tstrings.delete(channelKey(key, \"d\"));\n\t\tconst motion = to.motion;\n\t\tconst plan =\n\t\t\tmotion && from.geometry && to.geometry\n\t\t\t\t? motion.plan(from.geometry, to.geometry)\n\t\t\t\t: undefined;\n\t\tif (!plan) {\n\t\t\t// No meaning to move: the node's own morph, else the numbers of `d`.\n\t\t\tmixString(from, to, \"d\", start);\n\t\t\treturn;\n\t\t}\n\t\tfor (const channel of previous?.plan.targets.keys() ?? []) {\n\t\t\tif (!plan.targets.has(channel)) {\n\t\t\t\tnumbers.delete(channelKey(key, `g${channel}`));\n\t\t\t}\n\t\t}\n\t\tfor (const [channel, goal] of plan.targets) {\n\t\t\tconst was = plan.starts.get(channel) ?? goal;\n\t\t\tspring1(key, `g${channel}`, was, goal, start, now);\n\t\t}\n\t\tshapes.set(key, { plan, motion: motion as GeometryMotion });\n\t}\n\n\tfunction paintNode(node: SceneNode, now: number): SceneNode {\n\t\tif (node.kind === \"group\") {\n\t\t\treturn { ...node, children: paintList(node.children, node.key, now) };\n\t\t}\n\t\tconst key = node.key;\n\t\tconst moved: Numeric = {};\n\t\tfor (const field of FIELDS[node.kind]) {\n\t\t\tmoved[field] = value(\n\t\t\t\tkey,\n\t\t\t\tfield,\n\t\t\t\t(node as unknown as Numeric)[field] as number,\n\t\t\t\tnow,\n\t\t\t);\n\t\t}\n\t\tlet next = { ...node, ...moved } as Exclude<SceneNode, { kind: \"group\" }>;\n\t\tconst shape = shapes.get(key);\n\t\tif (next.kind === \"path\" && shape) {\n\t\t\tconst geometry = shape.plan.build((channel) =>\n\t\t\t\tvalue(key, `g${channel}`, shape.plan.targets.get(channel) ?? 0, now),\n\t\t\t);\n\t\t\tnext = { ...next, geometry, d: shape.motion.draw(geometry) };\n\t\t}\n\t\tconst mixed: Partial<Record<StringChannel, string>> = {};\n\t\tfor (const channel of STRING_CHANNELS) {\n\t\t\tconst motion = strings.get(channelKey(key, channel));\n\t\t\tif (motion) {\n\t\t\t\tmixed[channel] = motion.mix(\n\t\t\t\t\tmotion.generator.at(elapsed(motion, now)).value,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tnext = withStrings(next, mixed) as typeof next;\n\t\tconst opacity = value(key, OPACITY, 1, now);\n\t\tconst drawn = value(key, DRAWN, 1, now);\n\t\tif (opacity === 1 && drawn === 1) return next;\n\t\tconst geometry = next.kind === \"path\" ? next.geometry : undefined;\n\t\tconst length =\n\t\t\tgeometry && next.kind === \"path\" ? next.motion?.length?.(geometry) : 0;\n\t\treturn {\n\t\t\t...next,\n\t\t\tpaint: {\n\t\t\t\t...next.paint,\n\t\t\t\topacity: (next.paint.opacity ?? 1) * clamp(opacity),\n\t\t\t\t...(drawn < 1 && geometry\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tdrawn: {\n\t\t\t\t\t\t\t\tfraction: clamp(drawn),\n\t\t\t\t\t\t\t\tlength: length ?? 0,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\t\t\t\t\t: {}),\n\t\t\t},\n\t\t} as SceneNode;\n\t}\n\n\t/**\n\t * The painted list, with the marks still collapsing after the sibling they\n\t * followed, and the guides still fading back at their places.\n\t */\n\tfunction paintList(\n\t\tlist: readonly SceneNode[],\n\t\tparent: string | null,\n\t\tnow: number,\n\t): SceneNode[] {\n\t\tconst out: SceneNode[] = [];\n\t\tfunction place(after: string | undefined) {\n\t\t\tfor (const [key, exit] of leaving) {\n\t\t\t\tif (exit.after !== after) continue;\n\t\t\t\tout.push(paintNode(exit.node, now));\n\t\t\t\tplace(key);\n\t\t\t}\n\t\t}\n\t\tif (parent === null) place(undefined);\n\t\tfor (const node of list) {\n\t\t\tout.push(paintNode(node, now));\n\t\t\tplace(node.key);\n\t\t}\n\t\tif (guides.size === 0) return out;\n\t\tconst fading = [...guides.values()].map(\n\t\t\t(motion) =>\n\t\t\t\t[\n\t\t\t\t\tmotion.guide,\n\t\t\t\t\tMath.max(0, motion.generator.at(elapsed(motion, now)).value),\n\t\t\t\t] as const,\n\t\t);\n\t\treturn withLeaving(out, parent, fading);\n\t}\n\n\tfunction prune(now: number) {\n\t\tfor (const motions of [numbers, strings, guides]) {\n\t\t\tfor (const [key, motion] of motions) {\n\t\t\t\tif (done(motion, now)) motions.delete(key);\n\t\t\t}\n\t\t}\n\t\tconst moving = new Set(\n\t\t\t[...numbers.keys(), ...strings.keys()].map(\n\t\t\t\t(key) => key.split(SEPARATOR)[0],\n\t\t\t),\n\t\t);\n\t\tfor (const key of shapes.keys()) {\n\t\t\tif (!moving.has(key)) shapes.delete(key);\n\t\t}\n\t\tfor (const key of leaving.keys()) {\n\t\t\tif (!moving.has(key)) leaving.delete(key);\n\t\t}\n\t}\n\n\treturn {\n\t\tretarget(scene, now) {\n\t\t\tconst before = index(target.nodes);\n\t\t\tconst after = index(scene.nodes);\n\t\t\tfunction painted(key: string): SceneNode | undefined {\n\t\t\t\tconst node =\n\t\t\t\t\tbefore.byKey.get(key) ??\n\t\t\t\t\tleaving.get(key)?.node ??\n\t\t\t\t\tguides.get(key)?.guide.node;\n\t\t\t\treturn node && paintNode(node, now);\n\t\t\t}\n\t\t\t// Read before the exits below drop what the guides were showing.\n\t\t\tfor (const guide of leavingGuides(target.nodes, (key) =>\n\t\t\t\tafter.byKey.has(key),\n\t\t\t)) {\n\t\t\t\tguides.set(guide.node.key, {\n\t\t\t\t\tgenerator: generator(value(guide.node.key, OPACITY, 1, now), 0, 0),\n\t\t\t\t\tstart: now,\n\t\t\t\t\tguide,\n\t\t\t\t});\n\t\t\t}\n\t\t\tfor (const key of new Set([...before.byKey.keys(), ...leaving.keys()])) {\n\t\t\t\tif (after.byKey.has(key)) continue;\n\t\t\t\tconst base = (before.byKey.get(key) ??\n\t\t\t\t\tleaving.get(key)?.node) as SceneNode;\n\t\t\t\tconst siblings =\n\t\t\t\t\tbefore.siblings.get(key) ?? leaving.get(key)?.siblings ?? [];\n\t\t\t\tconst goal = exitTo(base, siblings, after.byKey);\n\t\t\t\tconst from = painted(key);\n\t\t\t\tif (!goal || !from) {\n\t\t\t\t\tdrop(key);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tmove(from, goal, now, now);\n\t\t\t\tconst at = siblings.findIndex((node) => node.key === key);\n\t\t\t\tleaving.set(key, {\n\t\t\t\t\tnode: goal,\n\t\t\t\t\tafter: siblings[at - 1]?.key,\n\t\t\t\t\tsiblings,\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst marks = [...after.byKey.values()].filter(\n\t\t\t\t(node) => node.role === \"mark\",\n\t\t\t);\n\t\t\tconst order = new Map(marks.map((node, at) => [node.key, at]));\n\t\t\tfor (const node of after.byKey.values()) {\n\t\t\t\tif (node.kind === \"group\") continue;\n\t\t\t\tconst start =\n\t\t\t\t\tnow +\n\t\t\t\t\tstaggerDelay(timing, order.get(node.key) ?? 0, marks.length) * 1000;\n\t\t\t\tconst old = painted(node.key);\n\t\t\t\tleaving.delete(node.key);\n\t\t\t\tconst from =\n\t\t\t\t\told ??\n\t\t\t\t\tenterFrom(node, after.siblings.get(node.key) ?? [], before.byKey);\n\t\t\t\tif (from) move(from, node, start, now);\n\t\t\t\tconst fading = numbers.get(channelKey(node.key, OPACITY));\n\t\t\t\tconst returning = guides.get(node.key);\n\t\t\t\tif ((!from && node.enter !== \"none\") || fading || returning) {\n\t\t\t\t\tconst shown = returning\n\t\t\t\t\t\t? Math.max(0, returning.generator.at(elapsed(returning, now)).value)\n\t\t\t\t\t\t: fading\n\t\t\t\t\t\t\t? value(node.key, OPACITY, 1, now)\n\t\t\t\t\t\t\t: 0;\n\t\t\t\t\tspring1(node.key, OPACITY, shown, 1, start, now);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const key of after.byKey.keys()) guides.delete(key);\n\t\t\ttarget = scene;\n\t\t},\n\t\tintroduce(now) {\n\t\t\tlet any = false;\n\t\t\twalk(target.nodes, (node) => {\n\t\t\t\tif (node.kind !== \"path\" || node.enter !== \"draw\") return;\n\t\t\t\tif (node.geometry?.kind !== \"points\" || node.paint.strokeDasharray) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tspring1(node.key, DRAWN, 0, 1, now, now);\n\t\t\t\tany = true;\n\t\t\t});\n\t\t\treturn any;\n\t\t},\n\t\tsnap(scene) {\n\t\t\tfor (const motions of [numbers, strings, guides, shapes, leaving]) {\n\t\t\t\tmotions.clear();\n\t\t\t}\n\t\t\ttarget = scene;\n\t\t},\n\t\tframe(now) {\n\t\t\tprune(now);\n\t\t\tif (numbers.size + strings.size + guides.size === 0) return target;\n\t\t\treturn { ...target, nodes: paintList(target.nodes, null, now) };\n\t\t},\n\t\tsettled(now) {\n\t\t\treturn [\n\t\t\t\t...numbers.values(),\n\t\t\t\t...strings.values(),\n\t\t\t\t...guides.values(),\n\t\t\t].every((motion) => done(motion, now));\n\t\t},\n\t};\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["geometry"]
7
+ }
@@ -0,0 +1,6 @@
1
+ import type { GeometryMotion, SceneGeometry } from "../types.js";
2
+ /** A line, an area or a radar outline through its points. */
3
+ export declare function pointsPath(geometry: SceneGeometry): string;
4
+ /** Lines, areas and radar outlines: points keyed by x (or by axis). */
5
+ export declare const POINTS_MOTION: GeometryMotion;
6
+ //# sourceMappingURL=points-motion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"points-motion.d.ts","sourceRoot":"","sources":["../../../src/core/motion/points-motion.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,cAAc,EAGd,aAAa,EAEb,MAAM,iBAAiB,CAAC;AAMzB,6DAA6D;AAC7D,wBAAgB,UAAU,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,CAkB1D;AAoJD,uEAAuE;AACvE,eAAO,MAAM,aAAa,EAAE,cAQ3B,CAAC"}
@@ -0,0 +1,140 @@
1
+ import { areaPath, linePath, polygonPath } from "../paths.js";
2
+ const POINT_FIELDS = ["x", "y", "x0", "y0"];
3
+ function pointsPath(geometry) {
4
+ if (geometry.kind !== "points") return "";
5
+ const { runs, shape, curve } = geometry;
6
+ if (shape === "polygon") return polygonPath(runs[0] ?? []);
7
+ return runs.map(
8
+ (run) => shape === "area" ? areaPath(
9
+ run,
10
+ run.map((point) => ({
11
+ x: point.x0 ?? point.x,
12
+ y: point.y0 ?? point.y
13
+ })),
14
+ curve
15
+ ) : linePath(run, curve)
16
+ ).join("");
17
+ }
18
+ function channel(key, field) {
19
+ return `${key}${field}`;
20
+ }
21
+ function fields(point, into) {
22
+ for (const field of POINT_FIELDS) {
23
+ const value = point[field];
24
+ if (value !== void 0) into.set(channel(point.key, field), value);
25
+ }
26
+ }
27
+ function as(key, point) {
28
+ return { ...point, key };
29
+ }
30
+ function neighbour(order, index, has) {
31
+ for (let at = index - 1; at >= 0; at -= 1) {
32
+ const found = has.get(order[at]);
33
+ if (found) return found;
34
+ }
35
+ for (let at = index + 1; at < order.length; at += 1) {
36
+ const found = has.get(order[at]);
37
+ if (found) return found;
38
+ }
39
+ return void 0;
40
+ }
41
+ function mergeOrder(before, after) {
42
+ const merged = [...after];
43
+ const known = new Set(after);
44
+ for (const [index, key] of before.entries()) {
45
+ if (known.has(key)) continue;
46
+ const previous = before[index - 1];
47
+ merged.splice(
48
+ previous === void 0 ? 0 : merged.indexOf(previous) + 1,
49
+ 0,
50
+ key
51
+ );
52
+ known.add(key);
53
+ }
54
+ return merged;
55
+ }
56
+ function pointsPlan(from, to) {
57
+ if (from.runs.length !== to.runs.length || from.shape !== to.shape)
58
+ return void 0;
59
+ const starts = /* @__PURE__ */ new Map();
60
+ const targets = /* @__PURE__ */ new Map();
61
+ const orders = to.runs.map((run, index) => {
62
+ const old = new Map(
63
+ (from.runs[index] ?? []).map((point) => [point.key, point])
64
+ );
65
+ const next = new Map(run.map((point) => [point.key, point]));
66
+ if (old.size !== (from.runs[index]?.length ?? 0) || next.size !== run.length) {
67
+ return void 0;
68
+ }
69
+ const order = mergeOrder([...old.keys()], [...next.keys()]);
70
+ for (const [at, key] of order.entries()) {
71
+ const target = next.get(key) ?? neighbour(order, at, next);
72
+ const start = old.get(key) ?? neighbour(order, at, old) ?? target;
73
+ if (target === void 0 || start === void 0) continue;
74
+ fields(as(key, start), starts);
75
+ fields(as(key, target), targets);
76
+ }
77
+ return order;
78
+ });
79
+ if (orders.some((order) => order === void 0)) return void 0;
80
+ return {
81
+ starts,
82
+ targets,
83
+ build(value) {
84
+ const area = to.runs.some((run) => run[0]?.y0 !== void 0);
85
+ return {
86
+ ...to,
87
+ runs: orders.map(
88
+ (order) => order.map((key) => ({
89
+ key,
90
+ x: value(channel(key, "x")),
91
+ y: value(channel(key, "y")),
92
+ ...area ? { x0: value(channel(key, "x0")), y0: value(channel(key, "y0")) } : {}
93
+ }))
94
+ )
95
+ };
96
+ }
97
+ };
98
+ }
99
+ function pointsLength(geometry) {
100
+ if (geometry.kind !== "points") return 0;
101
+ let length = 0;
102
+ for (const run of geometry.runs) {
103
+ for (let index = 1; index < run.length; index += 1) {
104
+ const a = run[index - 1];
105
+ const b = run[index];
106
+ length += Math.hypot(b.x - a.x, b.y - a.y);
107
+ }
108
+ }
109
+ return length * 1.1;
110
+ }
111
+ function collapsed(node) {
112
+ const geometry = node.geometry;
113
+ if (geometry?.kind !== "points" || geometry.runs[0]?.[0]?.y0 === void 0) {
114
+ return void 0;
115
+ }
116
+ return {
117
+ ...node,
118
+ geometry: {
119
+ ...geometry,
120
+ runs: geometry.runs.map(
121
+ (run) => run.map((point) => ({
122
+ ...point,
123
+ x: point.x0 ?? point.x,
124
+ y: point.y0 ?? point.y
125
+ }))
126
+ )
127
+ }
128
+ };
129
+ }
130
+ const POINTS_MOTION = {
131
+ plan: (from, to) => from.kind === "points" && to.kind === "points" ? pointsPlan(from, to) : void 0,
132
+ draw: pointsPath,
133
+ length: pointsLength,
134
+ enter: (node) => node.enter === "grow" ? collapsed(node) : void 0
135
+ };
136
+ export {
137
+ POINTS_MOTION,
138
+ pointsPath
139
+ };
140
+ //# sourceMappingURL=points-motion.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/core/motion/points-motion.ts"],
4
+ "sourcesContent": ["import { areaPath, linePath, polygonPath } from \"#/core/paths.ts\";\nimport type {\n\tGeometryMotion,\n\tGeometryPlan,\n\tGeometryPoint,\n\tSceneGeometry,\n\tScenePath,\n} from \"#/core/types.ts\";\n\ntype Points = Extract<SceneGeometry, { kind: \"points\" }>;\n\nconst POINT_FIELDS = [\"x\", \"y\", \"x0\", \"y0\"] as const;\n\n/** A line, an area or a radar outline through its points. */\nexport function pointsPath(geometry: SceneGeometry): string {\n\tif (geometry.kind !== \"points\") return \"\";\n\tconst { runs, shape, curve } = geometry;\n\tif (shape === \"polygon\") return polygonPath(runs[0] ?? []);\n\treturn runs\n\t\t.map((run) =>\n\t\t\tshape === \"area\"\n\t\t\t\t? areaPath(\n\t\t\t\t\t\trun,\n\t\t\t\t\t\trun.map((point) => ({\n\t\t\t\t\t\t\tx: point.x0 ?? point.x,\n\t\t\t\t\t\t\ty: point.y0 ?? point.y,\n\t\t\t\t\t\t})),\n\t\t\t\t\t\tcurve,\n\t\t\t\t\t)\n\t\t\t\t: linePath(run, curve),\n\t\t)\n\t\t.join(\"\");\n}\n\nfunction channel(key: string, field: string): string {\n\treturn `${key}\\u0001${field}`;\n}\n\nfunction fields(point: GeometryPoint, into: Map<string, number>) {\n\tfor (const field of POINT_FIELDS) {\n\t\tconst value = point[field];\n\t\tif (value !== undefined) into.set(channel(point.key, field), value);\n\t}\n}\n\n/** `point`'s numbers under `key`'s channel names: a point entering from, or folding into, a neighbour. */\nfunction as(key: string, point: GeometryPoint): GeometryPoint {\n\treturn { ...point, key };\n}\n\n/** The nearest of `order` around `index` that `has` knows: looking back first, then ahead. */\nfunction neighbour(\n\torder: readonly string[],\n\tindex: number,\n\thas: ReadonlyMap<string, GeometryPoint>,\n): GeometryPoint | undefined {\n\tfor (let at = index - 1; at >= 0; at -= 1) {\n\t\tconst found = has.get(order[at] as string);\n\t\tif (found) return found;\n\t}\n\tfor (let at = index + 1; at < order.length; at += 1) {\n\t\tconst found = has.get(order[at] as string);\n\t\tif (found) return found;\n\t}\n\treturn undefined;\n}\n\n/** The new keys, with the ones that left kept after the old point before them. */\nfunction mergeOrder(\n\tbefore: readonly string[],\n\tafter: readonly string[],\n): string[] {\n\tconst merged = [...after];\n\tconst known = new Set(after);\n\tfor (const [index, key] of before.entries()) {\n\t\tif (known.has(key)) continue;\n\t\tconst previous = before[index - 1];\n\t\tmerged.splice(\n\t\t\tprevious === undefined ? 0 : merged.indexOf(previous) + 1,\n\t\t\t0,\n\t\t\tkey,\n\t\t);\n\t\tknown.add(key);\n\t}\n\treturn merged;\n}\n\n/**\n * Points keyed by x. A point that appears enters from its nearest old\n * neighbour (a month added on the right slides out of the last one); a\n * point that leaves folds into its nearest new neighbour. Undefined when the\n * runs don't line up: the caller falls back to tweening the path.\n */\nfunction pointsPlan(from: Points, to: Points): GeometryPlan | undefined {\n\tif (from.runs.length !== to.runs.length || from.shape !== to.shape)\n\t\treturn undefined;\n\tconst starts = new Map<string, number>();\n\tconst targets = new Map<string, number>();\n\tconst orders = to.runs.map((run, index): string[] | undefined => {\n\t\tconst old = new Map(\n\t\t\t(from.runs[index] ?? []).map((point) => [point.key, point]),\n\t\t);\n\t\tconst next = new Map(run.map((point) => [point.key, point]));\n\t\t// Two points under one key can't be told apart: tween the path instead.\n\t\tif (\n\t\t\told.size !== (from.runs[index]?.length ?? 0) ||\n\t\t\tnext.size !== run.length\n\t\t) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst order = mergeOrder([...old.keys()], [...next.keys()]);\n\t\tfor (const [at, key] of order.entries()) {\n\t\t\tconst target = next.get(key) ?? neighbour(order, at, next);\n\t\t\tconst start = old.get(key) ?? neighbour(order, at, old) ?? target;\n\t\t\tif (target === undefined || start === undefined) continue;\n\t\t\tfields(as(key, start), starts);\n\t\t\tfields(as(key, target), targets);\n\t\t}\n\t\treturn order;\n\t});\n\tif (orders.some((order) => order === undefined)) return undefined;\n\treturn {\n\t\tstarts,\n\t\ttargets,\n\t\tbuild(value) {\n\t\t\tconst area = to.runs.some((run) => run[0]?.y0 !== undefined);\n\t\t\treturn {\n\t\t\t\t...to,\n\t\t\t\truns: (orders as string[][]).map((order) =>\n\t\t\t\t\torder.map((key) => ({\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tx: value(channel(key, \"x\")),\n\t\t\t\t\t\ty: value(channel(key, \"y\")),\n\t\t\t\t\t\t...(area\n\t\t\t\t\t\t\t? { x0: value(channel(key, \"x0\")), y0: value(channel(key, \"y0\")) }\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t})),\n\t\t\t\t),\n\t\t\t};\n\t\t},\n\t};\n}\n\n/** A line's length along its points: what Canvas dashes against to trace it in. */\nfunction pointsLength(geometry: SceneGeometry): number {\n\tif (geometry.kind !== \"points\") return 0;\n\tlet length = 0;\n\tfor (const run of geometry.runs) {\n\t\tfor (let index = 1; index < run.length; index += 1) {\n\t\t\tconst a = run[index - 1] as GeometryPoint;\n\t\t\tconst b = run[index] as GeometryPoint;\n\t\t\tlength += Math.hypot(b.x - a.x, b.y - a.y);\n\t\t}\n\t}\n\t// A curve runs a little longer than its chords; the dash must cover it all.\n\treturn length * 1.1;\n}\n\n/** An area, or the line over it, grows from its lower edge. */\nfunction collapsed(node: ScenePath): ScenePath | undefined {\n\tconst geometry = node.geometry;\n\t// A line over an area carries the area's lower edge too, so the two grow as one.\n\tif (geometry?.kind !== \"points\" || geometry.runs[0]?.[0]?.y0 === undefined) {\n\t\treturn undefined;\n\t}\n\treturn {\n\t\t...node,\n\t\tgeometry: {\n\t\t\t...geometry,\n\t\t\truns: geometry.runs.map((run) =>\n\t\t\t\trun.map((point) => ({\n\t\t\t\t\t...point,\n\t\t\t\t\tx: point.x0 ?? point.x,\n\t\t\t\t\ty: point.y0 ?? point.y,\n\t\t\t\t})),\n\t\t\t),\n\t\t},\n\t};\n}\n\n/** Lines, areas and radar outlines: points keyed by x (or by axis). */\nexport const POINTS_MOTION: GeometryMotion = {\n\tplan: (from, to) =>\n\t\tfrom.kind === \"points\" && to.kind === \"points\"\n\t\t\t? pointsPlan(from, to)\n\t\t\t: undefined,\n\tdraw: pointsPath,\n\tlength: pointsLength,\n\tenter: (node) => (node.enter === \"grow\" ? collapsed(node) : undefined),\n};\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,18 @@
1
+ import type { SceneNode } from "../types.js";
2
+ /** The channels that move as a string mixed by progress, not as a number. */
3
+ export declare const STRING_CHANNELS: readonly ["d", "fill", "stroke"];
4
+ export type StringChannel = (typeof STRING_CHANNELS)[number];
5
+ /**
6
+ * A colour part of the way to another, as CSS: `color-mix()` reads the
7
+ * chart's `var(--chart-1)` tokens as they are, so nothing parses colours,
8
+ * and the Canvas resolver computes the mix against the page like any
9
+ * other colour.
10
+ */
11
+ export declare function colourBetween(from: string, to: string, progress: number): string;
12
+ /** What a string channel of `node` reads now. */
13
+ export declare function stringOf(node: SceneNode | undefined, channel: StringChannel): string | undefined;
14
+ /** The mix from what was painted to what `node` asks for. */
15
+ export declare function stringMixer(node: SceneNode, channel: StringChannel, from: string, to: string): (progress: number) => string;
16
+ /** `node` with each string channel set to its moving value. */
17
+ export declare function withStrings(node: SceneNode, values: Partial<Record<StringChannel, string>>): SceneNode;
18
+ //# sourceMappingURL=strings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strings.d.ts","sourceRoot":"","sources":["../../../src/core/motion/strings.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,6EAA6E;AAC7E,eAAO,MAAM,eAAe,YAAI,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAU,CAAC;AAEhE,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAK7D;;;;;GAKG;AACH,wBAAgB,aAAa,CAC5B,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,MAAM,GACd,MAAM,CAKR;AAED,iDAAiD;AACjD,wBAAgB,QAAQ,CACvB,IAAI,EAAE,SAAS,GAAG,SAAS,EAC3B,OAAO,EAAE,aAAa,GACpB,MAAM,GAAG,SAAS,CAOpB;AAED,6DAA6D;AAC7D,wBAAgB,WAAW,CAC1B,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,aAAa,EACtB,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,GACR,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAM9B;AAED,+DAA+D;AAC/D,wBAAgB,WAAW,CAC1B,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,GAC5C,SAAS,CAQX"}
@@ -0,0 +1,34 @@
1
+ import { tweenPath } from "./tween.js";
2
+ const STRING_CHANNELS = ["d", "fill", "stroke"];
3
+ const COLOUR_STEPS = 50;
4
+ function colourBetween(from, to, progress) {
5
+ const step = Math.round(Math.min(1, Math.max(0, progress)) * COLOUR_STEPS);
6
+ if (step === 0) return from;
7
+ if (step === COLOUR_STEPS) return to;
8
+ return `color-mix(in oklab, ${to} ${step * 100 / COLOUR_STEPS}%, ${from})`;
9
+ }
10
+ function stringOf(node, channel) {
11
+ if (node === void 0 || node.kind === "group") return void 0;
12
+ return channel === "d" ? node.kind === "path" ? node.d : void 0 : node.paint[channel];
13
+ }
14
+ function stringMixer(node, channel, from, to) {
15
+ if (channel !== "d") {
16
+ return (progress) => colourBetween(from, to, progress);
17
+ }
18
+ const morph = node.kind === "path" ? node.morph : void 0;
19
+ return morph ? morph(from, to) : (progress) => tweenPath(from, to, progress);
20
+ }
21
+ function withStrings(node, values) {
22
+ if (node.kind === "group") return node;
23
+ const { d, ...colours } = values;
24
+ const shaped = d === void 0 || node.kind !== "path" ? node : { ...node, d };
25
+ return Object.keys(colours).length === 0 ? shaped : { ...shaped, paint: { ...shaped.paint, ...colours } };
26
+ }
27
+ export {
28
+ STRING_CHANNELS,
29
+ colourBetween,
30
+ stringMixer,
31
+ stringOf,
32
+ withStrings
33
+ };
34
+ //# sourceMappingURL=strings.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/core/motion/strings.ts"],
4
+ "sourcesContent": ["import { tweenPath } from \"#/core/motion/tween.ts\";\nimport type { SceneNode } from \"#/core/types.ts\";\n\n/** The channels that move as a string mixed by progress, not as a number. */\nexport const STRING_CHANNELS = [\"d\", \"fill\", \"stroke\"] as const;\n\nexport type StringChannel = (typeof STRING_CHANNELS)[number];\n\n/** 2 % steps: at most 51 colours per pair, so the Canvas resolver's cache stays small. */\nconst COLOUR_STEPS = 50;\n\n/**\n * A colour part of the way to another, as CSS: `color-mix()` reads the\n * chart's `var(--chart-1)` tokens as they are, so nothing parses colours,\n * and the Canvas resolver computes the mix against the page like any\n * other colour.\n */\nexport function colourBetween(\n\tfrom: string,\n\tto: string,\n\tprogress: number,\n): string {\n\tconst step = Math.round(Math.min(1, Math.max(0, progress)) * COLOUR_STEPS);\n\tif (step === 0) return from;\n\tif (step === COLOUR_STEPS) return to;\n\treturn `color-mix(in oklab, ${to} ${(step * 100) / COLOUR_STEPS}%, ${from})`;\n}\n\n/** What a string channel of `node` reads now. */\nexport function stringOf(\n\tnode: SceneNode | undefined,\n\tchannel: StringChannel,\n): string | undefined {\n\tif (node === undefined || node.kind === \"group\") return undefined;\n\treturn channel === \"d\"\n\t\t? node.kind === \"path\"\n\t\t\t? node.d\n\t\t\t: undefined\n\t\t: node.paint[channel];\n}\n\n/** The mix from what was painted to what `node` asks for. */\nexport function stringMixer(\n\tnode: SceneNode,\n\tchannel: StringChannel,\n\tfrom: string,\n\tto: string,\n): (progress: number) => string {\n\tif (channel !== \"d\") {\n\t\treturn (progress) => colourBetween(from, to, progress);\n\t}\n\tconst morph = node.kind === \"path\" ? node.morph : undefined;\n\treturn morph ? morph(from, to) : (progress) => tweenPath(from, to, progress);\n}\n\n/** `node` with each string channel set to its moving value. */\nexport function withStrings(\n\tnode: SceneNode,\n\tvalues: Partial<Record<StringChannel, string>>,\n): SceneNode {\n\tif (node.kind === \"group\") return node;\n\tconst { d, ...colours } = values;\n\tconst shaped =\n\t\td === undefined || node.kind !== \"path\" ? node : { ...node, d };\n\treturn Object.keys(colours).length === 0\n\t\t? shaped\n\t\t: ({ ...shaped, paint: { ...shaped.paint, ...colours } } as SceneNode);\n}\n"],
5
+ "mappings": "AAAA,SAAS,iBAAiB;AAInB,MAAM,kBAAkB,CAAC,KAAK,QAAQ,QAAQ;AAKrD,MAAM,eAAe;AAQd,SAAS,cACf,MACA,IACA,UACS;AACT,QAAM,OAAO,KAAK,MAAM,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,QAAQ,CAAC,IAAI,YAAY;AACzE,MAAI,SAAS,EAAG,QAAO;AACvB,MAAI,SAAS,aAAc,QAAO;AAClC,SAAO,uBAAuB,EAAE,IAAK,OAAO,MAAO,YAAY,MAAM,IAAI;AAC1E;AAGO,SAAS,SACf,MACA,SACqB;AACrB,MAAI,SAAS,UAAa,KAAK,SAAS,QAAS,QAAO;AACxD,SAAO,YAAY,MAChB,KAAK,SAAS,SACb,KAAK,IACL,SACD,KAAK,MAAM,OAAO;AACtB;AAGO,SAAS,YACf,MACA,SACA,MACA,IAC+B;AAC/B,MAAI,YAAY,KAAK;AACpB,WAAO,CAAC,aAAa,cAAc,MAAM,IAAI,QAAQ;AAAA,EACtD;AACA,QAAM,QAAQ,KAAK,SAAS,SAAS,KAAK,QAAQ;AAClD,SAAO,QAAQ,MAAM,MAAM,EAAE,IAAI,CAAC,aAAa,UAAU,MAAM,IAAI,QAAQ;AAC5E;AAGO,SAAS,YACf,MACA,QACY;AACZ,MAAI,KAAK,SAAS,QAAS,QAAO;AAClC,QAAM,EAAE,GAAG,GAAG,QAAQ,IAAI;AAC1B,QAAM,SACL,MAAM,UAAa,KAAK,SAAS,SAAS,OAAO,EAAE,GAAG,MAAM,EAAE;AAC/D,SAAO,OAAO,KAAK,OAAO,EAAE,WAAW,IACpC,SACC,EAAE,GAAG,QAAQ,OAAO,EAAE,GAAG,OAAO,OAAO,GAAG,QAAQ,EAAE;AACzD;",
6
+ "names": []
7
+ }
@@ -0,0 +1,30 @@
1
+ import type { EasingName } from "@voila.dev/motion";
2
+ /** How a data update moves: what `<Chart animate>` accepts beyond `true`, `false` and a duration. */
3
+ export type ChartAnimation = {
4
+ readonly type?: "spring";
5
+ /** Perceived duration, in milliseconds. */
6
+ readonly duration?: number;
7
+ /** 0 never overshoots (the default: a bar past its value shows a number the data doesn't have); up to 0.5. */
8
+ readonly bounce?: number;
9
+ /** Milliseconds between marks in focus order, capped at 300 ms in all. */
10
+ readonly stagger?: number;
11
+ } | {
12
+ readonly type: "tween";
13
+ readonly duration?: number;
14
+ readonly easing?: EasingName;
15
+ readonly stagger?: number;
16
+ };
17
+ export interface ChartTiming {
18
+ readonly type: "spring" | "tween";
19
+ /** Seconds, as the motion package counts them. */
20
+ readonly duration: number;
21
+ readonly bounce: number;
22
+ readonly easing: EasingName;
23
+ /** Seconds between neighbours. */
24
+ readonly stagger: number;
25
+ }
26
+ /** The timing an `animate` prop means, or `null` to snap. */
27
+ export declare function chartTiming(animate: boolean | number | ChartAnimation): ChartTiming | null;
28
+ /** The delay of the `index`-th of `count` marks: the gap shrinks so the last one never waits more than 300 ms. */
29
+ export declare function staggerDelay(timing: ChartTiming, index: number, count: number): number;
30
+ //# sourceMappingURL=timing.d.ts.map