rig-play 1.9.1 → 1.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -219,7 +219,10 @@ apart), `pause()` / `resume()` hold and release it, `timeScale` scales every
219
219
  step, and `seek(t)` moves it to a time. A seek is "reopen, then step to the
220
220
  time" in `seekStep` increments (1/60 s by default) — never a jump, because a
221
221
  physics rig has history and its pose at a time depends on the steps that
222
- reached it. `on('event', …)` receives the event keys each step crosses, in the
222
+ reached it. The physics integrates on rig-c's render clock (`physicsClockStep`:
223
+ each step and 2⁻³⁰ of it more), so stepping at a fixed rate — `frame(1/60)`, a
224
+ capture, a test — shows no single-frame spring jump where a frame ends on a
225
+ physics step; the animation, the events and `time` stay on the steps (#103). `on('event', …)` receives the event keys each step crosses, in the
223
226
  runtime's order across a loop's wrap; `on('complete', …)` fires once a step
224
227
  completes a cycle. `skin` (a constructor option) poses under a named skin;
225
228
  without it the rig is posed the way a fresh Spine skeleton is, with no skin
package/dist/index.js CHANGED
@@ -4927,6 +4927,17 @@ function setupPosed(doc, plant, mode) {
4927
4927
  if (world === null || shown === null || setup.slots === null || setup.drawOrder === null) throw new CoreInputError("the raw setup pose was not posed");
4928
4928
  return { world, shown, slots: setup.slots, drawOrder: setup.drawOrder };
4929
4929
  }
4930
+ const PHYSICS_CLOCK_LEAD = 2 ** -30;
4931
+ function physicsClockStep(step) {
4932
+ return step + step * PHYSICS_CLOCK_LEAD;
4933
+ }
4934
+ function checkedClock(clock, who) {
4935
+ return (dt) => {
4936
+ const moved = clock(dt);
4937
+ if (typeof moved !== "number" || !Number.isFinite(moved) || moved < 0) throw new CoreInputError(`${who} clock moved the physics by ${String(moved)} for a step of ${dt}, not a finite time at or above 0`);
4938
+ return moved;
4939
+ };
4940
+ }
4930
4941
  function loopedTime(trackTime, duration) {
4931
4942
  return duration === 0 ? 0 : trackTime % duration;
4932
4943
  }
@@ -5031,7 +5042,10 @@ function openTrack(doc, animation, options, plant = {}) {
5031
5042
  if (typeof options !== "object" || options === null || typeof options.loop !== "boolean") throw new CoreInputError(`the live track's options.loop is ${JSON.stringify(options?.loop)}, not true or false`);
5032
5043
  if (options.reset !== "animation" && options.reset !== "setup") throw new CoreInputError(`the live track's options.reset is ${JSON.stringify(options.reset)}, not "animation" or "setup"`);
5033
5044
  if (options.adjust !== void 0 && typeof options.adjust !== "function") throw new CoreInputError(`the live track's options.adjust is ${typeof options.adjust}, not a function`);
5034
- const { track, first } = openRawTrack(doc, animation, plant, options.reset, options.loop ? LOOPED : HELD, options.adjust);
5045
+ if (options.clock !== void 0 && typeof options.clock !== "function") throw new CoreInputError(`the live track's options.clock is ${typeof options.clock}, not a function`);
5046
+ const mode = options.loop ? LOOPED : HELD;
5047
+ const clock = options.clock === void 0 ? void 0 : checkedClock(options.clock, "the live track's");
5048
+ const { track, first } = openRawTrack(doc, animation, plant, options.reset, clock === void 0 ? mode : { ...mode, clock: (dt) => clock(dt) }, options.adjust);
5035
5049
  return new Track(track, first);
5036
5050
  }
5037
5051
  class Track {
@@ -5953,13 +5967,16 @@ function delayOf(v, call) {
5953
5967
  function openTracks(doc, options, plant = {}) {
5954
5968
  if (typeof options !== "object" || options === null || options.reset !== "animation" && options.reset !== "setup") throw new CoreInputError(`the live tracks' options.reset is ${JSON.stringify(options?.reset)}, not "animation" or "setup"`);
5955
5969
  if (options.adjust !== void 0 && typeof options.adjust !== "function") throw new CoreInputError(`the live tracks' options.adjust is ${typeof options.adjust}, not a function`);
5956
- return new Tracks(doc, options.reset, options.adjust, plant, openRawTracksPath(doc, plant));
5970
+ if (options.clock !== void 0 && typeof options.clock !== "function") throw new CoreInputError(`the live tracks' options.clock is ${typeof options.clock}, not a function`);
5971
+ const clock = options.clock === void 0 ? void 0 : checkedClock(options.clock, "the live tracks'");
5972
+ return new Tracks(doc, options.reset, options.adjust, clock, plant, openRawTracksPath(doc, plant));
5957
5973
  }
5958
5974
  class Tracks {
5959
- constructor(doc, reset, adjust, plant, path) {
5975
+ constructor(doc, reset, adjust, clock, plant, path) {
5960
5976
  this.doc = doc;
5961
5977
  this.reset = reset;
5962
5978
  this.adjust = adjust;
5979
+ this.clock = clock;
5963
5980
  this.plant = plant;
5964
5981
  this.path = path;
5965
5982
  this.started = false;
@@ -6064,7 +6081,7 @@ class Tracks {
6064
6081
  const { doc, path } = this;
6065
6082
  const ctx = path.ctx;
6066
6083
  const before = ctx.time;
6067
- ctx.time += dt;
6084
+ ctx.time += this.clock === void 0 ? dt : this.clock(dt);
6068
6085
  const pose = new Pose(doc, this.plant);
6069
6086
  const animationTime = this.mixer.apply(pose);
6070
6087
  const mixed = new Map(doc.slots.map((s, k) => [s.name, { deform: pose.slots[k].deform, seen: pose.slots[k].seen }]));
@@ -8582,7 +8599,7 @@ class RigPlayer {
8582
8599
  if (this.tracks === null && plain && !this.trackMode) {
8583
8600
  this.animation = name;
8584
8601
  this.loop = loop;
8585
- this.track = openTrack(this.view, name, { loop, reset: "animation", adjust: this.adjustHook });
8602
+ this.track = openTrack(this.view, name, { loop, reset: "animation", adjust: this.adjustHook, clock: physicsClockStep });
8586
8603
  this.pose = this.track.first;
8587
8604
  return;
8588
8605
  }
@@ -8673,7 +8690,7 @@ class RigPlayer {
8673
8690
  */
8674
8691
  compose() {
8675
8692
  if (this.tracks !== null) return this.tracks;
8676
- const tracks = openTracks(this.view, { reset: "animation", adjust: this.adjustHook });
8693
+ const tracks = openTracks(this.view, { reset: "animation", adjust: this.adjustHook, clock: physicsClockStep });
8677
8694
  const single = this.track;
8678
8695
  const time = single?.trackTime ?? 0;
8679
8696
  if (single !== null && this.animation !== null) {
@@ -8713,14 +8730,14 @@ class RigPlayer {
8713
8730
  /** Whatever is open, opened again with track 0's animation as `play` last set it, at pose 0: the single track, or the tracks under `tracks: true`. */
8714
8731
  reopen() {
8715
8732
  if (this.tracks !== null) {
8716
- const tracks = openTracks(this.view, { reset: "animation", adjust: this.adjustHook });
8733
+ const tracks = openTracks(this.view, { reset: "animation", adjust: this.adjustHook, clock: physicsClockStep });
8717
8734
  tracks.setAnimation(0, this.animation, { loop: this.loop, mixDuration: 0 });
8718
8735
  this.trackCount = 1;
8719
8736
  this.tracks = tracks;
8720
8737
  this.pose = tracks.start();
8721
8738
  return;
8722
8739
  }
8723
- this.track = openTrack(this.view, this.animation, { loop: this.loop, reset: "animation", adjust: this.adjustHook });
8740
+ this.track = openTrack(this.view, this.animation, { loop: this.loop, reset: "animation", adjust: this.adjustHook, clock: physicsClockStep });
8724
8741
  this.pose = this.track.first;
8725
8742
  }
8726
8743
  /** Steps whatever is open forward to `time` in `seekStep` increments, the last one shorter. */
@@ -8748,7 +8765,11 @@ class RigPlayer {
8748
8765
  * Steps the track by `dt × timeScale` seconds and fires the step's listeners,
8749
8766
  * without drawing. A paused player, or a step of zero, keeps the pose it
8750
8767
  * holds without re-posing it, so a held rig costs the renderer's dirty
8751
- * checks and nothing else.
8768
+ * checks and nothing else. The physics clock moves by `physicsClockStep`
8769
+ * of the step — the step and 2⁻³⁰ of it more, rig-c's render clock — so a
8770
+ * caller stepping at a fixed rate whose frame is a whole number of physics
8771
+ * steps (`frame(1/60)`, a capture) shows no spring jump; the animation time,
8772
+ * the events and `time` stay on the steps themselves (#103).
8752
8773
  */
8753
8774
  advance(dt) {
8754
8775
  if (this.pose === null) return;