paperlab 0.7.0 → 0.7.1
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/{chunk-MZCAN3AR.js → chunk-NTDVOOBA.js} +207 -12
- package/dist/chunk-NTDVOOBA.js.map +1 -0
- package/dist/index.cjs +206 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/stage.cjs +206 -11
- package/dist/stage.cjs.map +1 -1
- package/dist/stage.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-MZCAN3AR.js.map +0 -1
|
@@ -4936,6 +4936,8 @@ var ClothSim = class {
|
|
|
4936
4936
|
height;
|
|
4937
4937
|
positions;
|
|
4938
4938
|
prev;
|
|
4939
|
+
/** Where the constructor laid every particle out — what {@link restore} puts back. */
|
|
4940
|
+
laidOut;
|
|
4939
4941
|
pinned;
|
|
4940
4942
|
pinTargets;
|
|
4941
4943
|
/**
|
|
@@ -5069,6 +5071,7 @@ var ClothSim = class {
|
|
|
5069
5071
|
}
|
|
5070
5072
|
}
|
|
5071
5073
|
this.prev.set(this.positions);
|
|
5074
|
+
this.laidOut = this.positions.slice();
|
|
5072
5075
|
const idx = (r, c) => r * cols + c;
|
|
5073
5076
|
const links = [];
|
|
5074
5077
|
const link = (a, b, kind) => {
|
|
@@ -5186,6 +5189,22 @@ var ClothSim = class {
|
|
|
5186
5189
|
this.asleep = false;
|
|
5187
5190
|
this.stillFrames = 0;
|
|
5188
5191
|
}
|
|
5192
|
+
/**
|
|
5193
|
+
* Back to the sheet as it was laid out: flat, still, every particle where
|
|
5194
|
+
* the constructor put it. Pins, a grab and every lever are left alone.
|
|
5195
|
+
*
|
|
5196
|
+
* For a history that has been rewound — a burn played back from an earlier
|
|
5197
|
+
* moment, say, which hands back paper that had burnt away and paper that
|
|
5198
|
+
* had FALLEN, lying on the floor with its springs to the sheet suddenly
|
|
5199
|
+
* whole again. Solved from there, the solve hauls it up through the sheet
|
|
5200
|
+
* in a tangle. Laid out afresh, it is simply the sheet it is again.
|
|
5201
|
+
*/
|
|
5202
|
+
restore() {
|
|
5203
|
+
this.positions.set(this.laidOut);
|
|
5204
|
+
this.prev.set(this.laidOut);
|
|
5205
|
+
this.accumulator = 0;
|
|
5206
|
+
this.wake();
|
|
5207
|
+
}
|
|
5189
5208
|
/**
|
|
5190
5209
|
* Take hold of the sheet at one particle.
|
|
5191
5210
|
*
|
|
@@ -5525,6 +5544,7 @@ var CHAR_SHRINK = 0.12;
|
|
|
5525
5544
|
var CHAR_CURL = 6;
|
|
5526
5545
|
var FRONT_GRADIENT = 16 * 16;
|
|
5527
5546
|
var WET_MASS = 2;
|
|
5547
|
+
var TEAR_STRETCH = 1.5;
|
|
5528
5548
|
var DamageCoupling = class {
|
|
5529
5549
|
sim;
|
|
5530
5550
|
source = null;
|
|
@@ -5534,8 +5554,24 @@ var DamageCoupling = class {
|
|
|
5534
5554
|
particleTexel = new Int32Array(0);
|
|
5535
5555
|
/** Gone, per particle, as of the last read. */
|
|
5536
5556
|
gone;
|
|
5557
|
+
/** Whether any particle has ever been gone since the last reset — nothing can tear before. */
|
|
5558
|
+
holed = false;
|
|
5537
5559
|
/** The gone particles with live paper beside them. See {@link follow}. */
|
|
5538
5560
|
followers = [];
|
|
5561
|
+
/**
|
|
5562
|
+
* Which piece of paper each particle belongs to, named by the lowest index
|
|
5563
|
+
* in it — or -1 in the middle of a hole.
|
|
5564
|
+
*
|
|
5565
|
+
* A piece is whatever the sim's unbroken springs still hold together, which
|
|
5566
|
+
* is exactly what moves as one: two pieces are two things the solve lets
|
|
5567
|
+
* part. A gone particle on a cut takes the piece it borders MOST — a cut a
|
|
5568
|
+
* single particle wide borders two, and a particle averaged between them
|
|
5569
|
+
* would be dragged across the gap as one piece fell, stretching both edges
|
|
5570
|
+
* into it.
|
|
5571
|
+
*/
|
|
5572
|
+
piece;
|
|
5573
|
+
/** Union-find over the particles, reused on every read. */
|
|
5574
|
+
parent;
|
|
5539
5575
|
/**
|
|
5540
5576
|
* Per bend spring, how squarely it lies ACROSS the burn front, 0..1 — and
|
|
5541
5577
|
* remembered once it has been.
|
|
@@ -5553,9 +5589,20 @@ var DamageCoupling = class {
|
|
|
5553
5589
|
* but the paper stays rolled the way it rolled.
|
|
5554
5590
|
*/
|
|
5555
5591
|
curlWeight;
|
|
5592
|
+
/** The mesh's index as it was built — what {@link tear} hides triangles from and mends them back to. */
|
|
5593
|
+
original = null;
|
|
5594
|
+
/** Per triangle of that index: torn, and hidden until the history is rewound. */
|
|
5595
|
+
torn = new Uint8Array(0);
|
|
5596
|
+
/** The triangles with a gone corner — the only ones that can tear. */
|
|
5597
|
+
rim = [];
|
|
5598
|
+
rimStale = true;
|
|
5599
|
+
/** Every torn triangle has to be put back: the history was rewound, or the source went. */
|
|
5600
|
+
mend = false;
|
|
5556
5601
|
constructor(sim) {
|
|
5557
5602
|
this.sim = sim;
|
|
5558
5603
|
this.gone = new Uint8Array(sim.count);
|
|
5604
|
+
this.piece = new Int32Array(sim.count);
|
|
5605
|
+
this.parent = new Int32Array(sim.count);
|
|
5559
5606
|
this.curlWeight = new Float32Array(sim.constraintCount);
|
|
5560
5607
|
}
|
|
5561
5608
|
/**
|
|
@@ -5567,7 +5614,13 @@ var DamageCoupling = class {
|
|
|
5567
5614
|
update(source) {
|
|
5568
5615
|
const next = source ?? null;
|
|
5569
5616
|
if (next === this.source && (next === null || next.version === this.version)) return false;
|
|
5570
|
-
if (next !== this.source)
|
|
5617
|
+
if (next !== this.source) {
|
|
5618
|
+
this.curlWeight.fill(0);
|
|
5619
|
+
if (next && this.holed) {
|
|
5620
|
+
this.rewind();
|
|
5621
|
+
this.gone.fill(0);
|
|
5622
|
+
}
|
|
5623
|
+
}
|
|
5571
5624
|
this.source = next;
|
|
5572
5625
|
if (!next) {
|
|
5573
5626
|
this.reset();
|
|
@@ -5587,12 +5640,13 @@ var DamageCoupling = class {
|
|
|
5587
5640
|
* laid out flat around them — their positions plus its rest offset from
|
|
5588
5641
|
* each, averaged. The triangles that straddle the burnt edge then keep
|
|
5589
5642
|
* roughly their shape instead of stretching back to wherever the particle
|
|
5590
|
-
* was when it burnt. Only
|
|
5591
|
-
*
|
|
5643
|
+
* was when it burnt. Only the neighbours in its own {@link piece} count, so
|
|
5644
|
+
* on a cut between two pieces it leaves with one of them.
|
|
5592
5645
|
*/
|
|
5593
5646
|
follow() {
|
|
5594
5647
|
if (this.followers.length === 0) return;
|
|
5595
5648
|
const { gone, sim } = this;
|
|
5649
|
+
const piece = this.piece;
|
|
5596
5650
|
const p = sim.positions;
|
|
5597
5651
|
const { cols, rows } = sim;
|
|
5598
5652
|
const cellX = cols > 1 ? sim.width / (cols - 1) : 0;
|
|
@@ -5600,6 +5654,7 @@ var DamageCoupling = class {
|
|
|
5600
5654
|
for (const g of this.followers) {
|
|
5601
5655
|
const r = g / cols | 0;
|
|
5602
5656
|
const c = g % cols;
|
|
5657
|
+
const own = piece[g];
|
|
5603
5658
|
let sx = 0;
|
|
5604
5659
|
let sy = 0;
|
|
5605
5660
|
let sz = 0;
|
|
@@ -5611,7 +5666,7 @@ var DamageCoupling = class {
|
|
|
5611
5666
|
const nc = c + dc;
|
|
5612
5667
|
if (dr === 0 && dc === 0 || nc < 0 || nc >= cols) continue;
|
|
5613
5668
|
const j = nr * cols + nc;
|
|
5614
|
-
if (gone[j]) continue;
|
|
5669
|
+
if (gone[j] || piece[j] !== own) continue;
|
|
5615
5670
|
const j3 = j * 3;
|
|
5616
5671
|
sx += p[j3] - dc * cellX;
|
|
5617
5672
|
sy += p[j3 + 1] + dr * cellY;
|
|
@@ -5626,6 +5681,70 @@ var DamageCoupling = class {
|
|
|
5626
5681
|
p[g3 + 2] = sz / n;
|
|
5627
5682
|
}
|
|
5628
5683
|
}
|
|
5684
|
+
/**
|
|
5685
|
+
* Hide the triangles a piece has torn away from. Call after {@link follow},
|
|
5686
|
+
* with the sheet's index buffer: it is rewritten in place, and the return
|
|
5687
|
+
* says whether it changed, so the caller knows to upload it.
|
|
5688
|
+
*
|
|
5689
|
+
* A triangle tears when one of its edges has stretched past
|
|
5690
|
+
* {@link TEAR_STRETCH} — which, among triangles with a burnt-away corner,
|
|
5691
|
+
* only ever happens across a gap that is opening. It is then collapsed to a
|
|
5692
|
+
* point, which draws nothing, casts no shadow and adds nothing to its
|
|
5693
|
+
* corners' normals, and it stays hidden: torn paper does not mend because
|
|
5694
|
+
* the two sides swing close again. Only a rewound history puts it back.
|
|
5695
|
+
*
|
|
5696
|
+
* A sheet in one piece, held, never stretches one that far — so a burn that
|
|
5697
|
+
* cuts nothing loose leaves this index exactly as it was built.
|
|
5698
|
+
*/
|
|
5699
|
+
tear(index) {
|
|
5700
|
+
if (!this.holed && !this.mend) return false;
|
|
5701
|
+
if (!this.original || this.original.length !== index.length) {
|
|
5702
|
+
this.original = index.slice();
|
|
5703
|
+
this.torn = new Uint8Array(index.length / 3);
|
|
5704
|
+
this.rimStale = true;
|
|
5705
|
+
}
|
|
5706
|
+
const { original, torn, gone, sim } = this;
|
|
5707
|
+
let changed = false;
|
|
5708
|
+
if (this.mend) {
|
|
5709
|
+
this.mend = false;
|
|
5710
|
+
index.set(original);
|
|
5711
|
+
changed = true;
|
|
5712
|
+
}
|
|
5713
|
+
if (this.rimStale) {
|
|
5714
|
+
this.rimStale = false;
|
|
5715
|
+
const rim = [];
|
|
5716
|
+
for (let t = 0; t < torn.length; t++) {
|
|
5717
|
+
if (gone[original[t * 3]] || gone[original[t * 3 + 1]] || gone[original[t * 3 + 2]]) rim.push(t);
|
|
5718
|
+
}
|
|
5719
|
+
this.rim = rim;
|
|
5720
|
+
}
|
|
5721
|
+
const p = sim.positions;
|
|
5722
|
+
const { cols, rows } = sim;
|
|
5723
|
+
const cellX = cols > 1 ? sim.width / (cols - 1) : 0;
|
|
5724
|
+
const cellY = rows > 1 ? sim.height / (rows - 1) : 0;
|
|
5725
|
+
const limit = TEAR_STRETCH * TEAR_STRETCH;
|
|
5726
|
+
const stretched = (i, j) => {
|
|
5727
|
+
const dc = i % cols - j % cols;
|
|
5728
|
+
const dr = (i / cols | 0) - (j / cols | 0);
|
|
5729
|
+
const rest = (dc * cellX) ** 2 + (dr * cellY) ** 2;
|
|
5730
|
+
const i3 = i * 3;
|
|
5731
|
+
const j3 = j * 3;
|
|
5732
|
+
const now = (p[i3] - p[j3]) ** 2 + (p[i3 + 1] - p[j3 + 1]) ** 2 + (p[i3 + 2] - p[j3 + 2]) ** 2;
|
|
5733
|
+
return now > rest * limit;
|
|
5734
|
+
};
|
|
5735
|
+
for (const t of this.rim) {
|
|
5736
|
+
if (torn[t]) continue;
|
|
5737
|
+
const a = original[t * 3];
|
|
5738
|
+
const b = original[t * 3 + 1];
|
|
5739
|
+
const c = original[t * 3 + 2];
|
|
5740
|
+
if (!stretched(a, b) && !stretched(b, c) && !stretched(c, a)) continue;
|
|
5741
|
+
torn[t] = 1;
|
|
5742
|
+
index[t * 3 + 1] = a;
|
|
5743
|
+
index[t * 3 + 2] = a;
|
|
5744
|
+
changed = true;
|
|
5745
|
+
}
|
|
5746
|
+
return changed;
|
|
5747
|
+
}
|
|
5629
5748
|
/** Map each particle to its texel. The damage grid's row 0 is v = 0, the sheet's BOTTOM. */
|
|
5630
5749
|
layout(size) {
|
|
5631
5750
|
const { cols, rows, count } = this.sim;
|
|
@@ -5653,12 +5772,19 @@ var DamageCoupling = class {
|
|
|
5653
5772
|
constraintKind,
|
|
5654
5773
|
constraintMiddle
|
|
5655
5774
|
} = sim;
|
|
5775
|
+
let rewound = false;
|
|
5776
|
+
let holed = false;
|
|
5656
5777
|
for (let i = 0; i < sim.count; i++) {
|
|
5657
5778
|
const t = particleTexel[i] * 4;
|
|
5658
5779
|
const isGone = pixels[t + PRESENCE] < GONE_BELOW;
|
|
5780
|
+
if (gone[i] && !isGone) rewound = true;
|
|
5781
|
+
if (isGone) holed = true;
|
|
5659
5782
|
gone[i] = isGone ? 1 : 0;
|
|
5660
5783
|
invMass[i] = isGone ? 0 : 1 / (1 + WET_MASS * pixels[t + SATURATION] / 255);
|
|
5661
5784
|
}
|
|
5785
|
+
if (rewound) this.rewind();
|
|
5786
|
+
this.holed = holed;
|
|
5787
|
+
this.rimStale = true;
|
|
5662
5788
|
for (let k = 0; k < sim.constraintCount; k++) {
|
|
5663
5789
|
const a = constraintA[k];
|
|
5664
5790
|
const b = constraintB[k];
|
|
@@ -5691,31 +5817,95 @@ var DamageCoupling = class {
|
|
|
5691
5817
|
restBend[k] = CHAR_CURL * char * curlWeight[k] * span * span / 8;
|
|
5692
5818
|
}
|
|
5693
5819
|
}
|
|
5820
|
+
this.findPieces();
|
|
5821
|
+
}
|
|
5822
|
+
/**
|
|
5823
|
+
* Which piece each particle is in ({@link piece}), and which gone ones
|
|
5824
|
+
* follow a piece at all ({@link followers}).
|
|
5825
|
+
*
|
|
5826
|
+
* Pieces by union-find over the springs that still hold, rooted at the
|
|
5827
|
+
* lowest index so the same burn names the same pieces every time. Then each
|
|
5828
|
+
* gone particle with live paper beside it joins the piece it has the most
|
|
5829
|
+
* neighbours in, the lower-named on a tie.
|
|
5830
|
+
*/
|
|
5831
|
+
findPieces() {
|
|
5832
|
+
const { sim, gone } = this;
|
|
5833
|
+
const parent = this.parent;
|
|
5834
|
+
const piece = this.piece;
|
|
5835
|
+
const { broken, constraintA, constraintB } = sim;
|
|
5836
|
+
for (let i = 0; i < sim.count; i++) parent[i] = i;
|
|
5837
|
+
const find = (i) => {
|
|
5838
|
+
let r = i;
|
|
5839
|
+
while (parent[r] !== r) r = parent[r];
|
|
5840
|
+
while (parent[i] !== r) {
|
|
5841
|
+
const up = parent[i];
|
|
5842
|
+
parent[i] = r;
|
|
5843
|
+
i = up;
|
|
5844
|
+
}
|
|
5845
|
+
return r;
|
|
5846
|
+
};
|
|
5847
|
+
for (let k = 0; k < sim.constraintCount; k++) {
|
|
5848
|
+
if (broken[k]) continue;
|
|
5849
|
+
const ra = find(constraintA[k]);
|
|
5850
|
+
const rb = find(constraintB[k]);
|
|
5851
|
+
if (ra < rb) parent[rb] = ra;
|
|
5852
|
+
else if (rb < ra) parent[ra] = rb;
|
|
5853
|
+
}
|
|
5854
|
+
for (let i = 0; i < sim.count; i++) piece[i] = gone[i] ? -1 : find(i);
|
|
5694
5855
|
const followers = [];
|
|
5695
5856
|
const { cols, rows } = sim;
|
|
5857
|
+
const seen = [];
|
|
5858
|
+
const votes = [];
|
|
5696
5859
|
for (let i = 0; i < sim.count; i++) {
|
|
5697
5860
|
if (!gone[i]) continue;
|
|
5698
5861
|
const r = i / cols | 0;
|
|
5699
5862
|
const c = i % cols;
|
|
5700
|
-
|
|
5701
|
-
|
|
5863
|
+
seen.length = 0;
|
|
5864
|
+
votes.length = 0;
|
|
5865
|
+
for (let dr = -1; dr <= 1; dr++) {
|
|
5702
5866
|
const nr = r + dr;
|
|
5703
5867
|
if (nr < 0 || nr >= rows) continue;
|
|
5704
5868
|
for (let dc = -1; dc <= 1; dc++) {
|
|
5705
5869
|
const nc = c + dc;
|
|
5706
5870
|
if (nc < 0 || nc >= cols) continue;
|
|
5707
|
-
|
|
5708
|
-
|
|
5709
|
-
|
|
5871
|
+
const j = nr * cols + nc;
|
|
5872
|
+
if (gone[j]) continue;
|
|
5873
|
+
const at = seen.indexOf(piece[j]);
|
|
5874
|
+
if (at < 0) {
|
|
5875
|
+
seen.push(piece[j]);
|
|
5876
|
+
votes.push(1);
|
|
5877
|
+
} else {
|
|
5878
|
+
votes[at]++;
|
|
5710
5879
|
}
|
|
5711
5880
|
}
|
|
5712
5881
|
}
|
|
5713
|
-
if (
|
|
5882
|
+
if (seen.length === 0) continue;
|
|
5883
|
+
let best = 0;
|
|
5884
|
+
for (let k = 1; k < seen.length; k++) {
|
|
5885
|
+
if (votes[k] > votes[best] || votes[k] === votes[best] && seen[k] < seen[best]) best = k;
|
|
5886
|
+
}
|
|
5887
|
+
piece[i] = seen[best];
|
|
5888
|
+
followers.push(i);
|
|
5714
5889
|
}
|
|
5715
5890
|
this.followers = followers;
|
|
5716
5891
|
}
|
|
5892
|
+
/**
|
|
5893
|
+
* A rewound history: the sheet starts over.
|
|
5894
|
+
*
|
|
5895
|
+
* Paper that had fallen away is paper again, with every spring back to the
|
|
5896
|
+
* sheet it was cut from — solved from where it lies, the solve would haul it
|
|
5897
|
+
* up off the floor through the sheet in a tangle. And the curl it rolled
|
|
5898
|
+
* into belonged to a front that has not passed yet.
|
|
5899
|
+
*/
|
|
5900
|
+
rewind() {
|
|
5901
|
+
this.sim.restore();
|
|
5902
|
+
this.curlWeight.fill(0);
|
|
5903
|
+
this.torn.fill(0);
|
|
5904
|
+
this.mend = true;
|
|
5905
|
+
}
|
|
5717
5906
|
reset() {
|
|
5718
5907
|
const { sim } = this;
|
|
5908
|
+
if (this.holed) this.rewind();
|
|
5719
5909
|
sim.invMass.fill(1);
|
|
5720
5910
|
sim.restBend.fill(0);
|
|
5721
5911
|
sim.broken.fill(0);
|
|
@@ -5723,6 +5913,7 @@ var DamageCoupling = class {
|
|
|
5723
5913
|
this.gone.fill(0);
|
|
5724
5914
|
this.curlWeight.fill(0);
|
|
5725
5915
|
this.followers = [];
|
|
5916
|
+
this.holed = false;
|
|
5726
5917
|
this.version = -1;
|
|
5727
5918
|
sim.wake();
|
|
5728
5919
|
}
|
|
@@ -7392,7 +7583,11 @@ var PaperMesh = forwardRef(function PaperMesh2(props, ref) {
|
|
|
7392
7583
|
coupling?.update(props.damage);
|
|
7393
7584
|
sim.step(delta);
|
|
7394
7585
|
const moved = !sim.asleep;
|
|
7395
|
-
if (moved
|
|
7586
|
+
if (moved && coupling) {
|
|
7587
|
+
coupling.follow();
|
|
7588
|
+
const index = geometry.index;
|
|
7589
|
+
if (index && coupling.tear(index.array)) index.needsUpdate = true;
|
|
7590
|
+
}
|
|
7396
7591
|
if (!applyShape(sim.positions, moved) && moved) {
|
|
7397
7592
|
const position = geometry.attributes.position;
|
|
7398
7593
|
position.array.set(sim.positions);
|
|
@@ -10220,4 +10415,4 @@ export {
|
|
|
10220
10415
|
describeConfig,
|
|
10221
10416
|
buildAgentPayload
|
|
10222
10417
|
};
|
|
10223
|
-
//# sourceMappingURL=chunk-
|
|
10418
|
+
//# sourceMappingURL=chunk-NTDVOOBA.js.map
|