coily 0.11.0 → 0.12.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/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { a as SpringPosition, c as defineSpring, i as Spring, n as createSpringSystem, o as SpringConfig, r as TickerOptions, s as SpringOptions, t as SpringSystem } from "./system-B_kZMcpg.js";
2
- export { type Spring, type SpringConfig, type SpringOptions, type SpringPosition, type SpringSystem, type TickerOptions, createSpringSystem, defineSpring };
1
+ import { _ as defineSpring, a as Spring2D, c as Spring2DWithOffset, d as SpringPosition, f as SpringTarget, g as SpringState, h as SpringOptions, i as TickerOptions, l as Vector2, m as SpringConfig, n as SpringSystemOptions, o as Spring2DPosition, p as SpringWithOffset, r as createSpringSystem, s as Spring2DTarget, t as SpringSystem, u as Spring } from "./system-Do5rE0j7.js";
2
+ export { type Spring, type Spring2D, type Spring2DPosition, type Spring2DTarget, type Spring2DWithOffset, type SpringConfig, type SpringOptions, type SpringPosition, type SpringState, type SpringSystem, type SpringSystemOptions, type SpringTarget, type SpringWithOffset, type TickerOptions, type Vector2, createSpringSystem, defineSpring };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { r as defineSpring, t as createSpringSystem } from "./system-Dxxv4ee7.js";
1
+ import { r as defineSpring, t as createSpringSystem } from "./system-DPaqec-Z.js";
2
2
  export { createSpringSystem, defineSpring };
package/dist/nuxt.d.ts CHANGED
@@ -1,6 +1,9 @@
1
1
  import { NuxtModule } from "@nuxt/schema";
2
2
 
3
3
  //#region src/vue/nuxt.d.ts
4
+ interface CoilyModuleOptions {
5
+ debug?: boolean;
6
+ }
4
7
  declare const coilyModule: NuxtModule;
5
8
  //#endregion
6
- export { coilyModule as default };
9
+ export { CoilyModuleOptions, coilyModule as default };
package/dist/nuxt.js CHANGED
@@ -1,11 +1,12 @@
1
1
  import { addComponent, addImports, addPluginTemplate, defineNuxtModule } from "@nuxt/kit";
2
2
  //#region src/vue/nuxt.ts
3
- const pluginTemplate = `import { createSpringSystem } from 'coily'
3
+ function getPluginTemplate(options) {
4
+ return `import { createSpringSystem } from 'coily'
4
5
  import { provideSpringSystem } from 'coily/vue'
5
6
 
6
7
  export default defineNuxtPlugin((nuxtApp) => {
7
8
  nuxtApp.hook('app:created', (vueApp) => {
8
- const system = createSpringSystem()
9
+ const system = createSpringSystem(${options.debug ? "{ debug: true }" : ""})
9
10
  provideSpringSystem(system, vueApp)
10
11
 
11
12
  if(import.meta.client) {
@@ -13,25 +14,49 @@ export default defineNuxtPlugin((nuxtApp) => {
13
14
  }
14
15
  })
15
16
  })`;
17
+ }
16
18
  const coilyModule = defineNuxtModule({
17
19
  meta: {
18
20
  name: "coily",
19
21
  configKey: "coily"
20
22
  },
21
- async setup() {
23
+ defaults: { debug: false },
24
+ async setup(_options) {
22
25
  addPluginTemplate({
23
26
  name: "coily",
24
27
  filename: "coily.plugin.mjs",
25
28
  mode: "all",
26
- getContents: () => pluginTemplate
29
+ getContents: () => getPluginTemplate(_options)
27
30
  });
28
- addImports([{
29
- from: "coily/vue",
30
- name: "useSpring"
31
- }, {
32
- from: "coily",
33
- name: "defineSpring"
34
- }]);
31
+ addImports([
32
+ {
33
+ from: "coily/vue",
34
+ name: "useSpring"
35
+ },
36
+ {
37
+ from: "coily/vue",
38
+ name: "useSpring2D"
39
+ },
40
+ {
41
+ from: "coily/vue",
42
+ name: "SpringRef",
43
+ type: true
44
+ },
45
+ {
46
+ from: "coily/vue",
47
+ name: "SpringRef2D",
48
+ type: true
49
+ },
50
+ {
51
+ from: "coily",
52
+ name: "defineSpring"
53
+ },
54
+ {
55
+ from: "coily",
56
+ name: "SpringConfig",
57
+ type: true
58
+ }
59
+ ]);
35
60
  addComponent({
36
61
  name: "SpringValue",
37
62
  filePath: "coily/vue",
@@ -1,6 +1,11 @@
1
1
  //#region src/motion-set.ts
2
2
  var MotionSet = class {
3
3
  #motions = /* @__PURE__ */ new Set();
4
+ #debug;
5
+ #lastSize = 0;
6
+ constructor(debug = false) {
7
+ this.#debug = debug;
8
+ }
4
9
  add(motion) {
5
10
  this.#motions.add(motion);
6
11
  }
@@ -10,7 +15,11 @@ var MotionSet = class {
10
15
  tick(dt) {
11
16
  for (const motion of this.#motions) {
12
17
  motion.tick(dt);
13
- if (motion.resting) this.#motions.delete(motion);
18
+ if (motion.isResting) this.#motions.delete(motion);
19
+ }
20
+ if (this.#debug && this.#motions.size !== this.#lastSize) {
21
+ this.#lastSize = this.#motions.size;
22
+ console.log(`coily: ${this.#lastSize} active motions`);
14
23
  }
15
24
  }
16
25
  };
@@ -25,7 +34,10 @@ function invariant(condition, message) {
25
34
  /**
26
35
  * Immutable spring configuration.
27
36
  */
37
+ let globalConfigVersion = 0;
28
38
  var SpringConfig = class {
39
+ /** @internal Incremented by `SpringConfig.assign` to signal mutations. */
40
+ #version = 0;
29
41
  mass;
30
42
  tension;
31
43
  damping;
@@ -52,10 +64,10 @@ var SpringConfig = class {
52
64
  if (dampingRatio !== void 0) invariant(dampingRatio >= 0, "Damping ratio must be greater than or equal to 0");
53
65
  if (duration !== void 0) invariant(duration > 0, "Duration must be greater than 0");
54
66
  invariant(displacement !== 0, "Displacement must not be 0");
55
- invariant(precision > 0, "Precision must be greater than 0");
67
+ invariant(precision >= 0, "Precision must be greater than or equal to 0");
56
68
  this.precision = precision;
57
69
  this.precisionMultiplier = 10 ** precision;
58
- this.restingMagnitude = 1 / this.precisionMultiplier;
70
+ this.restingMagnitude = .5 / this.precisionMultiplier;
59
71
  const hasM = mass !== void 0;
60
72
  const hasK = tension !== void 0;
61
73
  const hasC = damping !== void 0;
@@ -80,7 +92,7 @@ var SpringConfig = class {
80
92
  this.tension = tension;
81
93
  this.damping = damping;
82
94
  } else if (hasZ && hasT) {
83
- const threshold = this.restingMagnitude;
95
+ const threshold = .005;
84
96
  const x0 = Math.abs(displacement);
85
97
  invariant(x0 > threshold, "displacement must be greater than the precision threshold");
86
98
  let sigmaCoeff;
@@ -107,6 +119,56 @@ var SpringConfig = class {
107
119
  this.criticalDamping = 2 * this.mass * this.naturalFrequency;
108
120
  this.dampingRatio = this.damping / this.criticalDamping;
109
121
  }
122
+ static get globalVersion() {
123
+ return globalConfigVersion;
124
+ }
125
+ static version(config) {
126
+ return config.#version;
127
+ }
128
+ static assign(target, source) {
129
+ globalConfigVersion++;
130
+ target.#version++;
131
+ const t = target;
132
+ t.mass = source.mass;
133
+ t.tension = source.tension;
134
+ t.damping = source.damping;
135
+ t.precision = source.precision;
136
+ t.naturalFrequency = source.naturalFrequency;
137
+ t.criticalDamping = source.criticalDamping;
138
+ t.dampingRatio = source.dampingRatio;
139
+ t.precisionMultiplier = source.precisionMultiplier;
140
+ t.restingMagnitude = source.restingMagnitude;
141
+ }
142
+ /**
143
+ * Analytically estimates the time remaining for a spring to come to rest,
144
+ * given the current displacement and velocity.
145
+ *
146
+ * Uses the exponential decay envelope common to all three damping regimes.
147
+ * The decay rate depends on the regime:
148
+ * - Underdamped (ζ < 1): σ = ζωₙ
149
+ * - Critically damped (ζ = 1): σ = ωₙ
150
+ * - Overdamped (ζ > 1): σ = (ζ - √(ζ²-1))ωₙ (the slower eigenvalue)
151
+ *
152
+ * The effective initial amplitude accounts for both displacement and velocity,
153
+ * since kinetic energy can convert to additional displacement:
154
+ * A₀ = |x₀| + |v₀| / ωₙ
155
+ *
156
+ * Settling time: t = ln(A₀ / threshold) / σ, with a 2× safety factor
157
+ * to account for polynomial terms in the critically damped solution.
158
+ *
159
+ * Returns the estimated time in milliseconds.
160
+ */
161
+ computeTimeRemaining(state) {
162
+ const { position, velocity } = state;
163
+ const a0 = Math.abs(position) + Math.abs(velocity) / this.naturalFrequency;
164
+ if (a0 <= this.restingMagnitude) return 0;
165
+ let sigma;
166
+ if (this.dampingRatio < 1) sigma = this.dampingRatio * this.naturalFrequency;
167
+ else if (this.dampingRatio === 1) sigma = this.naturalFrequency;
168
+ else sigma = (this.dampingRatio - Math.sqrt(this.dampingRatio * this.dampingRatio - 1)) * this.naturalFrequency;
169
+ if (sigma <= 0) return Infinity;
170
+ return 2 * Math.log(a0 / this.restingMagnitude) / sigma * 1e3;
171
+ }
110
172
  };
111
173
  function defineSpring(input) {
112
174
  return new SpringConfig(input);
@@ -174,9 +236,9 @@ var State = class {
174
236
  set velocity(value) {
175
237
  this.#velocity = value;
176
238
  }
177
- /** Uses raw (unrounded) values so resting detection isn't affected by output quantization. */
178
- get resting() {
179
- return Math.abs(this.#velocity) < this.#config.restingMagnitude && Math.abs(this.#position) < this.#config.restingMagnitude;
239
+ /** A spring is resting when its rounded position and velocity are both zero. */
240
+ get isResting() {
241
+ return this.position === 0 && this.velocity === 0;
180
242
  }
181
243
  };
182
244
  //#endregion
@@ -280,12 +342,16 @@ var Motion = class {
280
342
  #currentSolver = null;
281
343
  #needsUpdate = false;
282
344
  #needsReset = false;
345
+ #configVersion = 0;
346
+ #globalConfigVersion = 0;
347
+ #timeRemaining = 0;
283
348
  #emitter;
284
349
  constructor(config, position, velocity) {
285
350
  this.#config = config;
286
351
  this.#state = new State(config, position, velocity);
287
352
  this.#emitter = new Emitter();
288
353
  this.#updateSolver();
354
+ this.#timeRemaining = this.#config.computeTimeRemaining(this.#state);
289
355
  }
290
356
  get position() {
291
357
  return this.#state.position;
@@ -293,7 +359,7 @@ var Motion = class {
293
359
  set position(value) {
294
360
  this.#state.position = value;
295
361
  this.#needsReset = true;
296
- if (!this.#state.resting) this.#emitter.emit("start");
362
+ if (!this.#state.isResting) this.#emitter.emit("start");
297
363
  }
298
364
  get velocity() {
299
365
  return this.#state.velocity;
@@ -302,16 +368,31 @@ var Motion = class {
302
368
  this.#state.velocity = value;
303
369
  this.#needsReset = true;
304
370
  }
305
- get resting() {
306
- return this.#state.resting;
371
+ get timeRemaining() {
372
+ return this.#timeRemaining;
373
+ }
374
+ get isResting() {
375
+ return this.#state.isResting;
307
376
  }
308
377
  configure(config) {
309
378
  this.#config = config;
310
379
  this.#state.configure(config);
380
+ this.#configVersion = SpringConfig.version(config);
381
+ this.#globalConfigVersion = SpringConfig.globalVersion;
311
382
  this.#needsUpdate = true;
312
383
  }
313
384
  tick(dt, emit = true) {
314
385
  invariant(this.#currentSolver, "Cannot tick a disposed motion");
386
+ const gv = SpringConfig.globalVersion;
387
+ if (gv !== this.#globalConfigVersion) {
388
+ this.#globalConfigVersion = gv;
389
+ if (SpringConfig.version(this.#config) !== this.#configVersion) {
390
+ this.#state.configure(this.#config);
391
+ this.#configVersion = SpringConfig.version(this.#config);
392
+ this.#needsUpdate = true;
393
+ }
394
+ }
395
+ const needsTimeRemaining = this.#needsUpdate || this.#needsReset;
315
396
  if (this.#needsUpdate) {
316
397
  this.#updateSolver();
317
398
  this.#needsUpdate = false;
@@ -320,10 +401,15 @@ var Motion = class {
320
401
  this.#currentSolver.configure();
321
402
  this.#needsReset = false;
322
403
  }
404
+ if (needsTimeRemaining) this.#timeRemaining = this.#config.computeTimeRemaining(this.#state);
323
405
  this.#currentSolver.tick(dt);
406
+ this.#timeRemaining = Math.max(0, this.#timeRemaining - dt * 1e3);
324
407
  if (emit) {
325
408
  this.#emitter.emit("update");
326
- if (this.#state.resting) this.#emitter.emit("stop");
409
+ if (this.#state.isResting) {
410
+ this.#timeRemaining = 0;
411
+ this.#emitter.emit("stop");
412
+ }
327
413
  }
328
414
  }
329
415
  onUpdate(callback) {
@@ -358,37 +444,69 @@ var Motion = class {
358
444
  };
359
445
  //#endregion
360
446
  //#region src/spring.ts
361
- var Spring = class {
447
+ function normalizeTarget(target) {
448
+ if (target instanceof Spring) return {
449
+ spring: target,
450
+ offset: 0
451
+ };
452
+ if (typeof target === "object" && target !== null && "spring" in target && target.spring instanceof Spring) return {
453
+ spring: target.spring,
454
+ offset: target.offset ?? 0
455
+ };
456
+ return null;
457
+ }
458
+ var Spring = class Spring {
362
459
  #target;
363
460
  #config;
364
461
  #motion;
365
462
  #motions;
463
+ #leader = null;
464
+ #offset = 0;
465
+ #hasConfigOverride;
466
+ #unsubLeader = null;
366
467
  constructor(motions, position, config) {
367
- let target;
468
+ this.#motions = motions;
469
+ let numericTarget;
368
470
  let value;
471
+ let normalized = null;
369
472
  if (typeof position === "number") {
370
- target = position;
473
+ numericTarget = position;
371
474
  value = position;
372
475
  } else {
373
- target = position.target ?? position.value ?? 0;
374
- value = position.value ?? target;
476
+ normalized = normalizeTarget(position.target);
477
+ if (normalized) {
478
+ numericTarget = Spring.#rawValue(normalized.spring) + normalized.offset;
479
+ value = position.value ?? numericTarget;
480
+ } else {
481
+ numericTarget = position.target ?? position.value ?? 0;
482
+ value = position.value ?? numericTarget;
483
+ }
484
+ }
485
+ const hasOverride = config !== void 0;
486
+ this.#hasConfigOverride = hasOverride || !normalized;
487
+ if (normalized && !hasOverride) this.#config = normalized.spring.#config;
488
+ else this.#config = config;
489
+ this.#target = numericTarget;
490
+ this.#motion = new Motion(this.#config, value - numericTarget, 0);
491
+ if (!this.#motion.isResting) this.#motions.add(this.#motion);
492
+ if (normalized) {
493
+ this.#leader = normalized.spring;
494
+ this.#offset = normalized.offset;
495
+ this.#unsubLeader = normalized.spring.onUpdate(() => {
496
+ this.#setTarget(Spring.#rawValue(this.#leader) + this.#offset);
497
+ });
375
498
  }
376
- this.#target = target;
377
- this.#config = config;
378
- this.#motion = new Motion(config, value - target, 0);
379
- this.#motions = motions;
380
- if (!this.#motion.resting) this.#motions.add(this.#motion);
381
499
  }
382
500
  get target() {
383
501
  return this.#target;
384
502
  }
385
503
  set target(value) {
386
- if (value !== this.#target) {
387
- this.#motions.add(this.#motion);
388
- const currentValue = this.value;
389
- this.#target = value;
390
- this.#motion.position = currentValue - this.#target;
391
- this.#motion.tick(0);
504
+ if (typeof value === "number") {
505
+ this.#unfollow();
506
+ this.#setTarget(value);
507
+ } else {
508
+ const normalized = normalizeTarget(value);
509
+ this.#follow(normalized.spring, normalized.offset);
392
510
  }
393
511
  }
394
512
  get value() {
@@ -409,6 +527,9 @@ var Spring = class {
409
527
  this.#motions.add(this.#motion);
410
528
  this.#motion.velocity = value;
411
529
  }
530
+ get config() {
531
+ return this.#config;
532
+ }
412
533
  get mass() {
413
534
  return this.#config.mass;
414
535
  }
@@ -424,22 +545,32 @@ var Spring = class {
424
545
  get precision() {
425
546
  return this.#config.precision;
426
547
  }
427
- get resting() {
428
- return this.#motion.resting;
429
- }
430
548
  configure(config) {
431
- this.#config = config;
432
- this.#motion.configure(config);
549
+ if (this.#leader && !this.#hasConfigOverride) {
550
+ this.#hasConfigOverride = true;
551
+ this.#config = new SpringConfig({
552
+ tension: config.tension,
553
+ damping: config.damping,
554
+ mass: config.mass,
555
+ precision: config.precision
556
+ });
557
+ this.#motion.configure(this.#config);
558
+ } else SpringConfig.assign(this.#config, config);
433
559
  this.#motions.add(this.#motion);
434
560
  }
435
- onUpdate(callback) {
436
- return this.#motion.onUpdate(callback);
561
+ clearConfigOverride() {
562
+ if (this.#leader && this.#hasConfigOverride) {
563
+ this.#hasConfigOverride = false;
564
+ this.#config = this.#leader.#config;
565
+ this.#motion.configure(this.#config);
566
+ this.#motions.add(this.#motion);
567
+ }
437
568
  }
438
- onStart(callback) {
439
- return this.#motion.onStart(callback);
569
+ get timeRemaining() {
570
+ return this.#motion.timeRemaining;
440
571
  }
441
- onStop(callback) {
442
- return this.#motion.onStop(callback);
572
+ get isResting() {
573
+ return this.#motion.isResting;
443
574
  }
444
575
  jumpTo(value) {
445
576
  this.#target = value;
@@ -448,9 +579,254 @@ var Spring = class {
448
579
  this.#motion.tick(0);
449
580
  }
450
581
  dispose() {
582
+ if (this.#unsubLeader) {
583
+ this.#unsubLeader();
584
+ this.#unsubLeader = null;
585
+ }
451
586
  this.#motions.remove(this.#motion);
452
587
  this.#motion.dispose();
453
588
  }
589
+ onUpdate(callback) {
590
+ return this.#motion.onUpdate(callback);
591
+ }
592
+ onStart(callback) {
593
+ return this.#motion.onStart(callback);
594
+ }
595
+ onStop(callback) {
596
+ return this.#motion.onStop(callback);
597
+ }
598
+ static #rawValue(spring) {
599
+ return spring.#target + spring.#motion.position;
600
+ }
601
+ #setTarget(value) {
602
+ if (value !== this.#target) {
603
+ this.#motions.add(this.#motion);
604
+ const rawValue = this.#target + this.#motion.position;
605
+ this.#target = value;
606
+ this.#motion.position = rawValue - this.#target;
607
+ this.#motion.tick(0);
608
+ }
609
+ }
610
+ #follow(leader, offset) {
611
+ if (this.#unsubLeader) this.#unsubLeader();
612
+ this.#leader = leader;
613
+ this.#offset = offset;
614
+ if (!this.#hasConfigOverride) {
615
+ this.#config = leader.#config;
616
+ this.#motion.configure(this.#config);
617
+ }
618
+ this.#unsubLeader = leader.onUpdate(() => {
619
+ this.#setTarget(Spring.#rawValue(this.#leader) + this.#offset);
620
+ });
621
+ this.#setTarget(Spring.#rawValue(leader) + offset);
622
+ }
623
+ #unfollow() {
624
+ if (!this.#leader) return;
625
+ this.#unsubLeader();
626
+ this.#unsubLeader = null;
627
+ this.#leader = null;
628
+ this.#offset = 0;
629
+ if (!this.#hasConfigOverride) {
630
+ this.#hasConfigOverride = true;
631
+ this.#config = new SpringConfig({
632
+ tension: this.#config.tension,
633
+ damping: this.#config.damping,
634
+ mass: this.#config.mass,
635
+ precision: this.#config.precision
636
+ });
637
+ this.#motion.configure(this.#config);
638
+ }
639
+ }
640
+ };
641
+ //#endregion
642
+ //#region src/vector2.ts
643
+ function isVector2(value) {
644
+ return typeof value === "object" && value !== null && "x" in value && typeof value.x === "number";
645
+ }
646
+ //#endregion
647
+ //#region src/spring2d.ts
648
+ function normalizeTarget2D(target) {
649
+ if (target instanceof Spring2D) return {
650
+ spring: target,
651
+ offset: void 0
652
+ };
653
+ if (typeof target === "object" && target !== null && "spring" in target && target.spring instanceof Spring2D) return {
654
+ spring: target.spring,
655
+ offset: target.offset
656
+ };
657
+ return null;
658
+ }
659
+ function toScalarTarget(spring, offset) {
660
+ return offset ? {
661
+ spring,
662
+ offset
663
+ } : spring;
664
+ }
665
+ function toScalarPos(target, value) {
666
+ if (typeof target === "number") return value !== void 0 ? {
667
+ target,
668
+ value
669
+ } : target;
670
+ return {
671
+ target,
672
+ value
673
+ };
674
+ }
675
+ var Spring2D = class Spring2D {
676
+ #x;
677
+ #y;
678
+ #value = {
679
+ x: 0,
680
+ y: 0
681
+ };
682
+ #velocity = {
683
+ x: 0,
684
+ y: 0
685
+ };
686
+ constructor(motions, position, config) {
687
+ let xPos;
688
+ let yPos;
689
+ if (isVector2(position)) {
690
+ xPos = position.x;
691
+ yPos = position.y;
692
+ } else {
693
+ const normalized = normalizeTarget2D(position.target);
694
+ if (normalized) {
695
+ const lx = normalized.spring.#x;
696
+ const ly = normalized.spring.#y;
697
+ const xTarget = toScalarTarget(lx, normalized.offset?.x);
698
+ const yTarget = toScalarTarget(ly, normalized.offset?.y);
699
+ xPos = toScalarPos(xTarget, position.value?.x);
700
+ yPos = toScalarPos(yTarget, position.value?.y);
701
+ } else if (isVector2(position.target)) {
702
+ const t = position.target;
703
+ xPos = {
704
+ target: t.x,
705
+ value: position.value?.x
706
+ };
707
+ yPos = {
708
+ target: t.y,
709
+ value: position.value?.y
710
+ };
711
+ } else {
712
+ xPos = {
713
+ target: position.target,
714
+ value: position.value?.x
715
+ };
716
+ yPos = {
717
+ target: void 0,
718
+ value: position.value?.y
719
+ };
720
+ }
721
+ }
722
+ this.#x = new Spring(motions, xPos, config);
723
+ this.#y = new Spring(motions, yPos, config);
724
+ }
725
+ get target() {
726
+ return {
727
+ x: this.#x.target,
728
+ y: this.#y.target
729
+ };
730
+ }
731
+ set target(value) {
732
+ if (value instanceof Spring2D) {
733
+ this.#x.target = value.#x;
734
+ this.#y.target = value.#y;
735
+ } else if ("spring" in value && value.spring instanceof Spring2D) {
736
+ const { spring, offset } = value;
737
+ this.#x.target = toScalarTarget(spring.#x, offset?.x);
738
+ this.#y.target = toScalarTarget(spring.#y, offset?.y);
739
+ } else {
740
+ const v = value;
741
+ this.#x.target = v.x;
742
+ this.#y.target = v.y;
743
+ }
744
+ }
745
+ get value() {
746
+ this.#value.x = this.#x.value;
747
+ this.#value.y = this.#y.value;
748
+ return this.#value;
749
+ }
750
+ set value(value) {
751
+ this.#x.value = value.x;
752
+ this.#y.value = value.y;
753
+ }
754
+ get velocity() {
755
+ this.#velocity.x = this.#x.velocity;
756
+ this.#velocity.y = this.#y.velocity;
757
+ return this.#velocity;
758
+ }
759
+ set velocity(value) {
760
+ this.#x.velocity = value.x;
761
+ this.#y.velocity = value.y;
762
+ }
763
+ get config() {
764
+ return this.#x.config;
765
+ }
766
+ get mass() {
767
+ return this.#x.mass;
768
+ }
769
+ get tension() {
770
+ return this.#x.tension;
771
+ }
772
+ get damping() {
773
+ return this.#x.damping;
774
+ }
775
+ get dampingRatio() {
776
+ return this.#x.dampingRatio;
777
+ }
778
+ get precision() {
779
+ return this.#x.precision;
780
+ }
781
+ configure(config) {
782
+ this.#x.configure(config);
783
+ this.#y.configure(config);
784
+ }
785
+ clearConfigOverride() {
786
+ this.#x.clearConfigOverride();
787
+ this.#y.clearConfigOverride();
788
+ }
789
+ get timeRemaining() {
790
+ return Math.max(this.#x.timeRemaining, this.#y.timeRemaining);
791
+ }
792
+ get isResting() {
793
+ return this.#x.isResting && this.#y.isResting;
794
+ }
795
+ jumpTo(value) {
796
+ this.#x.jumpTo(value.x);
797
+ this.#y.jumpTo(value.y);
798
+ }
799
+ dispose() {
800
+ this.#x.dispose();
801
+ this.#y.dispose();
802
+ }
803
+ onUpdate(callback) {
804
+ const a = this.#x.onUpdate(callback);
805
+ const b = this.#y.onUpdate(callback);
806
+ return () => {
807
+ a();
808
+ b();
809
+ };
810
+ }
811
+ onStart(callback) {
812
+ const a = this.#x.onStart(callback);
813
+ const b = this.#y.onStart(callback);
814
+ return () => {
815
+ a();
816
+ b();
817
+ };
818
+ }
819
+ onStop(callback) {
820
+ const check = () => {
821
+ if (this.isResting) callback();
822
+ };
823
+ const a = this.#x.onStop(check);
824
+ const b = this.#y.onStop(check);
825
+ return () => {
826
+ a();
827
+ b();
828
+ };
829
+ }
454
830
  };
455
831
  //#endregion
456
832
  //#region src/ticker.ts
@@ -570,12 +946,15 @@ var SpringSystemImpl = class {
570
946
  #motion;
571
947
  #ticker;
572
948
  constructor(options) {
573
- this.#motion = new MotionSet();
949
+ this.#motion = new MotionSet(options?.debug);
574
950
  this.#ticker = new Ticker(this.#motion, options);
575
951
  }
576
952
  createSpring(position, config) {
577
953
  return new Spring(this.#motion, position, config);
578
954
  }
955
+ createSpring2D(position, config) {
956
+ return new Spring2D(this.#motion, position, config);
957
+ }
579
958
  advance(dt) {
580
959
  this.#motion.tick(dt / 1e3);
581
960
  }