spine-rigc 2.52.0 → 2.54.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/timelines.ts CHANGED
@@ -740,6 +740,67 @@ export const PHYSICS_POSE_RULES: PhysicsPoseRule[] = [
740
740
  },
741
741
  ];
742
742
 
743
+ /** What `physicsStepStability` reads off one pose of a physics constraint. */
744
+ export interface PhysicsStepStability {
745
+ /** `strength · massInverse · step²`, as the step's own products form it: `strength * (step * massInverse) * step`. */
746
+ readonly k: number;
747
+ /** The per-step velocity decay, `damping ** (60 * step)` — the runtime's own expression. */
748
+ readonly d: number;
749
+ /** `2 · (1 + d)`: the step is stable exactly when `k` is below it. */
750
+ readonly bound: number;
751
+ /** The largest magnitude among the step map's two eigenvalues — what one step multiplies an undisturbed offset by, at most. */
752
+ readonly growth: number;
753
+ /** `k < bound`, strictly. */
754
+ readonly stable: boolean;
755
+ }
756
+
757
+ /**
758
+ * Whether one pose of a physics constraint integrates to rest or runs away
759
+ * (issue #1416, part B) — a joint condition on three of the four values
760
+ * `PHYSICS_POSE_RULES` bounds one at a time, and on the constraint's `step`.
761
+ *
762
+ * 🔑 Every substep of `stepPhysics` (`src/core/constraints_physics.ts`, held to
763
+ * spine-core's `PhysicsConstraint.update` at tolerance 0) is, on each driven
764
+ * component, `v += (force − offset·strength)·m; offset += v·step; v *= d` with
765
+ * `m = step · massInverse` and `d = damping ** (60·step)`. The force is
766
+ * additive — wind and gravity set how far the offset goes, never whether it
767
+ * comes back — so with `u = v·step` the step is the linear map
768
+ * `[offset; u] ← [[1 − k, 1], [−k·d, d]]·[offset; u]`, `k = strength·m·step`:
769
+ * trace `1 − k + d`, determinant `d`. For `d` in `[0, 1]` (the damping row's own
770
+ * bound) both eigenvalues lie inside the unit circle exactly when
771
+ * `0 < k < 2·(1 + d)`. The lower end is the strength row's; this is the upper.
772
+ *
773
+ * ⚠️ **Strict.** At `k = 2·(1 + d)` one eigenvalue is exactly −1: the offset
774
+ * flips sign every step and never settles, and at `d = 1` the two coincide and
775
+ * it grows linearly.
776
+ *
777
+ * 📏 [measured, issue #1416's comment of 2026-10-14] on 270 generated rigs —
778
+ * x, y, rotate, scaleX and shearX drives; strength 10/100/1000; mass 0.1/1; fps
779
+ * 5/15/60; damping 0/0.85/1 — 115 of 115 with `k > bound` ran past 10⁶ units
780
+ * under a unit gust along the drive (92 of them in the runtime's default world
781
+ * with nothing set), 145 of 145 with `k < bound` stayed bounded, core and
782
+ * spine-core identical bit for bit. The 10 exactly on it (damping 1, k = 4)
783
+ * stayed under 10⁶ in the 6 s walk, which is why the 6 s classifier cannot
784
+ * stand for this edge: the x, y and scaleX ones ended the walk at their peak,
785
+ * still growing, and rotate and shearX swung their tip the bone's full length.
786
+ * All 270 passed every gate before this, because each value was inside its
787
+ * own row.
788
+ *
789
+ * The products are formed in the runtime's own order (`m = t * massInverse`,
790
+ * `d = damping ** (60 * t)`), so the number printed is the runtime's `k`
791
+ * rather than a re-association of it.
792
+ */
793
+ export function physicsStepStability(strength: number, massInverse: number, damping: number, step: number): PhysicsStepStability {
794
+ const m = step * massInverse;
795
+ const k = strength * m * step;
796
+ const d = damping ** (60 * step);
797
+ const bound = 2 * (1 + d);
798
+ const trace = 1 - k + d;
799
+ const disc = trace * trace - 4 * d;
800
+ const growth = disc >= 0 ? Math.max(Math.abs(trace + Math.sqrt(disc)), Math.abs(trace - Math.sqrt(disc))) / 2 : Math.sqrt(d);
801
+ return { k, d, bound, growth, stable: k < bound };
802
+ }
803
+
743
804
  /**
744
805
  * What `A23`'s setup arm says about a pose value its rule refuses: the arm of
745
806
  * `outside` that holds for it, or the bound itself where the row has no arms or
package/src/types.ts CHANGED
@@ -1679,6 +1679,20 @@ export interface CompileResult {
1679
1679
  artCells: number;
1680
1680
  keptCells: number;
1681
1681
  islands: number;
1682
+ /**
1683
+ * Present only when the mesh is weighted by an `influence` block (issue
1684
+ * #1415): the block as stated, how many vertices had more links in reach
1685
+ * than `max` — the vertices the cap dropped a link from — and the most
1686
+ * links in reach at any one vertex, which is the `max` that would drop none.
1687
+ */
1688
+ reach?: {
1689
+ radius: number;
1690
+ across: boolean;
1691
+ max: number;
1692
+ shape: 'linear' | 'smooth';
1693
+ capped: number;
1694
+ widest: number;
1695
+ };
1682
1696
  };
1683
1697
  /**
1684
1698
  * What a depth map put on this mesh's vertices, when one was named.