@msb235/blits 0.13.0 → 0.15.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/CHANGELOG.md +183 -0
- package/README.md +20 -3
- package/dist/ahead.d.ts +26 -0
- package/dist/ahead.js +264 -0
- package/dist/around.d.ts +11 -0
- package/dist/around.js +32 -0
- package/dist/bare.js +15 -7
- package/dist/blank.d.ts +10 -0
- package/dist/blank.js +26 -0
- package/dist/blend.d.ts +2 -1
- package/dist/blend.js +1 -6
- package/dist/bound.d.ts +70 -0
- package/dist/bound.js +351 -0
- package/dist/chain.d.ts +2 -1
- package/dist/chain.js +7 -5
- package/dist/changes.d.ts +24 -0
- package/dist/changes.js +189 -0
- package/dist/channels.d.ts +39 -13
- package/dist/channels.js +14 -5
- package/dist/closed.d.ts +1 -0
- package/dist/closed.js +45 -1
- package/dist/color.d.ts +12 -10
- package/dist/color.js +25 -25
- package/dist/columns.js +7 -3
- package/dist/contribution.d.ts +30 -0
- package/dist/contribution.js +374 -0
- package/dist/copyout.d.ts +10 -0
- package/dist/copyout.js +69 -24
- package/dist/crowd.d.ts +8 -2
- package/dist/crowd.js +4 -5
- package/dist/cue.d.ts +8 -1
- package/dist/cue.js +47 -14
- package/dist/cut.d.ts +8 -0
- package/dist/cut.js +4 -0
- package/dist/drop.d.ts +9 -0
- package/dist/drop.js +84 -0
- package/dist/due.d.ts +24 -5
- package/dist/due.js +113 -33
- package/dist/easing.d.ts +4 -4
- package/dist/easing.js +3 -3
- package/dist/everyone.d.ts +3 -2
- package/dist/everyone.js +3 -3
- package/dist/fade.d.ts +2 -1
- package/dist/fade.js +2 -1
- package/dist/fill.d.ts +3 -2
- package/dist/fill.js +51 -19
- package/dist/fit.d.ts +44 -26
- package/dist/fit.js +10 -9
- package/dist/fling.d.ts +66 -0
- package/dist/fling.js +162 -0
- package/dist/fold.d.ts +4 -37
- package/dist/fold.js +5 -512
- package/dist/gather.d.ts +12 -7
- package/dist/gather.js +179 -44
- package/dist/gone.js +2 -2
- package/dist/grid.d.ts +47 -0
- package/dist/grid.js +247 -0
- package/dist/handle.d.ts +3 -0
- package/dist/handle.js +19 -8
- package/dist/held.d.ts +2 -1
- package/dist/history.d.ts +6 -5
- package/dist/history.js +9 -9
- package/dist/hosts.js +9 -15
- package/dist/index.d.ts +5 -4
- package/dist/keyfill.d.ts +1 -1
- package/dist/keyfill.js +5 -3
- package/dist/keyrows.d.ts +12 -0
- package/dist/keyrows.js +99 -12
- package/dist/lane.d.ts +36 -6
- package/dist/lane.js +32 -1
- package/dist/lanes.d.ts +23 -8
- package/dist/lanes.js +29 -8
- package/dist/leavings.d.ts +2 -2
- package/dist/leavings.js +1 -1
- package/dist/lives.d.ts +47 -0
- package/dist/lives.js +180 -0
- package/dist/meet.d.ts +2 -2
- package/dist/meet.js +90 -4
- package/dist/mixer.d.ts +61 -35
- package/dist/mixer.js +64 -169
- package/dist/motion.d.ts +19 -106
- package/dist/motion.js +60 -13
- package/dist/motions.d.ts +62 -150
- package/dist/motions.js +57 -369
- package/dist/move.js +24 -1
- package/dist/moved.d.ts +1 -1
- package/dist/moved.js +16 -7
- package/dist/named.d.ts +0 -1
- package/dist/named.js +20 -2
- package/dist/numbers.d.ts +2 -0
- package/dist/numbers.js +4 -0
- package/dist/owed.d.ts +2 -0
- package/dist/owed.js +4 -0
- package/dist/pack.d.ts +3 -1
- package/dist/pack.js +4 -3
- package/dist/patch.d.ts +7 -3
- package/dist/patch.js +6 -2
- package/dist/places.d.ts +53 -0
- package/dist/places.js +171 -0
- package/dist/plot.d.ts +12 -0
- package/dist/plot.js +52 -0
- package/dist/project.d.ts +4 -4
- package/dist/project.js +9 -211
- package/dist/pull.js +11 -8
- package/dist/qualify.js +84 -21
- package/dist/reading.d.ts +6 -0
- package/dist/reading.js +5 -0
- package/dist/record.d.ts +5 -1
- package/dist/record.js +4 -2
- package/dist/relink.d.ts +1 -1
- package/dist/restored.d.ts +2 -2
- package/dist/restored.js +1 -1
- package/dist/rests.d.ts +9 -0
- package/dist/rests.js +76 -0
- package/dist/revive.d.ts +3 -2
- package/dist/revive.js +2 -2
- package/dist/{locus.d.ts → role.d.ts} +10 -9
- package/dist/{locus.js → role.js} +3 -3
- package/dist/roles.d.ts +10 -0
- package/dist/roles.js +158 -0
- package/dist/roster.js +3 -3
- package/dist/rotation.d.ts +1 -1
- package/dist/rotation.js +1 -1
- package/dist/rows.js +1 -1
- package/dist/sample.d.ts +2 -2
- package/dist/sample.js +33 -7
- package/dist/seek.js +13 -5
- package/dist/setting.d.ts +27 -0
- package/dist/setting.js +55 -0
- package/dist/shared.d.ts +1 -1
- package/dist/shared.js +1 -1
- package/dist/signals.d.ts +64 -32
- package/dist/signals.js +20 -18
- package/dist/stack.d.ts +5 -5
- package/dist/stack.js +5 -5
- package/dist/standing.d.ts +2 -1
- package/dist/standing.js +3 -2
- package/dist/store.d.ts +9 -0
- package/dist/store.js +24 -0
- package/dist/strays.d.ts +2 -1
- package/dist/strays.js +21 -14
- package/dist/stretch.d.ts +112 -0
- package/dist/stretch.js +9 -0
- package/dist/subject.d.ts +142 -0
- package/dist/subject.js +1 -0
- package/dist/tape.d.ts +9 -5
- package/dist/tape.js +5 -1
- package/dist/then.d.ts +9 -0
- package/dist/then.js +57 -0
- package/dist/ticker.d.ts +4 -4
- package/dist/transport.d.ts +0 -8
- package/dist/transport.js +0 -4
- package/dist/turns.d.ts +4 -3
- package/dist/turns.js +4 -3
- package/dist/types/arc.d.ts +12 -11
- package/dist/types/channel.d.ts +12 -8
- package/dist/types/history.d.ts +35 -19
- package/dist/types/mix.d.ts +222 -161
- package/dist/types/motion.d.ts +151 -0
- package/dist/types/motion.js +1 -0
- package/dist/types/patch.d.ts +69 -34
- package/dist/types/place.d.ts +51 -36
- package/dist/types/transport.d.ts +20 -18
- package/dist/types/voice.d.ts +145 -72
- package/dist/types/watch.d.ts +88 -40
- package/dist/types.d.ts +1 -0
- package/dist/voice.d.ts +9 -141
- package/dist/voice.js +30 -79
- package/dist/warn.d.ts +2 -0
- package/dist/warn.js +1 -1
- package/dist/watch.d.ts +2 -2
- package/dist/watch.js +5 -3
- package/dist/wave.d.ts +4 -3
- package/dist/wave.js +3 -2
- package/dist/weigh.d.ts +7 -1
- package/dist/weigh.js +27 -0
- package/docs/schema.html +223 -37
- package/package.json +6 -2
package/dist/fill.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
import { runBounded } from './bound.js';
|
|
1
2
|
import { foldNumber, lerpNumber } from './channels.js';
|
|
2
3
|
import { passAt, phaseAt, same, silent, weighed } from './clock.js';
|
|
3
4
|
import { flush } from './columns.js';
|
|
4
5
|
import { crowdsUpTo, freshen } from './crowd.js';
|
|
5
6
|
import { open as openRecord, shut as shutRecord } from './everyone.js';
|
|
6
7
|
import { unband } from './fold.js';
|
|
7
|
-
import {
|
|
8
|
+
import { foldRole, gatherRole, runRole } from './gather.js';
|
|
8
9
|
import { runKeys } from './keyfill.js';
|
|
10
|
+
import { keepProbed } from './lane.js';
|
|
9
11
|
import { reach } from './meet.js';
|
|
10
12
|
import { absent } from './numbers.js';
|
|
11
13
|
import { AT, NOTHING, readKeys, seg, segment, shifted } from './patch.js';
|
|
@@ -15,6 +17,8 @@ import { rested } from './turns.js';
|
|
|
15
17
|
// V8 reads a local const as a constant, where it reads an import from its module on every call.
|
|
16
18
|
const open = openRecord;
|
|
17
19
|
const shut = shutRecord;
|
|
20
|
+
/** The last `Arg.MARK` handed out, by every mix's lanes, so no two placings share one. */
|
|
21
|
+
let marks = 0;
|
|
18
22
|
/** The least prime at least `n`, and 1 below 2. */
|
|
19
23
|
function primeFrom(n) {
|
|
20
24
|
if (n < 2)
|
|
@@ -34,6 +38,7 @@ export function fillAll(lanes, now, version) {
|
|
|
34
38
|
flush(lanes);
|
|
35
39
|
const host = lanes.host;
|
|
36
40
|
lanes.filling = true;
|
|
41
|
+
const lookups = lanes.lookups;
|
|
37
42
|
try {
|
|
38
43
|
lanes.fills++;
|
|
39
44
|
lanes.owed.begin(lanes.fills);
|
|
@@ -84,17 +89,21 @@ export function fillAll(lanes, now, version) {
|
|
|
84
89
|
if (busy) {
|
|
85
90
|
for (const ch of lanes.laned)
|
|
86
91
|
rested(ch, size);
|
|
87
|
-
for (const g of lanes.
|
|
88
|
-
|
|
92
|
+
for (const g of lanes.roles)
|
|
93
|
+
gatherRole(lanes, g);
|
|
89
94
|
for (const lane of lanes.lanes) {
|
|
90
95
|
if (lane.idle)
|
|
91
96
|
continue;
|
|
92
|
-
if (lanes.crowds.length > 0)
|
|
97
|
+
if (lanes.crowds.length > 0) {
|
|
98
|
+
lanes.arg[6 /* Arg.MARK */] = Number.NaN;
|
|
93
99
|
crowdsUpTo(lanes, lane.voice.id);
|
|
100
|
+
}
|
|
94
101
|
lanes.run(lane);
|
|
95
102
|
}
|
|
96
|
-
if (lanes.crowds.length > 0)
|
|
103
|
+
if (lanes.crowds.length > 0) {
|
|
104
|
+
lanes.arg[6 /* Arg.MARK */] = Number.NaN;
|
|
97
105
|
crowdsUpTo(lanes, Number.POSITIVE_INFINITY);
|
|
106
|
+
}
|
|
98
107
|
for (const c of lanes.crowds)
|
|
99
108
|
if (!c.idle) {
|
|
100
109
|
c.bareFill = lanes.fills;
|
|
@@ -111,6 +120,7 @@ export function fillAll(lanes, now, version) {
|
|
|
111
120
|
}
|
|
112
121
|
lanes.moved = reading.moved;
|
|
113
122
|
lanes.inputs = reading.inputs;
|
|
123
|
+
lanes.holds = lanes.lookups !== lookups;
|
|
114
124
|
}
|
|
115
125
|
finally {
|
|
116
126
|
lanes.filling = false;
|
|
@@ -162,10 +172,7 @@ export function rest(lanes, lane) {
|
|
|
162
172
|
continue;
|
|
163
173
|
reach(lanes, slot, 1);
|
|
164
174
|
// A probe read this subject from the last fill, which this lane's weight stays from.
|
|
165
|
-
|
|
166
|
-
const o = p * 8 /* Row.STRIDE */;
|
|
167
|
-
data[o + 3 /* Row.PROBED */] = data[o + 2 /* Row.WEIGHT */];
|
|
168
|
-
}
|
|
175
|
+
keepProbed(data, p * 8 /* Row.STRIDE */, lanes.per, slot * 6 /* Per.SLOT */, lanes.fills);
|
|
169
176
|
}
|
|
170
177
|
}
|
|
171
178
|
export function wake(lanes, lane) {
|
|
@@ -205,6 +212,7 @@ export function parting(voice, slot) {
|
|
|
205
212
|
return subject === absent ? 1 : this.host.parting(voice, subject);
|
|
206
213
|
}
|
|
207
214
|
export function subjectAt(slot) {
|
|
215
|
+
this.lookups++;
|
|
208
216
|
let subject = this.subjects[slot];
|
|
209
217
|
if (subject === undefined) {
|
|
210
218
|
subject = this.numbers.subject(slot);
|
|
@@ -217,6 +225,7 @@ export function subjectAt(slot) {
|
|
|
217
225
|
export function reset(lane) {
|
|
218
226
|
const voice = lane.voice;
|
|
219
227
|
lane.placed = false;
|
|
228
|
+
this.arg[6 /* Arg.MARK */] = Number.NaN;
|
|
220
229
|
lane.read = false;
|
|
221
230
|
lane.weighed = false;
|
|
222
231
|
// With no fade in or out the envelope is 1 for every subject, which is what it would return.
|
|
@@ -232,11 +241,16 @@ export function run(lane) {
|
|
|
232
241
|
const voice = lane.voice;
|
|
233
242
|
if (voice.state === 'pending' || voice.state === 'done')
|
|
234
243
|
return;
|
|
235
|
-
|
|
236
|
-
|
|
244
|
+
const g = lane.group;
|
|
245
|
+
if (g !== null) {
|
|
246
|
+
if (!g.inline)
|
|
247
|
+
foldRole(this, lane, g);
|
|
248
|
+
else if (lane === g.members[0])
|
|
249
|
+
runRole(this, g);
|
|
237
250
|
return;
|
|
238
251
|
}
|
|
239
252
|
this.reset(lane);
|
|
253
|
+
lane.ran = this.fills;
|
|
240
254
|
if (lane.motion !== undefined && !voice.keeping) {
|
|
241
255
|
runMotion(this, lane, lane.motion);
|
|
242
256
|
return;
|
|
@@ -251,6 +265,10 @@ export function run(lane) {
|
|
|
251
265
|
this.runEveryone(lane);
|
|
252
266
|
return;
|
|
253
267
|
}
|
|
268
|
+
if (voice.bound !== null) {
|
|
269
|
+
runBounded(this, lane, voice.bound);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
254
272
|
for (let p = 0; p < list.length; p++) {
|
|
255
273
|
this.one(lane, p, list[p]);
|
|
256
274
|
// Its patch just made kept state: no further call this fill, the general path makes them.
|
|
@@ -281,7 +299,7 @@ export function runEveryone(lane) {
|
|
|
281
299
|
/**
|
|
282
300
|
* One subject's contribution from a lane's voice. True where the voice gave the subject a delta,
|
|
283
301
|
* as the general path's `contribution` does where it returns one, which is what makes a voice in a
|
|
284
|
-
*
|
|
302
|
+
* role one of its members for the subject.
|
|
285
303
|
*/
|
|
286
304
|
export function one(lane, p, slot) {
|
|
287
305
|
const host = this.host;
|
|
@@ -290,9 +308,7 @@ export function one(lane, p, slot) {
|
|
|
290
308
|
const arg = this.arg;
|
|
291
309
|
const data = lane.data;
|
|
292
310
|
const o = p * 8 /* Row.STRIDE */;
|
|
293
|
-
|
|
294
|
-
if (this.per[slot * 6 /* Per.SLOT */ + 5 /* Per.LANE_FILL */] === this.fills - 1)
|
|
295
|
-
data[o + 3 /* Row.PROBED */] = data[o + 2 /* Row.WEIGHT */];
|
|
311
|
+
keepProbed(data, o, this.per, slot * 6 /* Per.SLOT */, this.fills);
|
|
296
312
|
if (this.lately >= 0 && this.unread(slot))
|
|
297
313
|
return false;
|
|
298
314
|
const delay = data[o + 0 /* Row.DELAY */];
|
|
@@ -319,6 +335,8 @@ export function one(lane, p, slot) {
|
|
|
319
335
|
lane.placedAt = elapsed;
|
|
320
336
|
lane.phase = phaseAt(elapsed, voice.duration, voice.passes);
|
|
321
337
|
lane.pass = passAt(elapsed, voice.duration, voice.passes);
|
|
338
|
+
arg[6 /* Arg.MARK */] = ++marks;
|
|
339
|
+
lane.mark = arg[6 /* Arg.MARK */];
|
|
322
340
|
}
|
|
323
341
|
const since = data[o + 1 /* Row.SINCE */];
|
|
324
342
|
if (!lane.flat && (!lane.weighed || !same(since, lane.weighedSince))) {
|
|
@@ -352,6 +370,14 @@ export function one(lane, p, slot) {
|
|
|
352
370
|
lane.readAt = elapsed;
|
|
353
371
|
this.gather(lane, lane.delta);
|
|
354
372
|
}
|
|
373
|
+
// A signal is handed the gap since the subject's last read, which this read is, as it is on
|
|
374
|
+
// the general path; the record's delta is not this read's, so a probe reads it again.
|
|
375
|
+
if (typeof voice.spec.weight === 'function') {
|
|
376
|
+
const rec = lane.records[p];
|
|
377
|
+
rec.probed = this.now;
|
|
378
|
+
rec.seeks = voice.seeks;
|
|
379
|
+
rec.delta = null;
|
|
380
|
+
}
|
|
355
381
|
if (w > 0) {
|
|
356
382
|
arg[0 /* Arg.WEIGHT */] = w;
|
|
357
383
|
this.fold(lane, slot);
|
|
@@ -359,8 +385,9 @@ export function one(lane, p, slot) {
|
|
|
359
385
|
return true;
|
|
360
386
|
}
|
|
361
387
|
const rec = lane.records[p];
|
|
362
|
-
// The general path makes a voice's first call for a subject, where it first sees it play
|
|
363
|
-
|
|
388
|
+
// The general path makes a voice's first call for a subject, where it first sees it play, or a
|
|
389
|
+
// probe meeting the voice makes it here, or the fill of a lane that took every subject.
|
|
390
|
+
if (Number.isNaN(rec.probed) && !this.meeting && !lane.every) {
|
|
364
391
|
this.late.push(slot);
|
|
365
392
|
return false;
|
|
366
393
|
}
|
|
@@ -399,7 +426,7 @@ export function foldKeys(voice, chans, slot) {
|
|
|
399
426
|
const a = seg.a;
|
|
400
427
|
const b = seg.b;
|
|
401
428
|
const u = seg.eased;
|
|
402
|
-
if (ch.scalar || !ch.plain) {
|
|
429
|
+
if (ch.scalar || !ch.plain || !ch.straight) {
|
|
403
430
|
this.foldInto(ch, slot, voice.lerps[i](a, b, u));
|
|
404
431
|
continue;
|
|
405
432
|
}
|
|
@@ -440,8 +467,13 @@ export function call(voice, chans, rec, slot) {
|
|
|
440
467
|
this.foldDelta(chans, slot, rec.delta);
|
|
441
468
|
return true;
|
|
442
469
|
}
|
|
443
|
-
if (silent(voice, w))
|
|
470
|
+
if (silent(voice, w)) {
|
|
471
|
+
// As `contribution` leaves a silent read, so the next one is handed the same `dt`.
|
|
472
|
+
rec.delta = null;
|
|
473
|
+
rec.probed = this.now;
|
|
474
|
+
rec.seeks = voice.seeks;
|
|
444
475
|
return false;
|
|
476
|
+
}
|
|
445
477
|
const kept = reading.kept;
|
|
446
478
|
host.ready(voice, subject, rec);
|
|
447
479
|
if (this.keeps)
|
package/dist/fit.d.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* How hard a claim on time holds when it meets another
|
|
2
|
+
* How hard a claim on time holds when it meets another. The weaker gives way.
|
|
3
3
|
*
|
|
4
4
|
* @category score
|
|
5
5
|
*/
|
|
6
6
|
export type Strength = 'weak' | 'strong' | 'required';
|
|
7
7
|
/**
|
|
8
|
-
* How a span lays its children out
|
|
9
|
-
*
|
|
8
|
+
* How a span lays its children out. Each after the one before has ended, partway through it, or all
|
|
9
|
+
* at once.
|
|
10
10
|
*
|
|
11
11
|
* @category score
|
|
12
12
|
*/
|
|
13
13
|
export type Order = 'queue' | 'stagger' | 'together';
|
|
14
14
|
/**
|
|
15
|
-
* One child of a span as a fit sees it
|
|
15
|
+
* One child of a span as a fit sees it. What is left of it, and the ways it may give way.
|
|
16
16
|
*
|
|
17
17
|
* @category score
|
|
18
18
|
*/
|
|
@@ -25,13 +25,16 @@ export interface Claim {
|
|
|
25
25
|
faster: number;
|
|
26
26
|
/** How many times slower it may play; 1 for no slower. */
|
|
27
27
|
slower: number;
|
|
28
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* Whether it may run alongside the one before it. It may where the span's order would not have
|
|
30
|
+
* it.
|
|
31
|
+
*/
|
|
29
32
|
overlap: boolean;
|
|
30
33
|
/** Whether it may jump to its end instead of playing. */
|
|
31
34
|
ballast: boolean;
|
|
32
35
|
priority: Strength;
|
|
33
36
|
/**
|
|
34
|
-
* Span ms from now before which it may not start
|
|
37
|
+
* Span ms from now before which it may not start. From its `earliest`; absent for none. Below 0
|
|
35
38
|
* where it has passed, which places the child there.
|
|
36
39
|
*/
|
|
37
40
|
floor?: number;
|
|
@@ -52,9 +55,9 @@ export interface SpanClaim {
|
|
|
52
55
|
share: number;
|
|
53
56
|
}
|
|
54
57
|
/**
|
|
55
|
-
* What a fit decides
|
|
56
|
-
*
|
|
57
|
-
* the span ms the span may run past its budget.
|
|
58
|
+
* What a fit decides for each child. A rate over its own, and whether it is skipped, per child in
|
|
59
|
+
* the order given. `share` is how far through the one before a child that may overlap starts, and
|
|
60
|
+
* `allow` the span ms the span may run past its budget.
|
|
58
61
|
*
|
|
59
62
|
* @category score
|
|
60
63
|
*/
|
|
@@ -65,7 +68,8 @@ export interface FitPlan {
|
|
|
65
68
|
allow: number;
|
|
66
69
|
}
|
|
67
70
|
/**
|
|
68
|
-
* A strategy for fitting a span's children into its budget
|
|
71
|
+
* A strategy for fitting a span's children into its budget. It goes from the plan so far to a new
|
|
72
|
+
* one.
|
|
69
73
|
*
|
|
70
74
|
* @category score
|
|
71
75
|
*/
|
|
@@ -73,23 +77,31 @@ export type Fit = (span: SpanClaim, kids: readonly Claim[], plan: FitPlan) => Fi
|
|
|
73
77
|
/** How far through the one before a child starts under each order, before any fit. */
|
|
74
78
|
export declare function shareOf(order: Order, stagger: number): number;
|
|
75
79
|
/**
|
|
76
|
-
* The plan before any fit
|
|
80
|
+
* The plan before any fit. Every child at its own rate, played, in the span's order.
|
|
77
81
|
*
|
|
78
82
|
* @category score
|
|
79
83
|
*/
|
|
80
84
|
export declare function plain(span: SpanClaim, kids: readonly Claim[]): FitPlan;
|
|
81
85
|
/**
|
|
82
|
-
* Where
|
|
83
|
-
* is where it is, at 0; each pending one starts its share through the one before it, or at its
|
|
84
|
-
* floor where that is later. A floor already past places a child before now, so it starts partway
|
|
85
|
-
* through; one without a floor starts no sooner than now.
|
|
86
|
+
* Where `layout` puts a span's children.
|
|
86
87
|
*
|
|
87
88
|
* @category score
|
|
88
89
|
*/
|
|
89
|
-
export
|
|
90
|
+
export interface Layout {
|
|
91
|
+
/** Where each child starts, span ms from now, in the order given. */
|
|
90
92
|
at: number[];
|
|
93
|
+
/** How long they take together, span ms from now. */
|
|
91
94
|
length: number;
|
|
92
|
-
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Where a plan puts each child, and how long they take together. Times are span ms from now. A
|
|
98
|
+
* playing child is where it is, at 0; each pending one starts its share through the one before it,
|
|
99
|
+
* or at its floor where that is later. A floor already past places a child before now, so it starts
|
|
100
|
+
* partway through; one without a floor starts no sooner than now.
|
|
101
|
+
*
|
|
102
|
+
* @category score
|
|
103
|
+
*/
|
|
104
|
+
export declare function layout(span: SpanClaim, kids: readonly Claim[], plan: FitPlan): Layout;
|
|
93
105
|
/**
|
|
94
106
|
* Moves every child's rate by one factor toward the budget, before the span's fit: faster where
|
|
95
107
|
* they run past it, each no faster than its `faster` allows, and slower where they leave room in
|
|
@@ -97,8 +109,8 @@ export declare function layout(span: SpanClaim, kids: readonly Claim[], plan: Fi
|
|
|
97
109
|
*/
|
|
98
110
|
export declare function rescale(span: SpanClaim, kids: readonly Claim[], plan: FitPlan): FitPlan;
|
|
99
111
|
/**
|
|
100
|
-
* Starts children that may overlap sooner
|
|
101
|
-
*
|
|
112
|
+
* Starts children that may overlap sooner, until they fit. Each moves closer after the one before,
|
|
113
|
+
* toward all at once.
|
|
102
114
|
*
|
|
103
115
|
* @category score
|
|
104
116
|
*/
|
|
@@ -116,13 +128,20 @@ export declare function shed(): Fit;
|
|
|
116
128
|
*/
|
|
117
129
|
export declare function conclude(): Fit;
|
|
118
130
|
/**
|
|
119
|
-
*
|
|
131
|
+
* What `overrun` and `lax` take.
|
|
120
132
|
*
|
|
121
133
|
* @category score
|
|
122
134
|
*/
|
|
123
|
-
export
|
|
135
|
+
export interface OverrunOptions {
|
|
136
|
+
/** The most the span may run, as a multiple of its budget. Default: no limit. */
|
|
124
137
|
cap?: number;
|
|
125
|
-
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Lets the span run past its budget. Up to `cap` times it, or without limit.
|
|
141
|
+
*
|
|
142
|
+
* @category score
|
|
143
|
+
*/
|
|
144
|
+
export declare function overrun(opts?: OverrunOptions): Fit;
|
|
126
145
|
/**
|
|
127
146
|
* Tries each fit in turn, handing the plan on, and stops once the children fit.
|
|
128
147
|
*
|
|
@@ -130,13 +149,12 @@ export declare function overrun(opts?: {
|
|
|
130
149
|
*/
|
|
131
150
|
export declare function pipe(...fits_: readonly Fit[]): Fit;
|
|
132
151
|
/**
|
|
133
|
-
* Every way of giving way
|
|
152
|
+
* Every way of giving way, overrunning only once the rest are spent. It applies after the span's
|
|
153
|
+
* retiming.
|
|
134
154
|
*
|
|
135
155
|
* @category score
|
|
136
156
|
*/
|
|
137
|
-
export declare function lax(opts?:
|
|
138
|
-
cap?: number;
|
|
139
|
-
}): Fit;
|
|
157
|
+
export declare function lax(opts?: OverrunOptions): Fit;
|
|
140
158
|
/** The fit a span takes without one of its own. */
|
|
141
159
|
export declare const defaultFit: Fit;
|
|
142
160
|
/**
|
package/dist/fit.js
CHANGED
|
@@ -3,7 +3,7 @@ export function shareOf(order, stagger) {
|
|
|
3
3
|
return order === 'queue' ? 1 : order === 'together' ? 0 : stagger;
|
|
4
4
|
}
|
|
5
5
|
/**
|
|
6
|
-
* The plan before any fit
|
|
6
|
+
* The plan before any fit. Every child at its own rate, played, in the span's order.
|
|
7
7
|
*
|
|
8
8
|
* @category score
|
|
9
9
|
*/
|
|
@@ -16,10 +16,10 @@ export function plain(span, kids) {
|
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
|
-
* Where a plan puts each child,
|
|
20
|
-
* is where it is, at 0; each pending one starts its share through the one before it,
|
|
21
|
-
* floor where that is later. A floor already past places a child before now, so it starts
|
|
22
|
-
* through; one without a floor starts no sooner than now.
|
|
19
|
+
* Where a plan puts each child, and how long they take together. Times are span ms from now. A
|
|
20
|
+
* playing child is where it is, at 0; each pending one starts its share through the one before it,
|
|
21
|
+
* or at its floor where that is later. A floor already past places a child before now, so it starts
|
|
22
|
+
* partway through; one without a floor starts no sooner than now.
|
|
23
23
|
*
|
|
24
24
|
* @category score
|
|
25
25
|
*/
|
|
@@ -87,8 +87,8 @@ export function rescale(span, kids, plan) {
|
|
|
87
87
|
return fits(span, kids, at(f)) ? at(f) : at(Math.max(1, f - 1e-9));
|
|
88
88
|
}
|
|
89
89
|
/**
|
|
90
|
-
* Starts children that may overlap sooner
|
|
91
|
-
*
|
|
90
|
+
* Starts children that may overlap sooner, until they fit. Each moves closer after the one before,
|
|
91
|
+
* toward all at once.
|
|
92
92
|
*
|
|
93
93
|
* @category score
|
|
94
94
|
*/
|
|
@@ -130,7 +130,7 @@ export function conclude() {
|
|
|
130
130
|
return (_span, kids, plan) => ({ ...plan, skip: kids.map(() => true) });
|
|
131
131
|
}
|
|
132
132
|
/**
|
|
133
|
-
* Lets the span run past its budget
|
|
133
|
+
* Lets the span run past its budget. Up to `cap` times it, or without limit.
|
|
134
134
|
*
|
|
135
135
|
* @category score
|
|
136
136
|
*/
|
|
@@ -158,7 +158,8 @@ export function pipe(...fits_) {
|
|
|
158
158
|
};
|
|
159
159
|
}
|
|
160
160
|
/**
|
|
161
|
-
* Every way of giving way
|
|
161
|
+
* Every way of giving way, overrunning only once the rest are spent. It applies after the span's
|
|
162
|
+
* retiming.
|
|
162
163
|
*
|
|
163
164
|
* @category score
|
|
164
165
|
*/
|
package/dist/fling.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { Moving, Value } from './motion.js';
|
|
2
|
+
/**
|
|
3
|
+
* A pointer's recent path, for the velocity it is let go at.
|
|
4
|
+
*
|
|
5
|
+
* @category fling
|
|
6
|
+
*/
|
|
7
|
+
export interface Track<V extends Value = number> {
|
|
8
|
+
/** Where it was at `t`, ms; samples must come in time order. */
|
|
9
|
+
add(t: number, x: V): void;
|
|
10
|
+
/**
|
|
11
|
+
* Units per second at `at`, default the latest sample: the least-squares slope of the samples
|
|
12
|
+
* within `ms` of the latest, or zero once the latest is more than `stop` ms before `at`, or with
|
|
13
|
+
* fewer than two samples.
|
|
14
|
+
*/
|
|
15
|
+
velocity(at?: number): V;
|
|
16
|
+
clear(): void;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Tracks a pointer's velocity from its samples, with no listeners of its own: anything that can give
|
|
20
|
+
* a time and a position can feed it.
|
|
21
|
+
*
|
|
22
|
+
* @category fling
|
|
23
|
+
*/
|
|
24
|
+
export declare function track<V extends Value = number>(opts?: {
|
|
25
|
+
/** How far back the slope reads, ms. Default 100. */
|
|
26
|
+
ms?: number;
|
|
27
|
+
/** How long a pointer can sit still before letting go and still fling, ms. Default 40. */
|
|
28
|
+
stop?: number;
|
|
29
|
+
}): Track<V>;
|
|
30
|
+
/** The part of a pointer event `fling` reads. */
|
|
31
|
+
export interface PointerLike {
|
|
32
|
+
pointerId: number;
|
|
33
|
+
clientX: number;
|
|
34
|
+
clientY: number;
|
|
35
|
+
timeStamp: number;
|
|
36
|
+
}
|
|
37
|
+
/** Anything pointer events come from: an element, or a stand-in for one. */
|
|
38
|
+
export interface PointerTarget {
|
|
39
|
+
addEventListener(type: string, fn: (e: PointerLike) => void): void;
|
|
40
|
+
removeEventListener(type: string, fn: (e: PointerLike) => void): void;
|
|
41
|
+
setPointerCapture?(pointerId: number): void;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Drags `subject` of a motion patch, usually a `glide`, by pointer on `target`, and flings it on
|
|
45
|
+
* release at the pointer's velocity: a press stops it where it is, each move `set`s it under the
|
|
46
|
+
* pointer, and letting go `push`es it. Past the edge of a glide's `bounds` it follows less and less
|
|
47
|
+
* of the drag, never more than `give` past. One pointer at a time; a canceled one lets go at rest.
|
|
48
|
+
* Returns the function that removes its listeners.
|
|
49
|
+
*
|
|
50
|
+
* @category fling
|
|
51
|
+
*/
|
|
52
|
+
export declare function fling<I, O, V extends Value = number>(target: PointerTarget, motion: Pick<Moving<I, O, V>, 'read' | 'set' | 'push'> & {
|
|
53
|
+
bounds?(subject: I): readonly [min: V, max: V] | undefined;
|
|
54
|
+
}, subject: I, opts?: {
|
|
55
|
+
/** Which pointer axes it follows: `'xy'` moves a subject on two axes. Default `'y'`. */
|
|
56
|
+
axis?: 'x' | 'y' | 'xy';
|
|
57
|
+
/** Units per pixel of pointer travel; -1 scrolls content against the drag. Default 1. */
|
|
58
|
+
scale?: number;
|
|
59
|
+
/**
|
|
60
|
+
* How far past an edge of the glide's bounds a drag can carry the subject, in its units; 0
|
|
61
|
+
* holds it at the edge. Default 120.
|
|
62
|
+
*/
|
|
63
|
+
give?: number;
|
|
64
|
+
ms?: number;
|
|
65
|
+
stop?: number;
|
|
66
|
+
}): () => void;
|
package/dist/fling.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tracks a pointer's velocity from its samples, with no listeners of its own: anything that can give
|
|
3
|
+
* a time and a position can feed it.
|
|
4
|
+
*
|
|
5
|
+
* @category fling
|
|
6
|
+
*/
|
|
7
|
+
export function track(opts = {}) {
|
|
8
|
+
const span = opts.ms ?? 100;
|
|
9
|
+
const stop = opts.stop ?? 40;
|
|
10
|
+
if (!(span > 0) || !(stop >= 0))
|
|
11
|
+
throw new RangeError('blits: a track takes a positive ms and a stop of 0 or more');
|
|
12
|
+
const ts = [];
|
|
13
|
+
const xs = [];
|
|
14
|
+
let scalar = true;
|
|
15
|
+
return {
|
|
16
|
+
add(t, x) {
|
|
17
|
+
scalar = typeof x === 'number';
|
|
18
|
+
ts.push(t);
|
|
19
|
+
xs.push(typeof x === 'number' ? [x] : [...x]);
|
|
20
|
+
let drop = 0;
|
|
21
|
+
while (t - ts[drop] > span)
|
|
22
|
+
drop++;
|
|
23
|
+
if (drop > 0) {
|
|
24
|
+
ts.splice(0, drop);
|
|
25
|
+
xs.splice(0, drop);
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
velocity(at) {
|
|
29
|
+
const n = ts.length;
|
|
30
|
+
const axes = n === 0 ? 1 : xs[0].length;
|
|
31
|
+
const out = new Array(axes).fill(0);
|
|
32
|
+
const last = ts[n - 1];
|
|
33
|
+
if (n >= 2 && !((at ?? last) - last > stop)) {
|
|
34
|
+
let mt = 0;
|
|
35
|
+
for (const t of ts)
|
|
36
|
+
mt += t;
|
|
37
|
+
mt /= n;
|
|
38
|
+
let tt = 0;
|
|
39
|
+
for (const t of ts)
|
|
40
|
+
tt += (t - mt) ** 2;
|
|
41
|
+
if (tt > 0)
|
|
42
|
+
for (let k = 0; k < axes; k++) {
|
|
43
|
+
let mx = 0;
|
|
44
|
+
for (const x of xs)
|
|
45
|
+
mx += x[k];
|
|
46
|
+
mx /= n;
|
|
47
|
+
let tx = 0;
|
|
48
|
+
for (let i = 0; i < n; i++)
|
|
49
|
+
tx += (ts[i] - mt) * (xs[i][k] - mx);
|
|
50
|
+
out[k] = (tx / tt) * 1000;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return (scalar ? out[0] : out);
|
|
54
|
+
},
|
|
55
|
+
clear() {
|
|
56
|
+
ts.length = 0;
|
|
57
|
+
xs.length = 0;
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/** How much of a drag past an edge the subject follows, right at the edge. */
|
|
62
|
+
const PULL = 0.55;
|
|
63
|
+
/** How far a drag `d` past an edge carries the subject, never as far as `give`. */
|
|
64
|
+
const resist = (d, give) => give > 0 ? give * (1 - 1 / ((PULL * d) / give + 1)) : 0;
|
|
65
|
+
/** The drag past an edge that carries the subject `r` past it, as `resist` maps it. */
|
|
66
|
+
const unresist = (r, give) => {
|
|
67
|
+
const held = Math.min(r, give * 0.99);
|
|
68
|
+
return give > 0 ? (give * held) / (PULL * (give - held)) : 0;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Drags `subject` of a motion patch, usually a `glide`, by pointer on `target`, and flings it on
|
|
72
|
+
* release at the pointer's velocity: a press stops it where it is, each move `set`s it under the
|
|
73
|
+
* pointer, and letting go `push`es it. Past the edge of a glide's `bounds` it follows less and less
|
|
74
|
+
* of the drag, never more than `give` past. One pointer at a time; a canceled one lets go at rest.
|
|
75
|
+
* Returns the function that removes its listeners.
|
|
76
|
+
*
|
|
77
|
+
* @category fling
|
|
78
|
+
*/
|
|
79
|
+
export function fling(target, motion, subject, opts = {}) {
|
|
80
|
+
const axis = opts.axis ?? 'y';
|
|
81
|
+
const scale = opts.scale ?? 1;
|
|
82
|
+
const give = opts.give ?? 120;
|
|
83
|
+
if (!(give >= 0))
|
|
84
|
+
throw new RangeError('blits: a fling takes a give of 0 or more');
|
|
85
|
+
let lo;
|
|
86
|
+
let hi;
|
|
87
|
+
/** Where the drag `x` puts the subject, past an edge carried by `resist`. */
|
|
88
|
+
const held = (x) => lo === undefined || hi === undefined
|
|
89
|
+
? x
|
|
90
|
+
: x.map((xi, k) => {
|
|
91
|
+
const a = lo[k];
|
|
92
|
+
const b = hi[k];
|
|
93
|
+
return xi < a ? a - resist(a - xi, give) : xi > b ? b + resist(xi - b, give) : xi;
|
|
94
|
+
});
|
|
95
|
+
/** The drag that puts the subject at `x`, as `held` maps it. */
|
|
96
|
+
const dragged = (x) => lo === undefined || hi === undefined
|
|
97
|
+
? x
|
|
98
|
+
: x.map((xi, k) => {
|
|
99
|
+
const a = lo[k];
|
|
100
|
+
const b = hi[k];
|
|
101
|
+
return xi < a ? a - unresist(a - xi, give) : xi > b ? b + unresist(xi - b, give) : xi;
|
|
102
|
+
});
|
|
103
|
+
const path = track(opts);
|
|
104
|
+
let id;
|
|
105
|
+
let from = [];
|
|
106
|
+
let p0 = [];
|
|
107
|
+
const at = (e) => axis === 'x' ? [e.clientX] : axis === 'y' ? [e.clientY] : [e.clientX, e.clientY];
|
|
108
|
+
const value = (x) => (axis === 'xy' ? x : x[0]);
|
|
109
|
+
const flat = (v) => (typeof v === 'number' ? [v] : [...v]);
|
|
110
|
+
const zero = () => value(from.map(() => 0));
|
|
111
|
+
const down = (e) => {
|
|
112
|
+
if (id !== undefined)
|
|
113
|
+
return;
|
|
114
|
+
const now = motion.read(subject);
|
|
115
|
+
if (now === undefined)
|
|
116
|
+
return;
|
|
117
|
+
id = e.pointerId;
|
|
118
|
+
target.setPointerCapture?.(id);
|
|
119
|
+
const edges = motion.bounds?.(subject);
|
|
120
|
+
lo = edges === undefined ? undefined : flat(edges[0]);
|
|
121
|
+
hi = edges === undefined ? undefined : flat(edges[1]);
|
|
122
|
+
const here = flat(now.value);
|
|
123
|
+
from = dragged(here);
|
|
124
|
+
p0 = at(e);
|
|
125
|
+
motion.push(subject, zero());
|
|
126
|
+
// The push alone would spring a subject caught past an edge back; a set there holds it.
|
|
127
|
+
motion.set(subject, value(here));
|
|
128
|
+
path.clear();
|
|
129
|
+
path.add(e.timeStamp, here);
|
|
130
|
+
};
|
|
131
|
+
const move = (e) => {
|
|
132
|
+
if (e.pointerId !== id)
|
|
133
|
+
return;
|
|
134
|
+
const x = held(at(e).map((p, k) => from[k] + (p - p0[k]) * scale));
|
|
135
|
+
motion.set(subject, value(x));
|
|
136
|
+
path.add(e.timeStamp, x);
|
|
137
|
+
};
|
|
138
|
+
const up = (e) => {
|
|
139
|
+
if (e.pointerId !== id)
|
|
140
|
+
return;
|
|
141
|
+
id = undefined;
|
|
142
|
+
motion.push(subject, value(path.velocity(e.timeStamp)));
|
|
143
|
+
};
|
|
144
|
+
const cancel = (e) => {
|
|
145
|
+
if (e.pointerId !== id)
|
|
146
|
+
return;
|
|
147
|
+
id = undefined;
|
|
148
|
+
motion.push(subject, zero());
|
|
149
|
+
};
|
|
150
|
+
const on = [
|
|
151
|
+
['pointerdown', down],
|
|
152
|
+
['pointermove', move],
|
|
153
|
+
['pointerup', up],
|
|
154
|
+
['pointercancel', cancel],
|
|
155
|
+
];
|
|
156
|
+
for (const [type, fn] of on)
|
|
157
|
+
target.addEventListener(type, fn);
|
|
158
|
+
return () => {
|
|
159
|
+
for (const [type, fn] of on)
|
|
160
|
+
target.removeEventListener(type, fn);
|
|
161
|
+
};
|
|
162
|
+
}
|