phoria2 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/README.md +11 -9
  2. package/dist/bloom.js +4 -4
  3. package/dist/bloom.js.map +1 -1
  4. package/dist/charts.js +527 -510
  5. package/dist/charts.js.map +1 -1
  6. package/dist/core.js +1760 -1170
  7. package/dist/core.js.map +1 -1
  8. package/dist/debug.js +297 -0
  9. package/dist/debug.js.map +1 -0
  10. package/dist/edit.js +60 -60
  11. package/dist/edit.js.map +1 -1
  12. package/dist/explain.js +32 -13
  13. package/dist/explain.js.map +1 -1
  14. package/dist/gltf.js +155 -155
  15. package/dist/gltf.js.map +1 -1
  16. package/dist/keyboard.js +188 -0
  17. package/dist/keyboard.js.map +1 -0
  18. package/dist/motion.js +35 -35
  19. package/dist/motion.js.map +1 -1
  20. package/dist/particles.js +187 -114
  21. package/dist/particles.js.map +1 -1
  22. package/dist/passes.js +116 -104
  23. package/dist/passes.js.map +1 -1
  24. package/dist/phoria2.js +1 -1
  25. package/dist/picking.js +95 -62
  26. package/dist/picking.js.map +1 -1
  27. package/dist/snapshot.js +1 -1
  28. package/dist/tooltip.js +1 -1
  29. package/dist/types/View.d.ts +27 -0
  30. package/dist/types/area.d.ts +18 -0
  31. package/dist/types/debug.d.ts +74 -0
  32. package/dist/types/explain.d.ts +4 -0
  33. package/dist/types/frame.d.ts +5 -0
  34. package/dist/types/geometry/Geometry.d.ts +16 -0
  35. package/dist/types/index.d.ts +2 -0
  36. package/dist/types/material/Material.d.ts +67 -0
  37. package/dist/types/material/Texture.d.ts +18 -2
  38. package/dist/types/particles/Emitter.d.ts +39 -2
  39. package/dist/types/scene/drawables.d.ts +23 -2
  40. package/dist/types/watch.d.ts +8 -1
  41. package/dist/types/webgl/hooks.d.ts +48 -0
  42. package/dist/types/webgl/passes.d.ts +4 -0
  43. package/dist/types/webgl/programs.d.ts +15 -5
  44. package/dist/types/webgl/resources.d.ts +12 -2
  45. package/dist/types/webgl/shaders.d.ts +41 -5
  46. package/llms-full.txt +275 -1
  47. package/llms.txt +1 -1
  48. package/package.json +1 -1
  49. package/schema/scene.schema.json +75 -0
  50. package/src/View.js +121 -7
  51. package/src/area.js +131 -0
  52. package/src/charts/marks.js +29 -2
  53. package/src/debug.js +283 -0
  54. package/src/explain.js +19 -0
  55. package/src/frame.js +7 -2
  56. package/src/geometry/Geometry.js +16 -1
  57. package/src/index.js +1 -0
  58. package/src/material/Material.js +130 -4
  59. package/src/material/Texture.js +55 -2
  60. package/src/particles/Emitter.js +121 -10
  61. package/src/scene/drawables.js +89 -8
  62. package/src/watch.js +26 -3
  63. package/src/webgl/WebGLRenderer.js +55 -7
  64. package/src/webgl/hooks.js +121 -0
  65. package/src/webgl/passes.js +17 -2
  66. package/src/webgl/picking.js +45 -3
  67. package/src/webgl/programs.js +20 -9
  68. package/src/webgl/resources.js +169 -15
  69. package/src/webgl/shaders.js +545 -38
package/dist/motion.js CHANGED
@@ -1,4 +1,4 @@
1
- import { At as e, Ct as t, Dt as n, Et as r, Mt as i, Ot as a, Tt as o, V as s, dt as c, pt as l, r as u } from "./core.js";
1
+ import { At as e, Ft as t, Lt as n, Mt as r, Nt as i, Ot as a, W as o, gt as s, jt as c, r as l, vt as u } from "./core.js";
2
2
  // src/motion/Timeline.js
3
3
  var d = ["loop", "speed"], f = {
4
4
  repeat: "use 'loop: true'",
@@ -16,17 +16,17 @@ var d = ["loop", "speed"], f = {
16
16
  #i = [];
17
17
  loop = !1;
18
18
  speed = 1;
19
- constructor(e = {}) {
20
- n("Timeline", e, d, f), e.loop !== void 0 && (this.loop = !!e.loop), e.speed !== void 0 && (this.speed = o("Timeline", "speed", e.speed));
19
+ constructor(t = {}) {
20
+ r("Timeline", t, d, f), t.loop !== void 0 && (this.loop = !!t.loop), t.speed !== void 0 && (this.speed = e("Timeline", "speed", t.speed));
21
21
  }
22
- add(t, r, i = {}) {
23
- o("Timeline.add()", "duration", t), t < 0 && e("Timeline.add()", `'duration' is seconds, 0 or more, got ${t}`), typeof r != "function" && e("Timeline.add()", `takes (duration, (t) => { ... }), got ${a(r)} for the step`), n("Timeline.add()", i, p, m);
24
- let c = typeof i.ease == "function" ? i.ease : s[i.ease ?? "linear"];
25
- return c || e("Timeline.add()", `ease '${i.ease}' - expected ${Object.keys(s).map((e) => `'${e}'`).join(", ")}, or a function`), this.#e.push({
22
+ add(n, a, s = {}) {
23
+ e("Timeline.add()", "duration", n), n < 0 && t("Timeline.add()", `'duration' is seconds, 0 or more, got ${n}`), typeof a != "function" && t("Timeline.add()", `takes (duration, (t) => { ... }), got ${i(a)} for the step`), r("Timeline.add()", s, p, m);
24
+ let c = typeof s.ease == "function" ? s.ease : o[s.ease ?? "linear"];
25
+ return c || t("Timeline.add()", `ease '${s.ease}' - expected ${Object.keys(o).map((e) => `'${e}'`).join(", ")}, or a function`), this.#e.push({
26
26
  start: this.duration,
27
- duration: t,
28
- run: r,
29
- label: i.label ?? "",
27
+ duration: n,
28
+ run: a,
29
+ label: s.label ?? "",
30
30
  ease: c
31
31
  }), this.#a(), this;
32
32
  }
@@ -59,15 +59,15 @@ var d = ["loop", "speed"], f = {
59
59
  pause() {
60
60
  return this.#n = !1, this;
61
61
  }
62
- seek(e) {
63
- return this.#t = Math.min(this.duration, Math.max(0, o("Timeline.seek()", "time", e))), this.#a(), this;
62
+ seek(t) {
63
+ return this.#t = Math.min(this.duration, Math.max(0, e("Timeline.seek()", "time", t))), this.#a(), this;
64
64
  }
65
- seekStep(t) {
66
- let n = typeof t == "string" ? this.#e.findIndex((e) => e.label === t) : t, r = this.#e[n];
67
- return r || e("Timeline.seekStep()", `no step ${a(t)} - there are ${this.#e.length}${typeof t == "string" ? `: ${this.#e.map((e) => `'${e.label}'`).join(", ")}` : ""}`), this.seek(r.start);
65
+ seekStep(e) {
66
+ let n = typeof e == "string" ? this.#e.findIndex((t) => t.label === e) : e, r = this.#e[n];
67
+ return r || t("Timeline.seekStep()", `no step ${i(e)} - there are ${this.#e.length}${typeof e == "string" ? `: ${this.#e.map((e) => `'${e.label}'`).join(", ")}` : ""}`), this.seek(r.start);
68
68
  }
69
- onStep(t) {
70
- return typeof t != "function" && e("Timeline.onStep()", `takes (index, label) => { ... }, got ${a(t)}`), this.#i.push(t), this.#r >= 0 && t(this.#r, this.label), this;
69
+ onStep(e) {
70
+ return typeof e != "function" && t("Timeline.onStep()", `takes (index, label) => { ... }, got ${i(e)}`), this.#i.push(e), this.#r >= 0 && e(this.#r, this.label), this;
71
71
  }
72
72
  update(e) {
73
73
  if (this.#n && this.#e.length) {
@@ -135,33 +135,33 @@ var d = ["loop", "speed"], f = {
135
135
  0
136
136
  ], Math.PI / 2]
137
137
  };
138
- function S(e, a, o = {}) {
139
- n("face()", o, _, v);
140
- let s = o.forward === void 0 ? "-z" : r("face()", "forward", o.forward, g), u = e.position.clone().add(t.fromInput(a, "face()")), d = o.up === void 0 ? void 0 : t.fromInput(o.up, "face() up"), f = e.parent;
138
+ function S(e, t, i = {}) {
139
+ r("face()", i, _, v);
140
+ let o = i.forward === void 0 ? "-z" : c("face()", "forward", i.forward, g), l = e.position.clone().add(a.fromInput(t, "face()")), d = i.up === void 0 ? void 0 : a.fromInput(i.up, "face() up"), f = e.parent;
141
141
  if (f) {
142
- let n = f.updateWorld().worldMatrix;
143
- d &&= i(d, d.add(e.position), n).sub(i(new t(), e.position, n)), i(u, u, n);
142
+ let t = f.updateWorld().worldMatrix;
143
+ d &&= n(d, d.add(e.position), t).sub(n(new a(), e.position, t)), n(l, l, t);
144
144
  }
145
- e.lookAt(u, d);
146
- let p = x[s];
145
+ e.lookAt(l, d);
146
+ let p = x[o];
147
147
  if (p) {
148
- let t = l(/* @__PURE__ */ new Float32Array(4), p[0], p[1]);
149
- e.quaternion.copy(c(/* @__PURE__ */ new Float32Array(4), e.quaternion, t));
148
+ let t = u(/* @__PURE__ */ new Float32Array(4), p[0], p[1]);
149
+ e.quaternion.copy(s(/* @__PURE__ */ new Float32Array(4), e.quaternion, t));
150
150
  }
151
151
  return e;
152
152
  }
153
- function C(t, r, i, o = {}) {
154
- return r instanceof u || e("follow()", `takes (node, curve, t) - make the curve with curve([...points]) or ellipse({...}), got ${a(r)}`), n("follow()", o, y, b), t.position.copy(r.at(i)), o.orient !== !1 && S(t, r.tangentAt(i), {
153
+ function C(e, n, a, o = {}) {
154
+ return n instanceof l || t("follow()", `takes (node, curve, t) - make the curve with curve([...points]) or ellipse({...}), got ${i(n)}`), r("follow()", o, y, b), e.position.copy(n.at(a)), o.orient !== !1 && S(e, n.tangentAt(a), {
155
155
  forward: o.forward,
156
156
  up: o.up
157
- }), t;
157
+ }), e;
158
158
  }
159
- function w(t, n) {
160
- let r = t?.geometry;
161
- r && t.drawMode === "lines" || e("reveal()", `takes a Lines node, got ${a(t)}`);
162
- let i = r.vertexCount / 2;
163
- return r.drawCount = Math.round(Math.min(1, Math.max(0, n)) * i) * 2, t.visible = r.drawCount > 0, t;
159
+ function w(e, n) {
160
+ let r = e?.geometry;
161
+ r && e.drawMode === "lines" || t("reveal()", `takes a Lines node, got ${i(e)}`);
162
+ let a = r.vertexCount / 2;
163
+ return r.drawCount = Math.round(Math.min(1, Math.max(0, n)) * a) * 2, e.visible = r.drawCount > 0, e;
164
164
  }
165
- export { s as EASES, h as Timeline, S as face, C as follow, w as reveal };
165
+ export { o as EASES, h as Timeline, S as face, C as follow, w as reveal };
166
166
 
167
167
  //# sourceMappingURL=motion.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"motion.js","names":["fail","Vec3","checkOptions","checkOneOf","transformMat4","describe","checkNumber","EASES","multiply","setAxisAngle","Curve","OPTIONS","HINTS","STEP_OPTIONS","STEP_HINTS","Timeline","options","duration","run","ease","#steps","#apply","last","#time","#current","#playing","time","i","step","s","target","fn","#onStep","dt","steps","now","current","raw","FORWARD","FACE_OPTIONS","FACE_HINTS","FOLLOW_OPTIONS","FOLLOW_HINTS","TO_MINUS_Z","face","forward","ahead","node","direction","up","parent","m","turn","q","follow","path","t","reveal","g","lines","pieces"],"sources":["../src/motion/Timeline.js","../src/motion/follow.js"],"sourcesContent":["// A timeline of named steps played one after another - for stories, explainers and guided tours.\n// Each step is a function given its own progress (0 before it starts, 0-1 while it plays, 1 once\n// it's done), every frame. Steps set the scene from their progress rather than nudging it, so\n// seeking, stepping back and looping always leave the scene right.\n\nimport { checkNumber, checkOptions, describe, fail } from '../validate.js'\nimport { EASES } from './ease.js'\n\n/** @import { EaseName } from './ease.js' */\n\n/**\n * @typedef {object} StepOptions\n * @property {string} [label] what the step shows, for captions - timeline.label is the current one\n * @property {EaseName | ((t: number) => number)} [ease] how progress runs through the step, default 'linear'\n */\n\n/**\n * @typedef {object} TimelineOptions\n * @property {boolean} [loop] start again at the end, default false\n * @property {number} [speed] 1 is real time, default 1\n */\n\n/** @typedef {{ start: number, duration: number, run: (t: number) => void, label: string, ease: (t: number) => number }} Step */\n\n/**\n * Steps played in order, each a function of its progress.\n *\n * const story = new Timeline({ loop: true })\n * story.add(3, (t) => follow(rocket, ascent, t), { label: 'Launch' })\n * story.add(2, (t) => { capsule.visible = t > 0 }, { label: 'Separation' })\n * view.start((dt) => story.update(dt))\n */\nconst OPTIONS = ['loop', 'speed']\nconst HINTS = { repeat: \"use 'loop: true'\", autoplay: 'a timeline plays from the start - call pause() to hold it' }\n/** What add() takes, and hints for names people reach for instead. */\nexport const STEP_OPTIONS = ['label', 'ease']\nexport const STEP_HINTS = { name: \"use 'label'\", easing: \"use 'ease'\" }\n\nexport class Timeline {\n /** The options it takes, and hints for names people reach for instead. */\n static OPTIONS = OPTIONS\n static HINTS = HINTS\n /** @type {Step[]} */ #steps = []\n #time = 0\n #playing = true\n #current = -1\n /** @type {((index: number, label: string) => void)[]} */ #onStep = []\n loop = false\n speed = 1\n\n /** @param {TimelineOptions} [options] */\n constructor(options = {}) {\n checkOptions('Timeline', options, OPTIONS, HINTS)\n if (options.loop !== undefined) this.loop = !!options.loop\n if (options.speed !== undefined) this.speed = checkNumber('Timeline', 'speed', options.speed)\n }\n\n /**\n * Add a step after the last one.\n * @param {number} duration seconds; 0 for an instant change\n * @param {(t: number) => void} run called every frame with this step's progress, 0-1\n * @param {StepOptions} [options]\n * @returns {this}\n */\n add(duration, run, options = {}) {\n checkNumber('Timeline.add()', 'duration', duration)\n if (duration < 0) fail('Timeline.add()', `'duration' is seconds, 0 or more, got ${duration}`)\n if (typeof run !== 'function') fail('Timeline.add()', `takes (duration, (t) => { ... }), got ${describe(run)} for the step`)\n checkOptions('Timeline.add()', options, STEP_OPTIONS, STEP_HINTS)\n const ease = typeof options.ease === 'function' ? options.ease : EASES[options.ease ?? 'linear']\n if (!ease) fail('Timeline.add()', `ease '${options.ease}' - expected ${Object.keys(EASES).map((e) => `'${e}'`).join(', ')}, or a function`)\n this.#steps.push({ start: this.duration, duration, run, label: options.label ?? '', ease })\n this.#apply()\n return this\n }\n\n /** Total length in seconds. */\n get duration() {\n const last = this.#steps[this.#steps.length - 1]\n return last ? last.start + last.duration : 0\n }\n\n /** Seconds since the start. */\n get time() {\n return this.#time\n }\n\n /** The step playing now, by index - -1 before any are added. */\n get step() {\n return this.#current\n }\n\n /** The label of the step playing now. */\n get label() {\n return this.#steps[this.#current]?.label ?? ''\n }\n\n /** The steps, in order, with their start times and labels. */\n get steps() {\n return this.#steps.map(({ start, duration, label }) => ({ start, duration, label }))\n }\n\n get playing() {\n return this.#playing\n }\n\n /** @returns {this} */\n play() {\n if (this.#time >= this.duration && !this.loop) this.#time = 0\n this.#playing = true\n return this\n }\n\n /** @returns {this} */\n pause() {\n this.#playing = false\n return this\n }\n\n /**\n * Jump to a time in seconds, setting the scene to match.\n * @param {number} time @returns {this}\n */\n seek(time) {\n this.#time = Math.min(this.duration, Math.max(0, checkNumber('Timeline.seek()', 'time', time)))\n this.#apply()\n return this\n }\n\n /**\n * Jump to the start of a step, by index or label.\n * @param {number | string} step @returns {this}\n */\n seekStep(step) {\n const i = typeof step === 'string' ? this.#steps.findIndex((s) => s.label === step) : step\n const target = this.#steps[i]\n if (!target) fail('Timeline.seekStep()', `no step ${describe(step)} - there are ${this.#steps.length}${typeof step === 'string' ? `: ${this.#steps.map((s) => `'${s.label}'`).join(', ')}` : ''}`)\n return this.seek(target.start)\n }\n\n /**\n * Call with the step playing now, and again whenever it changes: (index, label). Called straight\n * away too, so a caption set up after the steps starts right.\n * @param {(index: number, label: string) => void} fn @returns {this}\n */\n onStep(fn) {\n if (typeof fn !== 'function') fail('Timeline.onStep()', `takes (index, label) => { ... }, got ${describe(fn)}`)\n this.#onStep.push(fn)\n if (this.#current >= 0) fn(this.#current, this.label)\n return this\n }\n\n /**\n * Move time on by dt seconds (while playing) and set the scene - call every frame.\n * @param {number} dt @returns {this}\n */\n update(dt) {\n if (this.#playing && this.#steps.length) {\n let t = this.#time + dt * this.speed\n if (t >= this.duration) {\n if (this.loop && this.duration > 0) t %= this.duration\n else {\n t = this.duration\n this.#playing = false\n }\n }\n this.#time = t\n }\n this.#apply()\n return this\n }\n\n // set every step from the time: later steps back to their start, latest first, then each step\n // up to now in order - so whatever a step controls ends up as the latest one to touch it says\n #apply() {\n const steps = this.#steps, now = this.#time\n let current = steps.length - 1\n for (let i = 0; i < steps.length; i++) {\n if (now < steps[i].start + steps[i].duration || (steps[i].duration === 0 && now <= steps[i].start)) {\n current = i\n break\n }\n }\n for (let i = steps.length - 1; i > current; i--) steps[i].run(0)\n for (let i = 0; i <= current; i++) {\n const s = steps[i]\n const raw = s.duration > 0 ? Math.min(1, Math.max(0, (now - s.start) / s.duration)) : (now >= s.start ? 1 : 0)\n s.run(s.ease(raw))\n }\n if (current !== this.#current) {\n this.#current = current\n for (const fn of this.#onStep) fn(current, steps[current]?.label ?? '')\n }\n }\n}\n","// Moving things along curves, and drawing curves in as they're travelled.\n\nimport { transformMat4 } from '../math/vec3.js'\nimport { multiply, setAxisAngle } from '../math/quat.js'\nimport { Curve } from '../geometry/Curve.js'\nimport { Vec3 } from '../value/Vec3.js'\nimport { checkOneOf, checkOptions, describe, fail } from '../validate.js'\n\n/** @import { Node } from '../scene/Node.js' */\n/** @import { Lines } from '../scene/drawables.js' */\n\n/** @import { Vec3Input } from '../types.js' */\n\n/**\n * Which of a model's own axes is its nose. Cameras, lights and lookAt use -Z; a model built\n * upright - a rocket along +Y - says '+y'.\n * @typedef {'-z' | '+z' | '-y' | '+y' | '-x' | '+x'} Forward\n */\n\n/**\n * @typedef {object} FaceOptions\n * @property {Forward} [forward] the model's nose axis, default '-z'\n * @property {Vec3Input} [up] which way is up as it turns, in the parent's space, default [0, 1, 0].\n * Heading straight along it leaves the roll undefined, so a craft that turns through every\n * heading in the x-y plane (seen face on) needs one out of the plane: [0, 0, 1]\n */\n\n/**\n * @typedef {object} FollowOptions\n * @property {boolean} [orient] turn the node to face along the curve, default true\n * @property {Forward} [forward] the model's nose axis, default '-z'\n * @property {Vec3Input} [up] which way is up as it turns, default [0, 1, 0] - see face()\n */\n\nconst FORWARD = ['-z', '+z', '-y', '+y', '-x', '+x']\n/** What face() and follow() take, and hints for names people reach for instead. */\nexport const FACE_OPTIONS = ['forward', 'up']\nexport const FACE_HINTS = { nose: \"use 'forward' - which of the model's axes is its nose\" }\nexport const FOLLOW_OPTIONS = ['orient', 'forward', 'up']\nexport const FOLLOW_HINTS = { rotate: \"use 'orient'\", lookAhead: 'the node faces along the curve - orient: false leaves it as it is', nose: \"use 'forward' - which of the model's axes is its nose\" }\n// the turn that takes each nose axis onto -Z, as [axis, angle]\nconst TO_MINUS_Z = {\n '-z': null,\n '+z': [[0, 1, 0], Math.PI],\n '-y': [[1, 0, 0], Math.PI / 2],\n '+y': [[1, 0, 0], -Math.PI / 2],\n '-x': [[0, 1, 0], -Math.PI / 2],\n '+x': [[0, 1, 0], Math.PI / 2],\n}\n\n/**\n * Turn a node so its nose points along a direction, in its parent's space - for a model built\n * along another axis, say which with forward.\n *\n * face(lander, [0, 1, 0], { forward: '+y' }) // stand it upright\n * @param {Node} node\n * @param {Vec3Input} direction\n * @param {FaceOptions} [options]\n * @returns {Node}\n */\nexport function face(node, direction, options = {}) {\n checkOptions('face()', options, FACE_OPTIONS, FACE_HINTS)\n const forward = /** @type {Forward} */ (options.forward === undefined ? '-z' : checkOneOf('face()', 'forward', options.forward, FORWARD))\n const ahead = node.position.clone().add(Vec3.fromInput(direction, 'face()'))\n let up = options.up === undefined ? undefined : Vec3.fromInput(options.up, 'face() up')\n const parent = node.parent\n if (parent) {\n const m = parent.updateWorld().worldMatrix\n if (up) up = transformMat4(up, up.add(node.position), m).sub(transformMat4(new Vec3(), node.position, m))\n transformMat4(ahead, ahead, m)\n }\n node.lookAt(ahead, up)\n const turn = TO_MINUS_Z[forward]\n if (turn) {\n const q = setAxisAngle(new Float32Array(4), turn[0], turn[1])\n node.quaternion.copy(multiply(new Float32Array(4), node.quaternion, q))\n }\n return node\n}\n\n/**\n * Put a node t of the way along a curve (0 start, 1 end, by distance), facing the way it runs.\n * The curve is in the node's parent's space - the same space as its position. A model built along\n * another axis than -Z says which is its nose with forward.\n *\n * story.add(4, (t) => follow(lander, transfer, t))\n * follow(rocket, ascent, t, { forward: '+y' }) // a rocket modelled upright\n * @param {Node} node\n * @param {Curve} path\n * @param {number} t\n * @param {FollowOptions} [options]\n * @returns {Node}\n */\nexport function follow(node, path, t, options = {}) {\n if (!(path instanceof Curve)) fail('follow()', `takes (node, curve, t) - make the curve with curve([...points]) or ellipse({...}), got ${describe(path)}`)\n checkOptions('follow()', options, FOLLOW_OPTIONS, FOLLOW_HINTS)\n node.position.copy(path.at(t))\n if (options.orient !== false) face(node, path.tangentAt(t), { forward: options.forward, up: options.up })\n return node\n}\n\n/**\n * Draw only the first part of a line - t of it (0 nothing, 1 all) - so a path draws in as it's\n * travelled. For Lines made from curve.line(): its pieces run in order from the start.\n *\n * story.add(4, (t) => { follow(lander, transfer, t); reveal(transferLine, t) })\n * @param {Lines} lines\n * @param {number} t\n * @returns {Lines}\n */\nexport function reveal(lines, t) {\n const g = lines?.geometry\n if (!g || /** @type {any} */ (lines).drawMode !== 'lines') fail('reveal()', `takes a Lines node, got ${describe(lines)}`)\n const pieces = g.vertexCount / 2\n g.drawCount = Math.round(Math.min(1, Math.max(0, t)) * pieces) * 2\n lines.visible = g.drawCount > 0\n return lines\n}\n\n"],"mappings":";;AAgCA,IAAMW,IAAU,CAAC,QAAQ,OAAO,GAC1BC,IAAQ;CAAE,QAAQ;CAAoB,UAAU;AAA4D,GAErGC,IAAe,CAAC,SAAS,MAAM,GAC/BC,IAAa;CAAE,MAAM;CAAe,QAAQ;AAAa,GAEzDC,IAAb,MAAsB;CAEpB,OAAO,UAAUJ;CACjB,OAAO,QAAQC;CACO,KAAS,CAAC;CAChC,KAAQ;CACR,KAAW;CACX,KAAW;CAC+C,KAAU,CAAC;CACrE,OAAO;CACP,QAAQ;CAGR,YAAY,IAAU,CAAC,GAAG;EAGxB,AAFAV,EAAa,YAAYc,GAASL,GAASC,CAAK,GAC5CI,EAAQ,SAAS,KAAA,MAAW,KAAK,OAAO,CAAC,CAACA,EAAQ,OAClDA,EAAQ,UAAU,KAAA,MAAW,KAAK,QAAQV,EAAY,YAAY,SAASU,EAAQ,KAAK;CAC9F;CASA,IAAI,GAAU,GAAK,IAAU,CAAC,GAAG;EAI/B,AAHAV,EAAY,kBAAkB,YAAYW,CAAQ,GAC9CA,IAAW,KAAGjB,EAAK,kBAAkB,yCAAyCiB,GAAU,GACxF,OAAOC,KAAQ,cAAYlB,EAAK,kBAAkB,yCAAyCK,EAASa,CAAG,EAAE,cAAc,GAC3HhB,EAAa,kBAAkBc,GAASH,GAAcC,CAAU;EAChE,IAAMK,IAAO,OAAOH,EAAQ,QAAS,aAAaA,EAAQ,OAAOT,EAAMS,EAAQ,QAAQ;EAIvF,OAHKG,KAAMnB,EAAK,kBAAkB,SAASgB,EAAQ,KAAK,eAAe,OAAO,KAAKT,CAAK,CAAC,CAAC,KAAK,MAAM,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,EAAE,gBAAgB,GAC1I,KAAKa,GAAO,KAAK;GAAE,OAAO,KAAK;GAAU;GAAU;GAAK,OAAOJ,EAAQ,SAAS;GAAI;EAAK,CAAC,GAC1F,KAAKK,GAAO,GACL;CACT;CAGA,IAAI,WAAW;EACb,IAAMC,IAAO,KAAKF,GAAO,KAAKA,GAAO,SAAS;EAC9C,OAAOE,IAAOA,EAAK,QAAQA,EAAK,WAAW;CAC7C;CAGA,IAAI,OAAO;EACT,OAAO,KAAKC;CACd;CAGA,IAAI,OAAO;EACT,OAAO,KAAKC;CACd;CAGA,IAAI,QAAQ;EACV,OAAO,KAAKJ,GAAO,KAAKI,GAAS,EAAE,SAAS;CAC9C;CAGA,IAAI,QAAQ;EACV,OAAO,KAAKJ,GAAO,KAAK,EAAE,UAAO,aAAU,gBAAa;GAAE;GAAO;GAAU;EAAM,EAAE;CACrF;CAEA,IAAI,UAAU;EACZ,OAAO,KAAKK;CACd;CAGA,OAAO;EAGL,OAFI,KAAKF,MAAS,KAAK,YAAY,CAAC,KAAK,SAAM,KAAKA,KAAQ,IAC5D,KAAKE,KAAW,IACT;CACT;CAGA,QAAQ;EAEN,OADA,KAAKA,KAAW,IACT;CACT;CAMA,KAAK,GAAM;EAGT,OAFA,KAAKF,KAAQ,KAAK,IAAI,KAAK,UAAU,KAAK,IAAI,GAAGjB,EAAY,mBAAmB,QAAQoB,CAAI,CAAC,CAAC,GAC9F,KAAKL,GAAO,GACL;CACT;CAMA,SAAS,GAAM;EACb,IAAMM,IAAI,OAAOC,KAAS,WAAW,KAAKR,GAAO,WAAW,MAAMS,EAAE,UAAUD,CAAI,IAAIA,GAChFE,IAAS,KAAKV,GAAOO;EAE3B,OADKG,KAAQ9B,EAAK,uBAAuB,WAAWK,EAASuB,CAAI,EAAE,eAAe,KAAKR,GAAO,SAAS,OAAOQ,KAAS,WAAW,KAAK,KAAKR,GAAO,KAAK,MAAM,IAAIS,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,IAAI,MAAM,IAAI,GAC1L,KAAK,KAAKC,EAAO,KAAK;CAC/B;CAOA,OAAO,GAAI;EAIT,OAHI,OAAOC,KAAO,cAAY/B,EAAK,qBAAqB,wCAAwCK,EAAS0B,CAAE,GAAG,GAC9G,KAAKC,GAAQ,KAAKD,CAAE,GAChB,KAAKP,MAAY,KAAGO,EAAG,KAAKP,IAAU,KAAK,KAAK,GAC7C;CACT;CAMA,OAAO,GAAI;EACT,IAAI,KAAKC,MAAY,KAAKL,GAAO,QAAQ;GACvC,IAAI,IAAI,KAAKG,KAAQU,IAAK,KAAK;GAQ/B,AAPI,KAAK,KAAK,aACR,KAAK,QAAQ,KAAK,WAAW,IAAG,KAAK,KAAK,YAE5C,IAAI,KAAK,UACT,KAAKR,KAAW,MAGpB,KAAKF,KAAQ;EACf;EAEA,OADA,KAAKF,GAAO,GACL;CACT;CAIA,KAAS;EACP,IAAMa,IAAQ,KAAKd,IAAQe,IAAM,KAAKZ,IAClCa,IAAUF,EAAM,SAAS;EAC7B,KAAK,IAAIP,IAAI,GAAGA,IAAIO,EAAM,QAAQ,KAChC,IAAIC,IAAMD,EAAMP,EAAE,CAAC,QAAQO,EAAMP,EAAE,CAAC,YAAaO,EAAMP,EAAE,CAAC,aAAa,KAAKQ,KAAOD,EAAMP,EAAE,CAAC,OAAQ;GAClG,IAAUA;GACV;EACF;EAEF,KAAK,IAAIA,IAAIO,EAAM,SAAS,GAAGP,IAAIS,GAAS,KAAK,EAAMT,EAAE,CAAC,IAAI,CAAC;EAC/D,KAAK,IAAIA,IAAI,GAAGA,KAAKS,GAAS,KAAK;GACjC,IAAMP,IAAIK,EAAMP,IACVU,IAAMR,EAAE,WAAW,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,IAAIM,IAAMN,EAAE,SAASA,EAAE,QAAQ,CAAC,IAAK,OAAOA,EAAE;GAChG,EAAE,IAAIA,EAAE,KAAKQ,CAAG,CAAC;EACnB;EACA,IAAID,MAAY,KAAKZ,IAAU;GAC7B,KAAKA,KAAWY;GAChB,KAAK,IAAML,KAAM,KAAKC,IAAS,EAAGI,GAASF,EAAME,EAAQ,EAAE,SAAS,EAAE;EACxE;CACF;AACF,GChKME,IAAU;CAAC;CAAM;CAAM;CAAM;CAAM;CAAM;AAAI,GAEtCC,IAAe,CAAC,WAAW,IAAI,GAC/BC,IAAa,EAAE,MAAM,wDAAwD,GAC7EC,IAAiB;CAAC;CAAU;CAAW;AAAI,GAC3CC,IAAe;CAAE,QAAQ;CAAgB,WAAW;CAAqE,MAAM;AAAwD,GAE9LC,IAAa;CACjB,MAAM;CACN,MAAM,CAAC;EAAC;EAAG;EAAG;CAAC,GAAG,KAAK,EAAE;CACzB,MAAM,CAAC;EAAC;EAAG;EAAG;CAAC,GAAG,KAAK,KAAK,CAAC;CAC7B,MAAM,CAAC;EAAC;EAAG;EAAG;CAAC,GAAG,CAAC,KAAK,KAAK,CAAC;CAC9B,MAAM,CAAC;EAAC;EAAG;EAAG;CAAC,GAAG,CAAC,KAAK,KAAK,CAAC;CAC9B,MAAM,CAAC;EAAC;EAAG;EAAG;CAAC,GAAG,KAAK,KAAK,CAAC;AAC/B;AAYA,SAAgBC,EAAK,GAAM,GAAW,IAAU,CAAC,GAAG;CAClD,EAAa,UAAU5B,GAASuB,GAAcC,CAAU;CACxD,IAAMK,IAAkC7B,EAAQ,YAAY,KAAA,IAAY,OAAOb,EAAW,UAAU,WAAWa,EAAQ,SAASsB,CAAO,GACjIQ,IAAQC,EAAK,SAAS,MAAM,CAAC,CAAC,IAAI9C,EAAK,UAAU+C,GAAW,QAAQ,CAAC,GACvEC,IAAKjC,EAAQ,OAAO,KAAA,IAAY,KAAA,IAAYf,EAAK,UAAUe,EAAQ,IAAI,WAAW,GAChFkC,IAASH,EAAK;CACpB,IAAIG,GAAQ;EACV,IAAMC,IAAID,EAAO,YAAY,CAAC,CAAC;EAE/B,AADA,AAAQD,MAAK7C,EAAc6C,GAAIA,EAAG,IAAIF,EAAK,QAAQ,GAAGI,CAAC,CAAC,CAAC,IAAI/C,EAAc,IAAIH,EAAK,GAAG8C,EAAK,UAAUI,CAAC,CAAC,GACxG/C,EAAc0C,GAAOA,GAAOK,CAAC;CAC/B;CACA,EAAK,OAAOL,GAAOG,CAAE;CACrB,IAAMG,IAAOT,EAAWE;CACxB,IAAIO,GAAM;EACR,IAAMC,IAAI5C,kBAAa,IAAI,aAAa,CAAC,GAAG2C,EAAK,IAAIA,EAAK,EAAE;EAC5D,EAAK,WAAW,KAAK5C,kBAAS,IAAI,aAAa,CAAC,GAAGuC,EAAK,YAAYM,CAAC,CAAC;CACxE;CACA,OAAON;AACT;AAeA,SAAgBO,EAAO,GAAM,GAAM,GAAG,IAAU,CAAC,GAAG;CAKlD,OAJMC,aAAgB7C,KAAQV,EAAK,YAAY,0FAA0FK,EAASkD,CAAI,GAAG,GACzJrD,EAAa,YAAYc,GAASyB,GAAgBC,CAAY,GAC9DK,EAAK,SAAS,KAAKQ,EAAK,GAAGC,CAAC,CAAC,GACzBxC,EAAQ,WAAW,MAAO4B,EAAKG,GAAMQ,EAAK,UAAUC,CAAC,GAAG;EAAE,SAASxC,EAAQ;EAAS,IAAIA,EAAQ;CAAG,CAAC,GACjG+B;AACT;AAWA,SAAgBU,EAAO,GAAO,GAAG;CAC/B,IAAMC,IAAIC,GAAO;CACjB,AAAKD,KAAyBC,EAAO,aAAa,WAAS3D,EAAK,YAAY,2BAA2BK,EAASsD,CAAK,GAAG;CACxH,IAAMC,IAASF,EAAE,cAAc;CAG/B,OAFA,EAAE,YAAY,KAAK,MAAM,KAAK,IAAI,GAAG,KAAK,IAAI,GAAGF,CAAC,CAAC,IAAII,CAAM,IAAI,GACjE,EAAM,UAAUF,EAAE,YAAY,GACvBC;AACT"}
1
+ {"version":3,"file":"motion.js","names":["checkNumber","fail","transformMat4","checkOptions","describe","Vec3","EASES","multiply","checkOneOf","Curve","setAxisAngle","OPTIONS","HINTS","STEP_OPTIONS","STEP_HINTS","Timeline","options","duration","run","ease","#steps","#apply","last","#time","#current","#playing","time","i","step","s","target","fn","#onStep","dt","steps","now","current","raw","FORWARD","FACE_OPTIONS","FACE_HINTS","FOLLOW_OPTIONS","FOLLOW_HINTS","TO_MINUS_Z","face","forward","ahead","node","direction","up","parent","m","turn","q","follow","path","t","reveal","g","lines","pieces"],"sources":["../src/motion/Timeline.js","../src/motion/follow.js"],"sourcesContent":["// A timeline of named steps played one after another - for stories, explainers and guided tours.\n// Each step is a function given its own progress (0 before it starts, 0-1 while it plays, 1 once\n// it's done), every frame. Steps set the scene from their progress rather than nudging it, so\n// seeking, stepping back and looping always leave the scene right.\n\nimport { checkNumber, checkOptions, describe, fail } from '../validate.js'\nimport { EASES } from './ease.js'\n\n/** @import { EaseName } from './ease.js' */\n\n/**\n * @typedef {object} StepOptions\n * @property {string} [label] what the step shows, for captions - timeline.label is the current one\n * @property {EaseName | ((t: number) => number)} [ease] how progress runs through the step, default 'linear'\n */\n\n/**\n * @typedef {object} TimelineOptions\n * @property {boolean} [loop] start again at the end, default false\n * @property {number} [speed] 1 is real time, default 1\n */\n\n/** @typedef {{ start: number, duration: number, run: (t: number) => void, label: string, ease: (t: number) => number }} Step */\n\n/**\n * Steps played in order, each a function of its progress.\n *\n * const story = new Timeline({ loop: true })\n * story.add(3, (t) => follow(rocket, ascent, t), { label: 'Launch' })\n * story.add(2, (t) => { capsule.visible = t > 0 }, { label: 'Separation' })\n * view.start((dt) => story.update(dt))\n */\nconst OPTIONS = ['loop', 'speed']\nconst HINTS = { repeat: \"use 'loop: true'\", autoplay: 'a timeline plays from the start - call pause() to hold it' }\n/** What add() takes, and hints for names people reach for instead. */\nexport const STEP_OPTIONS = ['label', 'ease']\nexport const STEP_HINTS = { name: \"use 'label'\", easing: \"use 'ease'\" }\n\nexport class Timeline {\n /** The options it takes, and hints for names people reach for instead. */\n static OPTIONS = OPTIONS\n static HINTS = HINTS\n /** @type {Step[]} */ #steps = []\n #time = 0\n #playing = true\n #current = -1\n /** @type {((index: number, label: string) => void)[]} */ #onStep = []\n loop = false\n speed = 1\n\n /** @param {TimelineOptions} [options] */\n constructor(options = {}) {\n checkOptions('Timeline', options, OPTIONS, HINTS)\n if (options.loop !== undefined) this.loop = !!options.loop\n if (options.speed !== undefined) this.speed = checkNumber('Timeline', 'speed', options.speed)\n }\n\n /**\n * Add a step after the last one.\n * @param {number} duration seconds; 0 for an instant change\n * @param {(t: number) => void} run called every frame with this step's progress, 0-1\n * @param {StepOptions} [options]\n * @returns {this}\n */\n add(duration, run, options = {}) {\n checkNumber('Timeline.add()', 'duration', duration)\n if (duration < 0) fail('Timeline.add()', `'duration' is seconds, 0 or more, got ${duration}`)\n if (typeof run !== 'function') fail('Timeline.add()', `takes (duration, (t) => { ... }), got ${describe(run)} for the step`)\n checkOptions('Timeline.add()', options, STEP_OPTIONS, STEP_HINTS)\n const ease = typeof options.ease === 'function' ? options.ease : EASES[options.ease ?? 'linear']\n if (!ease) fail('Timeline.add()', `ease '${options.ease}' - expected ${Object.keys(EASES).map((e) => `'${e}'`).join(', ')}, or a function`)\n this.#steps.push({ start: this.duration, duration, run, label: options.label ?? '', ease })\n this.#apply()\n return this\n }\n\n /** Total length in seconds. */\n get duration() {\n const last = this.#steps[this.#steps.length - 1]\n return last ? last.start + last.duration : 0\n }\n\n /** Seconds since the start. */\n get time() {\n return this.#time\n }\n\n /** The step playing now, by index - -1 before any are added. */\n get step() {\n return this.#current\n }\n\n /** The label of the step playing now. */\n get label() {\n return this.#steps[this.#current]?.label ?? ''\n }\n\n /** The steps, in order, with their start times and labels. */\n get steps() {\n return this.#steps.map(({ start, duration, label }) => ({ start, duration, label }))\n }\n\n get playing() {\n return this.#playing\n }\n\n /** @returns {this} */\n play() {\n if (this.#time >= this.duration && !this.loop) this.#time = 0\n this.#playing = true\n return this\n }\n\n /** @returns {this} */\n pause() {\n this.#playing = false\n return this\n }\n\n /**\n * Jump to a time in seconds, setting the scene to match.\n * @param {number} time @returns {this}\n */\n seek(time) {\n this.#time = Math.min(this.duration, Math.max(0, checkNumber('Timeline.seek()', 'time', time)))\n this.#apply()\n return this\n }\n\n /**\n * Jump to the start of a step, by index or label.\n * @param {number | string} step @returns {this}\n */\n seekStep(step) {\n const i = typeof step === 'string' ? this.#steps.findIndex((s) => s.label === step) : step\n const target = this.#steps[i]\n if (!target) fail('Timeline.seekStep()', `no step ${describe(step)} - there are ${this.#steps.length}${typeof step === 'string' ? `: ${this.#steps.map((s) => `'${s.label}'`).join(', ')}` : ''}`)\n return this.seek(target.start)\n }\n\n /**\n * Call with the step playing now, and again whenever it changes: (index, label). Called straight\n * away too, so a caption set up after the steps starts right.\n * @param {(index: number, label: string) => void} fn @returns {this}\n */\n onStep(fn) {\n if (typeof fn !== 'function') fail('Timeline.onStep()', `takes (index, label) => { ... }, got ${describe(fn)}`)\n this.#onStep.push(fn)\n if (this.#current >= 0) fn(this.#current, this.label)\n return this\n }\n\n /**\n * Move time on by dt seconds (while playing) and set the scene - call every frame.\n * @param {number} dt @returns {this}\n */\n update(dt) {\n if (this.#playing && this.#steps.length) {\n let t = this.#time + dt * this.speed\n if (t >= this.duration) {\n if (this.loop && this.duration > 0) t %= this.duration\n else {\n t = this.duration\n this.#playing = false\n }\n }\n this.#time = t\n }\n this.#apply()\n return this\n }\n\n // set every step from the time: later steps back to their start, latest first, then each step\n // up to now in order - so whatever a step controls ends up as the latest one to touch it says\n #apply() {\n const steps = this.#steps, now = this.#time\n let current = steps.length - 1\n for (let i = 0; i < steps.length; i++) {\n if (now < steps[i].start + steps[i].duration || (steps[i].duration === 0 && now <= steps[i].start)) {\n current = i\n break\n }\n }\n for (let i = steps.length - 1; i > current; i--) steps[i].run(0)\n for (let i = 0; i <= current; i++) {\n const s = steps[i]\n const raw = s.duration > 0 ? Math.min(1, Math.max(0, (now - s.start) / s.duration)) : (now >= s.start ? 1 : 0)\n s.run(s.ease(raw))\n }\n if (current !== this.#current) {\n this.#current = current\n for (const fn of this.#onStep) fn(current, steps[current]?.label ?? '')\n }\n }\n}\n","// Moving things along curves, and drawing curves in as they're travelled.\n\nimport { transformMat4 } from '../math/vec3.js'\nimport { multiply, setAxisAngle } from '../math/quat.js'\nimport { Curve } from '../geometry/Curve.js'\nimport { Vec3 } from '../value/Vec3.js'\nimport { checkOneOf, checkOptions, describe, fail } from '../validate.js'\n\n/** @import { Node } from '../scene/Node.js' */\n/** @import { Lines } from '../scene/drawables.js' */\n\n/** @import { Vec3Input } from '../types.js' */\n\n/**\n * Which of a model's own axes is its nose. Cameras, lights and lookAt use -Z; a model built\n * upright - a rocket along +Y - says '+y'.\n * @typedef {'-z' | '+z' | '-y' | '+y' | '-x' | '+x'} Forward\n */\n\n/**\n * @typedef {object} FaceOptions\n * @property {Forward} [forward] the model's nose axis, default '-z'\n * @property {Vec3Input} [up] which way is up as it turns, in the parent's space, default [0, 1, 0].\n * Heading straight along it leaves the roll undefined, so a craft that turns through every\n * heading in the x-y plane (seen face on) needs one out of the plane: [0, 0, 1]\n */\n\n/**\n * @typedef {object} FollowOptions\n * @property {boolean} [orient] turn the node to face along the curve, default true\n * @property {Forward} [forward] the model's nose axis, default '-z'\n * @property {Vec3Input} [up] which way is up as it turns, default [0, 1, 0] - see face()\n */\n\nconst FORWARD = ['-z', '+z', '-y', '+y', '-x', '+x']\n/** What face() and follow() take, and hints for names people reach for instead. */\nexport const FACE_OPTIONS = ['forward', 'up']\nexport const FACE_HINTS = { nose: \"use 'forward' - which of the model's axes is its nose\" }\nexport const FOLLOW_OPTIONS = ['orient', 'forward', 'up']\nexport const FOLLOW_HINTS = { rotate: \"use 'orient'\", lookAhead: 'the node faces along the curve - orient: false leaves it as it is', nose: \"use 'forward' - which of the model's axes is its nose\" }\n// the turn that takes each nose axis onto -Z, as [axis, angle]\nconst TO_MINUS_Z = {\n '-z': null,\n '+z': [[0, 1, 0], Math.PI],\n '-y': [[1, 0, 0], Math.PI / 2],\n '+y': [[1, 0, 0], -Math.PI / 2],\n '-x': [[0, 1, 0], -Math.PI / 2],\n '+x': [[0, 1, 0], Math.PI / 2],\n}\n\n/**\n * Turn a node so its nose points along a direction, in its parent's space - for a model built\n * along another axis, say which with forward.\n *\n * face(lander, [0, 1, 0], { forward: '+y' }) // stand it upright\n * @param {Node} node\n * @param {Vec3Input} direction\n * @param {FaceOptions} [options]\n * @returns {Node}\n */\nexport function face(node, direction, options = {}) {\n checkOptions('face()', options, FACE_OPTIONS, FACE_HINTS)\n const forward = /** @type {Forward} */ (options.forward === undefined ? '-z' : checkOneOf('face()', 'forward', options.forward, FORWARD))\n const ahead = node.position.clone().add(Vec3.fromInput(direction, 'face()'))\n let up = options.up === undefined ? undefined : Vec3.fromInput(options.up, 'face() up')\n const parent = node.parent\n if (parent) {\n const m = parent.updateWorld().worldMatrix\n if (up) up = transformMat4(up, up.add(node.position), m).sub(transformMat4(new Vec3(), node.position, m))\n transformMat4(ahead, ahead, m)\n }\n node.lookAt(ahead, up)\n const turn = TO_MINUS_Z[forward]\n if (turn) {\n const q = setAxisAngle(new Float32Array(4), turn[0], turn[1])\n node.quaternion.copy(multiply(new Float32Array(4), node.quaternion, q))\n }\n return node\n}\n\n/**\n * Put a node t of the way along a curve (0 start, 1 end, by distance), facing the way it runs.\n * The curve is in the node's parent's space - the same space as its position. A model built along\n * another axis than -Z says which is its nose with forward.\n *\n * story.add(4, (t) => follow(lander, transfer, t))\n * follow(rocket, ascent, t, { forward: '+y' }) // a rocket modelled upright\n * @param {Node} node\n * @param {Curve} path\n * @param {number} t\n * @param {FollowOptions} [options]\n * @returns {Node}\n */\nexport function follow(node, path, t, options = {}) {\n if (!(path instanceof Curve)) fail('follow()', `takes (node, curve, t) - make the curve with curve([...points]) or ellipse({...}), got ${describe(path)}`)\n checkOptions('follow()', options, FOLLOW_OPTIONS, FOLLOW_HINTS)\n node.position.copy(path.at(t))\n if (options.orient !== false) face(node, path.tangentAt(t), { forward: options.forward, up: options.up })\n return node\n}\n\n/**\n * Draw only the first part of a line - t of it (0 nothing, 1 all) - so a path draws in as it's\n * travelled. For Lines made from curve.line(): its pieces run in order from the start.\n *\n * story.add(4, (t) => { follow(lander, transfer, t); reveal(transferLine, t) })\n * @param {Lines} lines\n * @param {number} t\n * @returns {Lines}\n */\nexport function reveal(lines, t) {\n const g = lines?.geometry\n if (!g || /** @type {any} */ (lines).drawMode !== 'lines') fail('reveal()', `takes a Lines node, got ${describe(lines)}`)\n const pieces = g.vertexCount / 2\n g.drawCount = Math.round(Math.min(1, Math.max(0, t)) * pieces) * 2\n lines.visible = g.drawCount > 0\n return lines\n}\n\n"],"mappings":"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"}