@voila.dev/chart 5.0.118 → 6.0.121

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 (43) hide show
  1. package/dist/core/motion/motion-store.d.ts.map +1 -1
  2. package/dist/core/motion/motion-store.js +65 -105
  3. package/dist/core/motion/motion-store.js.map +3 -3
  4. package/dist/core/motion/points-motion.d.ts.map +1 -1
  5. package/dist/core/motion/points-motion.js +9 -22
  6. package/dist/core/motion/points-motion.js.map +2 -2
  7. package/dist/core/motion/strings.d.ts +4 -2
  8. package/dist/core/motion/strings.d.ts.map +1 -1
  9. package/dist/core/motion/strings.js +5 -6
  10. package/dist/core/motion/strings.js.map +2 -2
  11. package/dist/core/motion/timing.d.ts +16 -18
  12. package/dist/core/motion/timing.d.ts.map +1 -1
  13. package/dist/core/motion/timing.js +9 -8
  14. package/dist/core/motion/timing.js.map +2 -2
  15. package/dist/core/motion/tween-timing.d.ts +17 -0
  16. package/dist/core/motion/tween-timing.d.ts.map +1 -0
  17. package/dist/core/motion/tween-timing.js +22 -0
  18. package/dist/core/motion/tween-timing.js.map +7 -0
  19. package/dist/index.d.ts +2 -1
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +2 -0
  22. package/dist/index.js.map +2 -2
  23. package/dist/react/chart.d.ts +3 -2
  24. package/dist/react/chart.d.ts.map +1 -1
  25. package/dist/react/chart.js +3 -1
  26. package/dist/react/chart.js.map +2 -2
  27. package/dist/react/use-animated-scene.d.ts.map +1 -1
  28. package/dist/react/use-animated-scene.js +4 -4
  29. package/dist/react/use-animated-scene.js.map +2 -2
  30. package/package.json +2 -2
  31. package/src/core/motion/motion-store.ts +93 -139
  32. package/src/core/motion/points-motion.ts +12 -24
  33. package/src/core/motion/strings.ts +9 -12
  34. package/src/core/motion/timing.ts +28 -33
  35. package/src/core/motion/tween-timing.ts +35 -0
  36. package/src/index.ts +2 -0
  37. package/src/react/chart.tsx +9 -4
  38. package/src/react/use-animated-scene.ts +6 -4
  39. package/dist/core/motion/leaving.d.ts +0 -12
  40. package/dist/core/motion/leaving.d.ts.map +0 -1
  41. package/dist/core/motion/leaving.js +0 -33
  42. package/dist/core/motion/leaving.js.map +0 -7
  43. package/src/core/motion/leaving.ts +0 -55
@@ -1 +1 @@
1
- {"version":3,"file":"motion-store.d.ts","sourceRoot":"","sources":["../../../src/core/motion/motion-store.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,KAAK,WAAW,EAAgB,MAAM,yBAAyB,CAAC;AACzE,OAAO,KAAK,EACX,UAAU,EAIV,MAAM,iBAAiB,CAAC;AAEzB;;;;;;;;GAQG;AACH,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/C,8FAA8F;IAC9F,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAChC,qDAAqD;IACrD,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAAC;IAC/B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAC9B;AA4ED,wBAAgB,iBAAiB,CAChC,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,WAAW,GACjB,WAAW,CA0Ub"}
1
+ {"version":3,"file":"motion-store.d.ts","sourceRoot":"","sources":["../../../src/core/motion/motion-store.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,KAAK,WAAW,EAAgB,MAAM,yBAAyB,CAAC;AACzE,OAAO,KAAK,EACX,UAAU,EAIV,MAAM,iBAAiB,CAAC;AAEzB;;;;;;;;GAQG;AACH,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/C,8FAA8F;IAC9F,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAChC,qDAAqD;IACrD,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAAC;IAC/B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAC9B;AAiGD,wBAAgB,iBAAiB,CAChC,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,WAAW,GACjB,WAAW,CA4Qb"}
@@ -1,14 +1,9 @@
1
- import { spring, tween } from "@voila.dev/motion";
2
1
  import { enterFrom, exitTo } from "./enter-exit.js";
3
- import {
4
- leavingGuides,
5
- withLeaving
6
- } from "./leaving.js";
7
2
  import {
8
3
  STRING_CHANNELS,
9
4
  stringMixer,
10
5
  stringOf,
11
- withStrings
6
+ withString
12
7
  } from "./strings.js";
13
8
  import { staggerDelay } from "./timing.js";
14
9
  const FIELDS = {
@@ -22,20 +17,28 @@ const FIELDS = {
22
17
  const OPACITY = "opacity";
23
18
  const DRAWN = "drawn";
24
19
  const SEPARATOR = "\0";
25
- function walk(nodes, visit) {
20
+ const GUIDE_ROLES = /* @__PURE__ */ new Set(["grid", "axis"]);
21
+ function first(parent) {
22
+ return parent === null ? void 0 : parent + SEPARATOR;
23
+ }
24
+ function walk(nodes, visit, parent = null) {
25
+ let after = first(parent);
26
26
  for (const node of nodes) {
27
- visit(node, nodes);
28
- if (node.kind === "group") walk(node.children, visit);
27
+ visit(node, nodes, after);
28
+ if (node.kind === "group") walk(node.children, visit, node.key);
29
+ after = node.key;
29
30
  }
30
31
  }
31
32
  function index(nodes) {
32
33
  const byKey = /* @__PURE__ */ new Map();
33
34
  const siblings = /* @__PURE__ */ new Map();
34
- walk(nodes, (node, list) => {
35
+ const afters = /* @__PURE__ */ new Map();
36
+ walk(nodes, (node, list, after) => {
35
37
  byKey.set(node.key, node);
36
38
  siblings.set(node.key, list);
39
+ afters.set(node.key, after);
37
40
  });
38
- return { byKey, siblings };
41
+ return { byKey, siblings, afters };
39
42
  }
40
43
  function channelKey(node, channel) {
41
44
  return `${node}${SEPARATOR}${channel}`;
@@ -43,6 +46,9 @@ function channelKey(node, channel) {
43
46
  function elapsed(motion, now) {
44
47
  return Math.max(0, (now - motion.start) / 1e3);
45
48
  }
49
+ function sample(motion, now) {
50
+ return motion.generator.at(elapsed(motion, now));
51
+ }
46
52
  function done(motion, now) {
47
53
  return elapsed(motion, now) >= motion.generator.duration;
48
54
  }
@@ -51,53 +57,36 @@ function clamp(value) {
51
57
  }
52
58
  function createMotionStore(initial, timing) {
53
59
  let target = initial;
54
- const numbers = /* @__PURE__ */ new Map();
55
- const strings = /* @__PURE__ */ new Map();
56
- const guides = /* @__PURE__ */ new Map();
60
+ const channels = /* @__PURE__ */ new Map();
57
61
  const shapes = /* @__PURE__ */ new Map();
58
62
  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
63
  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;
64
+ const motion = channels.get(channelKey(node, channel));
65
+ return motion ? sample(motion, now).value : fallback;
75
66
  }
76
67
  function spring1(node, channel, from, to, start, now) {
77
68
  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);
69
+ const running = channels.get(key);
70
+ const velocity = running ? sample(running, now).velocity : 0;
71
+ channels.delete(key);
81
72
  if (from === to && velocity === 0) return;
82
- numbers.set(key, { generator: generator(from, to, velocity), start });
73
+ channels.set(key, { generator: timing.motion(from, to, velocity), start });
83
74
  }
84
75
  function mixString(from, to, channel, start) {
85
76
  const key = channelKey(to.key, channel);
86
- strings.delete(key);
77
+ channels.delete(key);
87
78
  const was = stringOf(from, channel);
88
79
  const goal = stringOf(to, channel);
89
80
  if (was === void 0 || goal === void 0 || was === goal) return;
90
- strings.set(key, {
91
- generator: generator(0, 1, 0),
81
+ channels.set(key, {
82
+ generator: timing.motion(0, 1, 0),
92
83
  start,
93
84
  mix: stringMixer(to, channel, was, goal)
94
85
  });
95
86
  }
96
87
  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
- }
88
+ for (const key of channels.keys()) {
89
+ if (key.startsWith(node + SEPARATOR)) channels.delete(key);
101
90
  }
102
91
  shapes.delete(node);
103
92
  leaving.delete(node);
@@ -120,7 +109,7 @@ function createMotionStore(initial, timing) {
120
109
  if (to.kind !== "path" || from.kind !== "path") return;
121
110
  const previous = shapes.get(key);
122
111
  shapes.delete(key);
123
- strings.delete(channelKey(key, "d"));
112
+ channels.delete(channelKey(key, "d"));
124
113
  const motion = to.motion;
125
114
  const plan = motion && from.geometry && to.geometry ? motion.plan(from.geometry, to.geometry) : void 0;
126
115
  if (!plan) {
@@ -129,7 +118,7 @@ function createMotionStore(initial, timing) {
129
118
  }
130
119
  for (const channel of previous?.plan.targets.keys() ?? []) {
131
120
  if (!plan.targets.has(channel)) {
132
- numbers.delete(channelKey(key, `g${channel}`));
121
+ channels.delete(channelKey(key, `g${channel}`));
133
122
  }
134
123
  }
135
124
  for (const [channel, goal] of plan.targets) {
@@ -155,37 +144,31 @@ function createMotionStore(initial, timing) {
155
144
  let next = { ...node, ...moved };
156
145
  const shape = shapes.get(key);
157
146
  if (next.kind === "path" && shape) {
158
- const geometry2 = shape.plan.build(
147
+ const geometry = shape.plan.build(
159
148
  (channel) => value(key, `g${channel}`, shape.plan.targets.get(channel) ?? 0, now)
160
149
  );
161
- next = { ...next, geometry: geometry2, d: shape.motion.draw(geometry2) };
150
+ next = { ...next, geometry, d: shape.motion.draw(geometry) };
162
151
  }
163
- const mixed = {};
164
152
  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
- );
153
+ const motion = channels.get(channelKey(key, channel));
154
+ if (motion?.mix) {
155
+ next = withString(next, channel, motion.mix(sample(motion, now).value));
170
156
  }
171
157
  }
172
- next = withStrings(next, mixed);
173
158
  const opacity = value(key, OPACITY, 1, now);
174
159
  const drawn = value(key, DRAWN, 1, now);
175
160
  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
161
  return {
179
162
  ...next,
180
163
  paint: {
181
164
  ...next.paint,
182
165
  opacity: (next.paint.opacity ?? 1) * clamp(opacity),
183
- ...drawn < 1 && geometry ? {
166
+ ...drawn < 1 && next.kind === "path" && next.geometry && {
184
167
  drawn: {
185
168
  fraction: clamp(drawn),
186
- length: length ?? 0
169
+ length: next.motion?.length?.(next.geometry) ?? 0
187
170
  }
188
- } : {}
171
+ }
189
172
  }
190
173
  };
191
174
  }
@@ -198,36 +181,24 @@ function createMotionStore(initial, timing) {
198
181
  place(key);
199
182
  }
200
183
  }
201
- if (parent === null) place(void 0);
184
+ place(first(parent));
202
185
  for (const node of list) {
203
186
  out.push(paintNode(node, now));
204
187
  place(node.key);
205
188
  }
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);
189
+ return out;
214
190
  }
215
191
  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
- }
192
+ for (const [key, motion] of channels) {
193
+ if (done(motion, now)) channels.delete(key);
220
194
  }
221
195
  const moving = new Set(
222
- [...numbers.keys(), ...strings.keys()].map(
223
- (key) => key.split(SEPARATOR)[0]
224
- )
196
+ [...channels.keys()].map((key) => key.split(SEPARATOR)[0])
225
197
  );
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);
198
+ for (const byNode of [shapes, leaving]) {
199
+ for (const key of byNode.keys()) {
200
+ if (!moving.has(key)) byNode.delete(key);
201
+ }
231
202
  }
232
203
  }
233
204
  return {
@@ -235,34 +206,28 @@ function createMotionStore(initial, timing) {
235
206
  const before = index(target.nodes);
236
207
  const after = index(scene.nodes);
237
208
  function painted(key) {
238
- const node = before.byKey.get(key) ?? leaving.get(key)?.node ?? guides.get(key)?.guide.node;
209
+ const node = before.byKey.get(key) ?? leaving.get(key)?.node;
239
210
  return node && paintNode(node, now);
240
211
  }
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
212
  for (const key of /* @__PURE__ */ new Set([...before.byKey.keys(), ...leaving.keys()])) {
252
213
  if (after.byKey.has(key)) continue;
253
214
  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);
215
+ const exit = leaving.get(key);
216
+ const siblings = exit?.siblings ?? before.siblings.get(key) ?? [];
217
+ const guide = base.kind !== "group" && GUIDE_ROLES.has(base.role ?? "");
218
+ const goal = exitTo(base, siblings, after.byKey) ?? (guide && base);
256
219
  const from = painted(key);
257
220
  if (!goal || !from) {
258
221
  drop(key);
259
222
  continue;
260
223
  }
261
224
  move(from, goal, now, now);
262
- const at = siblings.findIndex((node) => node.key === key);
225
+ if (guide) {
226
+ spring1(key, OPACITY, value(key, OPACITY, 1, now), 0, now, now);
227
+ }
263
228
  leaving.set(key, {
264
229
  node: goal,
265
- after: siblings[at - 1]?.key,
230
+ after: exit ? exit.after : before.afters.get(key),
266
231
  siblings
267
232
  });
268
233
  }
@@ -274,17 +239,16 @@ function createMotionStore(initial, timing) {
274
239
  if (node.kind === "group") continue;
275
240
  const start = now + staggerDelay(timing, order.get(node.key) ?? 0, marks.length) * 1e3;
276
241
  const old = painted(node.key);
277
- leaving.delete(node.key);
242
+ const returning = leaving.delete(node.key);
278
243
  const from = old ?? enterFrom(node, after.siblings.get(node.key) ?? [], before.byKey);
279
244
  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;
245
+ const fading = channels.get(channelKey(node.key, OPACITY));
246
+ if (!from && node.enter !== "none" || fading) {
247
+ const shown = fading ? Math.max(0, sample(fading, now).value) : 0;
248
+ if (returning) channels.delete(channelKey(node.key, OPACITY));
284
249
  spring1(node.key, OPACITY, shown, 1, start, now);
285
250
  }
286
251
  }
287
- for (const key of after.byKey.keys()) guides.delete(key);
288
252
  target = scene;
289
253
  },
290
254
  introduce(now) {
@@ -300,22 +264,18 @@ function createMotionStore(initial, timing) {
300
264
  return any;
301
265
  },
302
266
  snap(scene) {
303
- for (const motions of [numbers, strings, guides, shapes, leaving]) {
267
+ for (const motions of [channels, shapes, leaving]) {
304
268
  motions.clear();
305
269
  }
306
270
  target = scene;
307
271
  },
308
272
  frame(now) {
309
273
  prune(now);
310
- if (numbers.size + strings.size + guides.size === 0) return target;
274
+ if (channels.size === 0) return target;
311
275
  return { ...target, nodes: paintList(target.nodes, null, now) };
312
276
  },
313
277
  settled(now) {
314
- return [
315
- ...numbers.values(),
316
- ...strings.values(),
317
- ...guides.values()
318
- ].every((motion) => done(motion, now));
278
+ return [...channels.values()].every((motion) => done(motion, now));
319
279
  }
320
280
  };
321
281
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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"]
4
+ "sourcesContent": ["import type { Generator } from \"@voila.dev/motion\";\nimport { enterFrom, exitTo } from \"#/core/motion/enter-exit.ts\";\nimport {\n\tSTRING_CHANNELS,\n\ttype StringChannel,\n\tstringMixer,\n\tstringOf,\n\twithString,\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\t/** A path or a colour: the generator runs a progress from 0 to 1, and this reads it. */\n\treadonly mix?: (progress: number) => string;\n}\n\n/**\n * A node on its way out, drawn after its old sibling until its springs\n * settle: a data mark collapsing, or a guide fading where it stood.\n */\ninterface Leaving {\n\treadonly node: SceneNode;\n\t/** Its old previous sibling's key, else its group's `first` marker. */\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/**\n * A tick label or a grid line vanishing at once reads as a glitch, so they\n * fade out. Data marks never linger: a bar the data no longer has would show\n * a value that isn't true.\n */\nconst GUIDE_ROLES = new Set([\"grid\", \"axis\"]);\n\n/** Where the leaving first children of the group `parent` are drawn. */\nfunction first(parent: string | null): string | undefined {\n\treturn parent === null ? undefined : parent + SEPARATOR;\n}\n\n/** Visits every node with its list and the key it is drawn after when it leaves (see `Leaving`). */\nfunction walk(\n\tnodes: readonly SceneNode[],\n\tvisit: (\n\t\tnode: SceneNode,\n\t\tsiblings: readonly SceneNode[],\n\t\tafter: string | undefined,\n\t) => void,\n\tparent: string | null = null,\n) {\n\tlet after = first(parent);\n\tfor (const node of nodes) {\n\t\tvisit(node, nodes, after);\n\t\tif (node.kind === \"group\") walk(node.children, visit, node.key);\n\t\tafter = node.key;\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\tconst afters = new Map<string, string | undefined>();\n\twalk(nodes, (node, list, after) => {\n\t\tbyKey.set(node.key, node);\n\t\tsiblings.set(node.key, list);\n\t\tafters.set(node.key, after);\n\t});\n\treturn { byKey, siblings, afters };\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 sample(motion: Motion, now: number) {\n\treturn motion.generator.at(elapsed(motion, now));\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 channels = new Map<string, Motion>();\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 value(node: string, channel: string, fallback: number, now: number) {\n\t\tconst motion = channels.get(channelKey(node, channel));\n\t\treturn motion ? sample(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 = channels.get(key);\n\t\tconst velocity = running ? sample(running, now).velocity : 0;\n\t\tchannels.delete(key);\n\t\tif (from === to && velocity === 0) return;\n\t\tchannels.set(key, { generator: timing.motion(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\tchannels.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\tchannels.set(key, {\n\t\t\tgenerator: timing.motion(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 key of channels.keys()) {\n\t\t\tif (key.startsWith(node + SEPARATOR)) channels.delete(key);\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\tchannels.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\tchannels.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\tfor (const channel of STRING_CHANNELS) {\n\t\t\tconst motion = channels.get(channelKey(key, channel));\n\t\t\tif (motion?.mix) {\n\t\t\t\tnext = withString(next, channel, motion.mix(sample(motion, now).value));\n\t\t\t}\n\t\t}\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\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 &&\n\t\t\t\t\tnext.kind === \"path\" &&\n\t\t\t\t\tnext.geometry && {\n\t\t\t\t\t\tdrawn: {\n\t\t\t\t\t\t\tfraction: clamp(drawn),\n\t\t\t\t\t\t\tlength: next.motion?.length?.(next.geometry) ?? 0,\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/** The painted list, with the nodes still leaving after the sibling they followed. */\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\tplace(first(parent));\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\treturn out;\n\t}\n\n\tfunction prune(now: number) {\n\t\tfor (const [key, motion] of channels) {\n\t\t\tif (done(motion, now)) channels.delete(key);\n\t\t}\n\t\tconst moving = new Set(\n\t\t\t[...channels.keys()].map((key) => key.split(SEPARATOR)[0]),\n\t\t);\n\t\tfor (const byNode of [shapes, leaving]) {\n\t\t\tfor (const key of byNode.keys()) {\n\t\t\t\tif (!moving.has(key)) byNode.delete(key);\n\t\t\t}\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 = before.byKey.get(key) ?? leaving.get(key)?.node;\n\t\t\t\treturn node && paintNode(node, now);\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 exit = leaving.get(key);\n\t\t\t\tconst siblings = exit?.siblings ?? before.siblings.get(key) ?? [];\n\t\t\t\t// A whole guide group leaving goes at once, its lines with it.\n\t\t\t\tconst guide = base.kind !== \"group\" && GUIDE_ROLES.has(base.role ?? \"\");\n\t\t\t\tconst goal = exitTo(base, siblings, after.byKey) ?? (guide && base);\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\tif (guide) {\n\t\t\t\t\tspring1(key, OPACITY, value(key, OPACITY, 1, now), 0, now, now);\n\t\t\t\t}\n\t\t\t\tleaving.set(key, {\n\t\t\t\t\tnode: goal,\n\t\t\t\t\tafter: exit ? exit.after : before.afters.get(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\tconst returning = leaving.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 = channels.get(channelKey(node.key, OPACITY));\n\t\t\t\tif ((!from && node.enter !== \"none\") || fading) {\n\t\t\t\t\tconst shown = fading ? Math.max(0, sample(fading, now).value) : 0;\n\t\t\t\t\t// A guide coming back starts from rest: its fade-out speed points the wrong way.\n\t\t\t\t\tif (returning) channels.delete(channelKey(node.key, OPACITY));\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\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 [channels, 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 (channels.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 [...channels.values()].every((motion) => done(motion, now));\n\t\t},\n\t};\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
7
  }
@@ -1 +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"}
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;AAWzB,6DAA6D;AAC7D,wBAAgB,UAAU,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,CAW1D;AA0ID,uEAAuE;AACvE,eAAO,MAAM,aAAa,EAAE,cAQ3B,CAAC"}
@@ -1,32 +1,25 @@
1
1
  import { areaPath, linePath, polygonPath } from "../paths.js";
2
2
  const POINT_FIELDS = ["x", "y", "x0", "y0"];
3
+ function lowerEdge(point) {
4
+ return { ...point, x: point.x0 ?? point.x, y: point.y0 ?? point.y };
5
+ }
3
6
  function pointsPath(geometry) {
4
7
  if (geometry.kind !== "points") return "";
5
8
  const { runs, shape, curve } = geometry;
6
9
  if (shape === "polygon") return polygonPath(runs[0] ?? []);
7
10
  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)
11
+ (run) => shape === "area" ? areaPath(run, run.map(lowerEdge), curve) : linePath(run, curve)
16
12
  ).join("");
17
13
  }
18
14
  function channel(key, field) {
19
15
  return `${key}${field}`;
20
16
  }
21
- function fields(point, into) {
17
+ function fields(key, point, into) {
22
18
  for (const field of POINT_FIELDS) {
23
19
  const value = point[field];
24
- if (value !== void 0) into.set(channel(point.key, field), value);
20
+ if (value !== void 0) into.set(channel(key, field), value);
25
21
  }
26
22
  }
27
- function as(key, point) {
28
- return { ...point, key };
29
- }
30
23
  function neighbour(order, index, has) {
31
24
  for (let at = index - 1; at >= 0; at -= 1) {
32
25
  const found = has.get(order[at]);
@@ -71,8 +64,8 @@ function pointsPlan(from, to) {
71
64
  const target = next.get(key) ?? neighbour(order, at, next);
72
65
  const start = old.get(key) ?? neighbour(order, at, old) ?? target;
73
66
  if (target === void 0 || start === void 0) continue;
74
- fields(as(key, start), starts);
75
- fields(as(key, target), targets);
67
+ fields(key, start, starts);
68
+ fields(key, target, targets);
76
69
  }
77
70
  return order;
78
71
  });
@@ -117,13 +110,7 @@ function collapsed(node) {
117
110
  ...node,
118
111
  geometry: {
119
112
  ...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
- )
113
+ runs: geometry.runs.map((run) => run.map(lowerEdge))
127
114
  }
128
115
  };
129
116
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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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",
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/** Where `point` sits on its area's lower edge. */\nfunction lowerEdge(point: GeometryPoint): GeometryPoint {\n\treturn { ...point, x: point.x0 ?? point.x, y: point.y0 ?? point.y };\n}\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(run, run.map(lowerEdge), curve)\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\n/** `point`'s numbers under `key`'s channel names: `point` may be a neighbour that `key` enters from or folds into. */\nfunction fields(key: string, 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(key, field), value);\n\t}\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(key, start, starts);\n\t\t\tfields(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) => run.map(lowerEdge)),\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
6
  "names": []
7
7
  }
@@ -13,6 +13,8 @@ export declare function colourBetween(from: string, to: string, progress: number
13
13
  export declare function stringOf(node: SceneNode | undefined, channel: StringChannel): string | undefined;
14
14
  /** The mix from what was painted to what `node` asks for. */
15
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;
16
+ /** `node` with one string channel set to its moving value. */
17
+ export declare function withString<Node extends Exclude<SceneNode, {
18
+ kind: "group";
19
+ }>>(node: Node, channel: StringChannel, value: string): Node;
18
20
  //# sourceMappingURL=strings.d.ts.map
@@ -1 +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"}
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,8DAA8D;AAC9D,wBAAgB,UAAU,CAAC,IAAI,SAAS,OAAO,CAAC,SAAS,EAAE;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,EAC5E,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,MAAM,GACX,IAAI,CAIN"}
@@ -18,17 +18,16 @@ function stringMixer(node, channel, from, to) {
18
18
  const morph = node.kind === "path" ? node.morph : void 0;
19
19
  return morph ? morph(from, to) : (progress) => tweenPath(from, to, progress);
20
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 } };
21
+ function withString(node, channel, value) {
22
+ if (channel !== "d")
23
+ return { ...node, paint: { ...node.paint, [channel]: value } };
24
+ return node.kind === "path" ? { ...node, d: value } : node;
26
25
  }
27
26
  export {
28
27
  STRING_CHANNELS,
29
28
  colourBetween,
30
29
  stringMixer,
31
30
  stringOf,
32
- withStrings
31
+ withString
33
32
  };
34
33
  //# sourceMappingURL=strings.js.map