coily 0.10.1 → 0.11.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/api.mjs DELETED
@@ -1,460 +0,0 @@
1
- class Scheduler {
2
- #solvers = /* @__PURE__ */ new Set();
3
- tick(delta) {
4
- for (const solver of this.#solvers.values()) {
5
- solver.tick(delta);
6
- if (solver.resting) {
7
- this.#solvers.delete(solver);
8
- }
9
- }
10
- }
11
- add(solver) {
12
- this.#solvers.add(solver);
13
- }
14
- remove(solver) {
15
- this.#solvers.delete(solver);
16
- }
17
- has(solver) {
18
- return this.#solvers.has(solver);
19
- }
20
- }
21
-
22
- class Emitter {
23
- #handlers = /* @__PURE__ */ new Map();
24
- on(type, handler) {
25
- const handlers = this.#handlers.get(type);
26
- if (handlers) {
27
- handlers.push(handler);
28
- } else {
29
- this.#handlers.set(type, [handler]);
30
- }
31
- return () => {
32
- this.off(type, handler);
33
- };
34
- }
35
- off(type, handler) {
36
- const handlers = this.#handlers.get(type);
37
- if (handlers) {
38
- if (handler) {
39
- handlers.splice(handlers.indexOf(handler) >>> 0, 1);
40
- } else {
41
- this.#handlers.set(type, []);
42
- }
43
- }
44
- }
45
- emit(type, evt) {
46
- const handlers = this.#handlers?.get(type);
47
- if (handlers) {
48
- handlers.slice().map((handler) => {
49
- handler(evt);
50
- });
51
- }
52
- }
53
- }
54
-
55
- function invariant(condition, message) {
56
- if (condition) {
57
- return;
58
- }
59
- throw new Error(message ?? "Invariant Failed");
60
- }
61
- function roundTo(value, precision) {
62
- const p = 10 ** precision;
63
- return Math.round(value * p) / p;
64
- }
65
-
66
- class State {
67
- #precision;
68
- #restingMagnitude;
69
- #position;
70
- #velocity;
71
- constructor(position, velocity, precision) {
72
- this.#precision = precision;
73
- this.#restingMagnitude = 1 / 10 ** this.#precision;
74
- this.#position = roundTo(position, this.#precision);
75
- this.#velocity = roundTo(velocity, this.#precision);
76
- }
77
- get position() {
78
- return this.#position;
79
- }
80
- set position(value) {
81
- this.#position = roundTo(value, this.#precision);
82
- }
83
- get velocity() {
84
- return this.#velocity;
85
- }
86
- set velocity(value) {
87
- this.#velocity = roundTo(value, this.#precision);
88
- }
89
- get precision() {
90
- return this.#precision;
91
- }
92
- set precision(value) {
93
- this.#precision = value;
94
- this.#restingMagnitude = 1 / 10 ** this.#precision;
95
- this.#position = roundTo(this.#position, this.#precision);
96
- this.#velocity = roundTo(this.#velocity, this.#precision);
97
- }
98
- get resting() {
99
- return Math.abs(this.#velocity) < this.#restingMagnitude && Math.abs(this.#position) < this.#restingMagnitude;
100
- }
101
- }
102
-
103
- class Solver {
104
- #mass;
105
- #tension;
106
- #damping;
107
- #state;
108
- #underdampedSolver = null;
109
- #criticallyDampedSolver = null;
110
- #overdampedSolver = null;
111
- #currentSolver;
112
- #needsUpdate = false;
113
- #needsReset = false;
114
- #emitter;
115
- constructor(options) {
116
- this.#mass = options.mass;
117
- this.#tension = options.tension;
118
- this.#damping = options.damping;
119
- this.#state = new State(
120
- options.position,
121
- options.velocity,
122
- options.precision
123
- );
124
- this.#emitter = new Emitter();
125
- this.#updateSolver();
126
- }
127
- get mass() {
128
- return this.#mass;
129
- }
130
- set mass(value) {
131
- this.#mass = value;
132
- this.#needsUpdate = true;
133
- }
134
- get tension() {
135
- return this.#tension;
136
- }
137
- set tension(value) {
138
- this.#tension = value;
139
- this.#needsUpdate = true;
140
- }
141
- get damping() {
142
- return this.#damping;
143
- }
144
- set damping(value) {
145
- this.#damping = value;
146
- this.#needsUpdate = true;
147
- }
148
- get position() {
149
- return this.#state.position;
150
- }
151
- set position(value) {
152
- this.#state.position = value;
153
- this.#needsReset = true;
154
- if (!this.#state.resting) {
155
- this.#emitter.emit("start");
156
- }
157
- }
158
- get velocity() {
159
- return this.#state.velocity;
160
- }
161
- set velocity(value) {
162
- this.#state.velocity = value;
163
- this.#needsReset = true;
164
- }
165
- get precision() {
166
- return this.#state.precision;
167
- }
168
- set precision(value) {
169
- this.#state.precision = value;
170
- }
171
- get resting() {
172
- return this.#state.resting;
173
- }
174
- get naturalFrequency() {
175
- return Math.sqrt(this.#tension / this.#mass);
176
- }
177
- get criticalDamping() {
178
- return 2 * this.#mass * this.naturalFrequency;
179
- }
180
- get dampingRatio() {
181
- return this.#damping / this.criticalDamping;
182
- }
183
- tick(dt, emit = true) {
184
- if (this.#needsUpdate) {
185
- this.#updateSolver();
186
- this.#needsUpdate = false;
187
- }
188
- if (this.#needsReset) {
189
- this.#currentSolver.reset();
190
- this.#needsReset = false;
191
- }
192
- this.#currentSolver.tick(dt);
193
- if (emit) {
194
- this.#emitter.emit("update");
195
- if (this.#state.resting) {
196
- this.#emitter.emit("stop");
197
- }
198
- }
199
- }
200
- onUpdate(callback) {
201
- return this.#emitter.on("update", callback);
202
- }
203
- onStart(callback) {
204
- return this.#emitter.on("start", callback);
205
- }
206
- onStop(callback) {
207
- return this.#emitter.on("stop", callback);
208
- }
209
- #updateSolver() {
210
- if (this.dampingRatio < 1) {
211
- this.#underdampedSolver ||= new UnderdampedSolver(this, this.#state);
212
- this.#currentSolver = this.#underdampedSolver;
213
- } else if (this.dampingRatio === 1) {
214
- this.#criticallyDampedSolver ||= new CriticallyDampedSolver(
215
- this,
216
- this.#state
217
- );
218
- this.#currentSolver = this.#criticallyDampedSolver;
219
- } else {
220
- this.#overdampedSolver ||= new OverdampedSolver(this, this.#state);
221
- this.#currentSolver = this.#overdampedSolver;
222
- }
223
- }
224
- }
225
- class UnderdampedSolver {
226
- #solver;
227
- #state;
228
- #dampedFrequency;
229
- #t = 0;
230
- #c1 = 0;
231
- #c2 = 0;
232
- constructor(solver, state) {
233
- this.#solver = solver;
234
- this.#state = state;
235
- this.reset();
236
- }
237
- reset() {
238
- this.#dampedFrequency = this.#solver.naturalFrequency * Math.sqrt(1 - this.#solver.dampingRatio ** 2);
239
- this.#t = 0;
240
- this.#c1 = this.#state.position;
241
- this.#c2 = (this.#state.velocity + this.#solver.dampingRatio * this.#solver.naturalFrequency * this.#state.position) / this.#dampedFrequency;
242
- this.tick(0);
243
- }
244
- tick(dt) {
245
- this.#t += dt;
246
- const sin = Math.sin(this.#dampedFrequency * this.#t);
247
- const cos = Math.cos(this.#dampedFrequency * this.#t);
248
- const decay = Math.exp(
249
- -this.#solver.dampingRatio * this.#solver.naturalFrequency * this.#t
250
- );
251
- const decayVelocity = -this.#solver.dampingRatio * this.#solver.naturalFrequency * decay;
252
- const oscillation = this.#c1 * cos + this.#c2 * sin;
253
- const oscillationVelocity = -this.#c1 * this.#dampedFrequency * sin + this.#c2 * this.#dampedFrequency * cos;
254
- this.#state.position = decay * oscillation;
255
- this.#state.velocity = decay * oscillationVelocity + decayVelocity * oscillation;
256
- }
257
- }
258
- class CriticallyDampedSolver {
259
- #solver;
260
- #state;
261
- #t = 0;
262
- #c1 = 0;
263
- #c2 = 0;
264
- constructor(solver, state) {
265
- this.#solver = solver;
266
- this.#state = state;
267
- this.reset();
268
- }
269
- reset() {
270
- this.#t = 0;
271
- this.#c1 = this.#state.position;
272
- this.#c2 = this.#state.velocity + this.#solver.naturalFrequency * this.#state.position;
273
- this.tick(0);
274
- }
275
- tick(dt) {
276
- this.#t += dt;
277
- const decay = Math.exp(-this.#solver.naturalFrequency * this.#t);
278
- const decayVelocity = -this.#solver.naturalFrequency * decay;
279
- const scale = this.#c1 + this.#c2 * this.#t;
280
- const scaleVelocity = this.#c2;
281
- this.#state.position = scale * decay;
282
- this.#state.velocity = scaleVelocity * decay + decayVelocity * scale;
283
- }
284
- }
285
- class OverdampedSolver {
286
- #solver;
287
- #state;
288
- #dampedFrequency;
289
- #t = 0;
290
- #c1 = 0;
291
- #c2 = 0;
292
- constructor(solver, state) {
293
- this.#solver = solver;
294
- this.#state = state;
295
- this.reset();
296
- }
297
- reset() {
298
- this.#dampedFrequency = this.#solver.naturalFrequency * Math.sqrt(this.#solver.dampingRatio ** 2 - 1);
299
- this.#t = 0;
300
- this.#c1 = this.#state.velocity + this.#solver.dampingRatio * this.#solver.naturalFrequency * this.#state.position;
301
- this.#c2 = this.#state.position * this.#dampedFrequency;
302
- }
303
- tick(dt) {
304
- this.#t += dt;
305
- const decay = Math.exp(
306
- -this.#solver.dampingRatio * this.#solver.naturalFrequency * this.#t
307
- );
308
- const decayVelocity = -this.#solver.dampingRatio * this.#solver.naturalFrequency * decay;
309
- const clamped = Math.min(this.#dampedFrequency * this.#t, 300);
310
- const sinh = Math.sinh(clamped);
311
- const cosh = Math.cosh(clamped);
312
- const scale = this.#c1 * sinh + this.#c2 * cosh;
313
- const scaleVelocity = -this.#c1 * this.#dampedFrequency * sinh + this.#c2 * this.#dampedFrequency * cosh;
314
- this.#state.position = scale * decay / this.#dampedFrequency;
315
- this.#state.velocity = (scale * decayVelocity + scaleVelocity * decay) / this.#dampedFrequency;
316
- }
317
- }
318
-
319
- class Spring {
320
- #target;
321
- #solver;
322
- #scheduler;
323
- constructor(scheduler, options) {
324
- invariant(options.mass > 0, "Mass must be greater than 0");
325
- invariant(options.tension > 0, "Tension must be greater than 0");
326
- invariant(
327
- options.damping >= 0,
328
- "Damping must be greater than or equal to 0"
329
- );
330
- invariant(
331
- options.precision === void 0 || options.precision > 0,
332
- "Precision must be greater than 0"
333
- );
334
- this.#target = options.target ?? options.value ?? 0;
335
- this.#solver = new Solver({
336
- mass: options.mass,
337
- tension: options.tension,
338
- damping: options.damping,
339
- position: (options.value ?? this.#target) - this.#target,
340
- velocity: 0,
341
- precision: options.precision ?? 2
342
- });
343
- this.#scheduler = scheduler;
344
- if (!this.#solver.resting) {
345
- this.#scheduler.add(this.#solver);
346
- }
347
- }
348
- get target() {
349
- return this.#target;
350
- }
351
- set target(value) {
352
- if (value !== this.#target) {
353
- if (!this.#scheduler.has(this.#solver)) {
354
- this.#scheduler.add(this.#solver);
355
- }
356
- const currentValue = this.value;
357
- this.#target = value;
358
- this.#solver.position = currentValue - this.#target;
359
- this.#solver.tick(0);
360
- }
361
- }
362
- get value() {
363
- return this.#target + this.#solver.position;
364
- }
365
- set value(value) {
366
- const position = value - this.#target;
367
- if (position !== this.#solver.position) {
368
- if (!this.#scheduler.has(this.#solver)) {
369
- this.#scheduler.add(this.#solver);
370
- }
371
- this.#solver.position = position;
372
- this.#solver.tick(0);
373
- }
374
- }
375
- get velocity() {
376
- return this.#solver.velocity;
377
- }
378
- set velocity(value) {
379
- if (!this.#scheduler.has(this.#solver)) {
380
- this.#scheduler.add(this.#solver);
381
- }
382
- this.#solver.velocity = value;
383
- }
384
- get mass() {
385
- return this.#solver.mass;
386
- }
387
- set mass(value) {
388
- invariant(value > 0, "Mass must be greater than 0");
389
- if (!this.#scheduler.has(this.#solver)) {
390
- this.#scheduler.add(this.#solver);
391
- }
392
- this.#solver.mass = value;
393
- }
394
- get tension() {
395
- return this.#solver.tension;
396
- }
397
- set tension(value) {
398
- invariant(value > 0, "Tension must be greater than 0");
399
- if (!this.#scheduler.has(this.#solver)) {
400
- this.#scheduler.add(this.#solver);
401
- }
402
- this.#solver.tension = value;
403
- }
404
- get damping() {
405
- return this.#solver.damping;
406
- }
407
- set damping(value) {
408
- invariant(value >= 0, "Damping must be greater than or equal to 0");
409
- if (!this.#scheduler.has(this.#solver)) {
410
- this.#scheduler.add(this.#solver);
411
- }
412
- this.#solver.damping = value;
413
- }
414
- get precision() {
415
- return this.#solver.precision;
416
- }
417
- set precision(value) {
418
- invariant(value > 0, "Precision must be greater than 0");
419
- if (!this.#scheduler.has(this.#solver)) {
420
- this.#scheduler.add(this.#solver);
421
- }
422
- this.#solver.precision = value;
423
- }
424
- get resting() {
425
- return this.#solver.resting;
426
- }
427
- onUpdate(callback) {
428
- return this.#solver.onUpdate(callback);
429
- }
430
- onStart(callback) {
431
- return this.#solver.onStart(callback);
432
- }
433
- onStop(callback) {
434
- return this.#solver.onStop(callback);
435
- }
436
- jumpTo(value) {
437
- this.#target = value;
438
- this.#solver.position = 0;
439
- this.#solver.velocity = 0;
440
- this.#solver.tick(0);
441
- }
442
- }
443
-
444
- class SpringSystemImpl {
445
- #scheduler;
446
- constructor() {
447
- this.#scheduler = new Scheduler();
448
- }
449
- createSpring(options) {
450
- return new Spring(this.#scheduler, options);
451
- }
452
- tick(dt) {
453
- this.#scheduler.tick(dt);
454
- }
455
- }
456
- function createSpringSystem() {
457
- return new SpringSystemImpl();
458
- }
459
-
460
- export { createSpringSystem };
package/dist/loop.d.mts DELETED
@@ -1,5 +0,0 @@
1
- import { SpringSystem } from './api.mjs';
2
-
3
- declare function start(system: SpringSystem): () => void;
4
-
5
- export { start };
package/dist/loop.d.ts DELETED
@@ -1,5 +0,0 @@
1
- import { SpringSystem } from './api.js';
2
-
3
- declare function start(system: SpringSystem): () => void;
4
-
5
- export { start };
package/dist/loop.mjs DELETED
@@ -1,14 +0,0 @@
1
- import { createTicker } from 'tickloop';
2
-
3
- function start(system) {
4
- const ticker = createTicker();
5
- ticker.start();
6
- ticker.add((_, delta) => {
7
- system.tick(delta / 1e3);
8
- });
9
- return () => {
10
- ticker.stop();
11
- };
12
- }
13
-
14
- export { start };