@weasel-js/geom 1.13.0 → 1.14.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/README.md +6 -2
- package/dist/3d/index.d.ts +8 -1
- package/dist/3d/index.js +25 -1
- package/dist/3d/index.js.map +1 -1
- package/dist/booleans/index.d.ts +6 -5
- package/dist/booleans/index.js +583 -73
- package/dist/booleans/index.js.map +1 -1
- package/dist/{chunk-CE7IY7TB.js → chunk-2N6OWA7E.js} +3 -3
- package/dist/{chunk-CE7IY7TB.js.map → chunk-2N6OWA7E.js.map} +1 -1
- package/dist/{chunk-Q66NA2FU.js → chunk-3HQJGI63.js} +8 -5
- package/dist/chunk-3HQJGI63.js.map +1 -0
- package/dist/{chunk-YPWFGFQX.js → chunk-3YH5XU3H.js} +125 -8
- package/dist/chunk-3YH5XU3H.js.map +1 -0
- package/dist/{chunk-UI3FUWXM.js → chunk-42TZDN5C.js} +4 -4
- package/dist/{chunk-UI3FUWXM.js.map → chunk-42TZDN5C.js.map} +1 -1
- package/dist/{chunk-B53BERKG.js → chunk-B3D6KWW3.js} +110 -8
- package/dist/chunk-B3D6KWW3.js.map +1 -0
- package/dist/{chunk-2PKPKU4P.js → chunk-EURWZUNY.js} +25 -22
- package/dist/chunk-EURWZUNY.js.map +1 -0
- package/dist/{chunk-VHXOCDZ2.js → chunk-FROQPADA.js} +6 -6
- package/dist/{chunk-VHXOCDZ2.js.map → chunk-FROQPADA.js.map} +1 -1
- package/dist/{chunk-UGE57OTX.js → chunk-GWSJQSQE.js} +11 -8
- package/dist/chunk-GWSJQSQE.js.map +1 -0
- package/dist/chunk-XXNK4N7Z.js +683 -0
- package/dist/chunk-XXNK4N7Z.js.map +1 -0
- package/dist/chunk-ZOMUTU7J.js +249 -0
- package/dist/chunk-ZOMUTU7J.js.map +1 -0
- package/dist/{curvature-PkoLduGc.d.ts → curvature-CDOI32hv.d.ts} +46 -3
- package/dist/curves/index.d.ts +2 -2
- package/dist/curves/index.js +4 -4
- package/dist/index.d.ts +172 -45
- package/dist/index.js +244 -178
- package/dist/index.js.map +1 -1
- package/dist/nd/index.d.ts +28 -1
- package/dist/nd/index.js +27 -675
- package/dist/nd/index.js.map +1 -1
- package/dist/{path-BW8d_2RY.d.ts → path-t80OoJnM.d.ts} +11 -0
- package/dist/tessellate/index.d.ts +11 -4
- package/dist/tessellate/index.js +4 -4
- package/dist/warp/index.d.ts +1 -1
- package/dist/warp/index.js +238 -64
- package/dist/warp/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-2PKPKU4P.js.map +0 -1
- package/dist/chunk-B53BERKG.js.map +0 -1
- package/dist/chunk-Q66NA2FU.js.map +0 -1
- package/dist/chunk-STGSBIOL.js +0 -152
- package/dist/chunk-STGSBIOL.js.map +0 -1
- package/dist/chunk-UGE57OTX.js.map +0 -1
- package/dist/chunk-YPWFGFQX.js.map +0 -1
package/dist/nd/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { createBvh } from '../chunk-XXNK4N7Z.js';
|
|
1
2
|
export { PORT_REACH, cubicAt, portControls, portCurvePoints } from '../chunk-YQADYJJ7.js';
|
|
2
3
|
|
|
3
4
|
// src/snap/flat.ts
|
|
@@ -295,686 +296,37 @@ function stepLine(about, toward, basis, step) {
|
|
|
295
296
|
return { at: about, dirs: [u.map((x, i) => x * c + v[i] * s)] };
|
|
296
297
|
}
|
|
297
298
|
|
|
298
|
-
// src/bvh/
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
root = -1;
|
|
303
|
-
nodeBox;
|
|
304
|
-
left;
|
|
305
|
-
right;
|
|
306
|
-
parent;
|
|
307
|
-
leafCount;
|
|
308
|
-
leafItems;
|
|
309
|
-
count;
|
|
310
|
-
nodeCap = 0;
|
|
311
|
-
nodeTop = 0;
|
|
312
|
-
freeNodes = [];
|
|
313
|
-
itemBox;
|
|
314
|
-
/** Twice each item's box center, `[item * dim + axis]`: what a build splits on. */
|
|
315
|
-
itemCentroid2;
|
|
316
|
-
itemLeaf;
|
|
317
|
-
keys = [];
|
|
318
|
-
itemCap = 0;
|
|
319
|
-
itemTop = 0;
|
|
320
|
-
freeItems = [];
|
|
321
|
-
constructor(dim) {
|
|
322
|
-
this.dim = dim;
|
|
323
|
-
this.nodeBox = new Float64Array(0);
|
|
324
|
-
this.left = this.right = this.parent = this.leafCount = this.leafItems = this.count = new Int32Array(0);
|
|
325
|
-
this.itemBox = new Float64Array(0);
|
|
326
|
-
this.itemCentroid2 = new Float64Array(0);
|
|
327
|
-
this.itemLeaf = new Int32Array(0);
|
|
328
|
-
this.growNodes(16);
|
|
329
|
-
this.growItems(16);
|
|
330
|
-
}
|
|
331
|
-
growNodes(cap) {
|
|
332
|
-
const s = 2 * this.dim;
|
|
333
|
-
this.nodeBox = grow(this.nodeBox, cap * s);
|
|
334
|
-
this.left = grow(this.left, cap);
|
|
335
|
-
this.right = grow(this.right, cap);
|
|
336
|
-
this.parent = grow(this.parent, cap);
|
|
337
|
-
this.leafCount = grow(this.leafCount, cap);
|
|
338
|
-
this.leafItems = grow(this.leafItems, cap * LEAF_SIZE);
|
|
339
|
-
this.count = grow(this.count, cap);
|
|
340
|
-
this.nodeCap = cap;
|
|
341
|
-
}
|
|
342
|
-
growItems(cap) {
|
|
343
|
-
this.itemBox = grow(this.itemBox, cap * 2 * this.dim);
|
|
344
|
-
this.itemCentroid2 = grow(this.itemCentroid2, cap * this.dim);
|
|
345
|
-
this.itemLeaf = grow(this.itemLeaf, cap);
|
|
346
|
-
this.itemCap = cap;
|
|
347
|
-
}
|
|
348
|
-
allocNode(parent) {
|
|
349
|
-
let n = this.freeNodes.pop();
|
|
350
|
-
if (n === void 0) {
|
|
351
|
-
if (this.nodeTop === this.nodeCap) this.growNodes(this.nodeCap * 2);
|
|
352
|
-
n = this.nodeTop++;
|
|
353
|
-
}
|
|
354
|
-
this.left[n] = -1;
|
|
355
|
-
this.right[n] = -1;
|
|
356
|
-
this.parent[n] = parent;
|
|
357
|
-
this.leafCount[n] = 0;
|
|
358
|
-
return n;
|
|
359
|
-
}
|
|
360
|
-
freeNode(n) {
|
|
361
|
-
this.freeNodes.push(n);
|
|
362
|
-
}
|
|
363
|
-
allocItem(key, box) {
|
|
364
|
-
let i = this.freeItems.pop();
|
|
365
|
-
if (i === void 0) {
|
|
366
|
-
if (this.itemTop === this.itemCap) this.growItems(this.itemCap * 2);
|
|
367
|
-
i = this.itemTop++;
|
|
368
|
-
}
|
|
369
|
-
this.keys[i] = key;
|
|
370
|
-
this.writeItemBox(i, box);
|
|
371
|
-
return i;
|
|
372
|
-
}
|
|
373
|
-
freeItem(i) {
|
|
374
|
-
this.keys[i] = void 0;
|
|
375
|
-
this.itemLeaf[i] = -1;
|
|
376
|
-
this.freeItems.push(i);
|
|
377
|
-
}
|
|
378
|
-
writeItemBox(i, box) {
|
|
379
|
-
const d = this.dim;
|
|
380
|
-
const s = 2 * d;
|
|
381
|
-
for (let j = 0; j < s; j++) this.itemBox[i * s + j] = box[j];
|
|
382
|
-
for (let a = 0; a < d; a++) this.itemCentroid2[i * d + a] = box[a] + box[d + a];
|
|
383
|
-
}
|
|
384
|
-
/** Recompute `n`'s box and count from its children or items. */
|
|
385
|
-
computeBox(n) {
|
|
386
|
-
const d = this.dim;
|
|
387
|
-
const s = 2 * d;
|
|
388
|
-
const nb = this.nodeBox;
|
|
389
|
-
const o = n * s;
|
|
390
|
-
for (let j = 0; j < d; j++) {
|
|
391
|
-
nb[o + j] = Infinity;
|
|
392
|
-
nb[o + d + j] = -Infinity;
|
|
393
|
-
}
|
|
394
|
-
if (this.left[n] !== -1) {
|
|
395
|
-
unionInto(nb, o, nb, this.left[n] * s, d);
|
|
396
|
-
unionInto(nb, o, nb, this.right[n] * s, d);
|
|
397
|
-
this.count[n] = this.count[this.left[n]] + this.count[this.right[n]];
|
|
398
|
-
return;
|
|
399
|
-
}
|
|
400
|
-
this.count[n] = this.leafCount[n];
|
|
401
|
-
const base = n * LEAF_SIZE;
|
|
402
|
-
for (let k = 0; k < this.leafCount[n]; k++) unionInto(nb, o, this.itemBox, this.leafItems[base + k] * s, d);
|
|
403
|
-
}
|
|
404
|
-
refitUp(n) {
|
|
405
|
-
for (; n !== -1; n = this.parent[n]) this.computeBox(n);
|
|
406
|
-
}
|
|
407
|
-
/** Discard the topology and rebuild it top-down over every live item. */
|
|
408
|
-
build() {
|
|
409
|
-
this.freeNodes = [];
|
|
410
|
-
this.nodeTop = 0;
|
|
411
|
-
this.root = -1;
|
|
412
|
-
const live = [];
|
|
413
|
-
for (let i = 0; i < this.itemTop; i++) if (this.itemLeaf[i] !== -1) live.push(i);
|
|
414
|
-
if (live.length === 0) return;
|
|
415
|
-
if (this.nodeCap < 2 * live.length) this.growNodes(2 * live.length);
|
|
416
|
-
const idx = Int32Array.from(live);
|
|
417
|
-
this.root = this.buildRange(idx, 0, idx.length, -1);
|
|
418
|
-
}
|
|
419
|
-
/** Rebuild the subtree under `n` balanced, in its place. */
|
|
420
|
-
rebuildSubtree(n) {
|
|
421
|
-
const p = this.parent[n];
|
|
422
|
-
const wasLeft = p !== -1 && this.left[p] === n;
|
|
423
|
-
const items = new Int32Array(this.count[n]);
|
|
424
|
-
const nodes = [n];
|
|
425
|
-
let m = 0;
|
|
426
|
-
while (nodes.length > 0) {
|
|
427
|
-
const c = nodes.pop();
|
|
428
|
-
if (this.left[c] !== -1) nodes.push(this.left[c], this.right[c]);
|
|
429
|
-
else for (let k = 0; k < this.leafCount[c]; k++) items[m++] = this.leafItems[c * LEAF_SIZE + k];
|
|
430
|
-
this.freeNode(c);
|
|
431
|
-
}
|
|
432
|
-
const r = this.buildRange(items, 0, m, p);
|
|
433
|
-
if (p === -1) this.root = r;
|
|
434
|
-
else if (wasLeft) this.left[p] = r;
|
|
435
|
-
else this.right[p] = r;
|
|
436
|
-
}
|
|
437
|
-
/** `key` is scratch the length of `idx`, for `select`. */
|
|
438
|
-
buildRange(idx, lo, hi, parent, key = new Float64Array(idx.length)) {
|
|
439
|
-
const n = this.allocNode(parent);
|
|
440
|
-
if (hi - lo <= LEAF_SIZE) {
|
|
441
|
-
for (let k = lo; k < hi; k++) this.placeInLeaf(n, idx[k]);
|
|
442
|
-
} else {
|
|
443
|
-
const mid = lo + hi >> 1;
|
|
444
|
-
this.select(idx, key, lo, hi, mid, this.longestCentroidAxis(idx, lo, hi));
|
|
445
|
-
const l = this.buildRange(idx, lo, mid, n, key);
|
|
446
|
-
const r = this.buildRange(idx, mid, hi, n, key);
|
|
447
|
-
this.left[n] = l;
|
|
448
|
-
this.right[n] = r;
|
|
449
|
-
}
|
|
450
|
-
this.computeBox(n);
|
|
451
|
-
return n;
|
|
452
|
-
}
|
|
453
|
-
placeInLeaf(n, item) {
|
|
454
|
-
this.leafItems[n * LEAF_SIZE + this.leafCount[n]++] = item;
|
|
455
|
-
this.itemLeaf[item] = n;
|
|
456
|
-
}
|
|
457
|
-
/** Turn a full leaf into an internal node over two leaves, the full set
|
|
458
|
-
* plus `extra` split at the median of their longest centroid axis. */
|
|
459
|
-
splitLeaf(n, extra) {
|
|
460
|
-
const base = n * LEAF_SIZE;
|
|
461
|
-
const idx = new Int32Array(LEAF_SIZE + 1);
|
|
462
|
-
for (let k = 0; k < LEAF_SIZE; k++) idx[k] = this.leafItems[base + k];
|
|
463
|
-
idx[LEAF_SIZE] = extra;
|
|
464
|
-
const mid = idx.length >> 1;
|
|
465
|
-
this.select(idx, new Float64Array(idx.length), 0, idx.length, mid, this.longestCentroidAxis(idx, 0, idx.length));
|
|
466
|
-
this.leafCount[n] = 0;
|
|
467
|
-
const l = this.allocNode(n);
|
|
468
|
-
const r = this.allocNode(n);
|
|
469
|
-
for (let k = 0; k < mid; k++) this.placeInLeaf(l, idx[k]);
|
|
470
|
-
for (let k = mid; k < idx.length; k++) this.placeInLeaf(r, idx[k]);
|
|
471
|
-
this.left[n] = l;
|
|
472
|
-
this.right[n] = r;
|
|
473
|
-
this.computeBox(l);
|
|
474
|
-
this.computeBox(r);
|
|
475
|
-
}
|
|
476
|
-
longestCentroidAxis(idx, lo, hi) {
|
|
477
|
-
const d = this.dim;
|
|
478
|
-
const cent = this.itemCentroid2;
|
|
479
|
-
let best = 0;
|
|
480
|
-
let bestExt = -1;
|
|
481
|
-
for (let a = 0; a < this.dim; a++) {
|
|
482
|
-
let mn = Infinity;
|
|
483
|
-
let mx = -Infinity;
|
|
484
|
-
for (let k = lo; k < hi; k++) {
|
|
485
|
-
const c = cent[idx[k] * d + a];
|
|
486
|
-
if (c < mn) mn = c;
|
|
487
|
-
if (c > mx) mx = c;
|
|
488
|
-
}
|
|
489
|
-
if (mx - mn > bestExt) {
|
|
490
|
-
bestExt = mx - mn;
|
|
491
|
-
best = a;
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
return best;
|
|
495
|
-
}
|
|
496
|
-
/** Quickselect: reorder `idx[lo, hi)` so `idx[k]` holds the item whose
|
|
497
|
-
* centroid on `axis` ranks k, with smaller ones before it. The centroids
|
|
498
|
-
* are copied into `key` beside `idx` and swapped with it, so the scans read
|
|
499
|
-
* memory in order. */
|
|
500
|
-
select(idx, key, lo, hi, k, axis) {
|
|
501
|
-
const d = this.dim;
|
|
502
|
-
const cent = this.itemCentroid2;
|
|
503
|
-
for (let m = lo; m < hi; m++) key[m] = cent[idx[m] * d + axis];
|
|
504
|
-
let a = lo;
|
|
505
|
-
let b = hi - 1;
|
|
506
|
-
while (a < b) {
|
|
507
|
-
const pivot = key[a + b >> 1];
|
|
508
|
-
let i = a;
|
|
509
|
-
let j = b;
|
|
510
|
-
while (i <= j) {
|
|
511
|
-
while (key[i] < pivot) i++;
|
|
512
|
-
while (key[j] > pivot) j--;
|
|
513
|
-
if (i <= j) {
|
|
514
|
-
const t = idx[i];
|
|
515
|
-
idx[i] = idx[j];
|
|
516
|
-
idx[j] = t;
|
|
517
|
-
const u = key[i];
|
|
518
|
-
key[i] = key[j];
|
|
519
|
-
key[j] = u;
|
|
520
|
-
i++;
|
|
521
|
-
j--;
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
if (k <= j) b = j;
|
|
525
|
-
else if (k >= i) a = i;
|
|
526
|
-
else return;
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
};
|
|
530
|
-
function grow(arr, len) {
|
|
531
|
-
const next = new arr.constructor(len);
|
|
532
|
-
next.set(arr);
|
|
533
|
-
return next;
|
|
534
|
-
}
|
|
535
|
-
function unionInto(dst, o, src, so, d) {
|
|
536
|
-
for (let j = 0; j < d; j++) {
|
|
537
|
-
if (src[so + j] < dst[o + j]) dst[o + j] = src[so + j];
|
|
538
|
-
if (src[so + d + j] > dst[o + d + j]) dst[o + d + j] = src[so + d + j];
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
// src/bvh/bvhQuery.ts
|
|
543
|
-
var Scratch = class _Scratch {
|
|
544
|
-
stack = new Int32Array(64);
|
|
545
|
-
heapT = new Float64Array(64);
|
|
546
|
-
heapId = new Int32Array(64);
|
|
547
|
-
busy = false;
|
|
548
|
-
take() {
|
|
549
|
-
if (this.busy) return new _Scratch();
|
|
550
|
-
this.busy = true;
|
|
551
|
-
return this;
|
|
552
|
-
}
|
|
553
|
-
release() {
|
|
554
|
-
this.busy = false;
|
|
555
|
-
}
|
|
556
|
-
stackAt(len) {
|
|
557
|
-
if (len >= this.stack.length) {
|
|
558
|
-
const next = new Int32Array(this.stack.length * 2);
|
|
559
|
-
next.set(this.stack);
|
|
560
|
-
this.stack = next;
|
|
561
|
-
}
|
|
562
|
-
return this.stack;
|
|
563
|
-
}
|
|
564
|
-
ensureHeap(len) {
|
|
565
|
-
if (len < this.heapT.length) return;
|
|
566
|
-
const t = new Float64Array(this.heapT.length * 2);
|
|
567
|
-
const id = new Int32Array(this.heapId.length * 2);
|
|
568
|
-
t.set(this.heapT);
|
|
569
|
-
id.set(this.heapId);
|
|
570
|
-
this.heapT = t;
|
|
571
|
-
this.heapId = id;
|
|
572
|
-
}
|
|
573
|
-
};
|
|
574
|
-
function heapPush(sc, size, t, id) {
|
|
575
|
-
sc.ensureHeap(size + 1);
|
|
576
|
-
const ht = sc.heapT;
|
|
577
|
-
const hid = sc.heapId;
|
|
578
|
-
let i = size;
|
|
579
|
-
while (i > 0) {
|
|
580
|
-
const p = i - 1 >> 1;
|
|
581
|
-
if (ht[p] <= t) break;
|
|
582
|
-
ht[i] = ht[p];
|
|
583
|
-
hid[i] = hid[p];
|
|
584
|
-
i = p;
|
|
585
|
-
}
|
|
586
|
-
ht[i] = t;
|
|
587
|
-
hid[i] = id;
|
|
588
|
-
return size + 1;
|
|
589
|
-
}
|
|
590
|
-
function heapPop(sc, size) {
|
|
591
|
-
const ht = sc.heapT;
|
|
592
|
-
const hid = sc.heapId;
|
|
593
|
-
const n = size - 1;
|
|
594
|
-
const lastT = ht[n];
|
|
595
|
-
const lastId = hid[n];
|
|
596
|
-
let i = 0;
|
|
597
|
-
for (; ; ) {
|
|
598
|
-
let c = 2 * i + 1;
|
|
599
|
-
if (c >= n) break;
|
|
600
|
-
if (c + 1 < n && ht[c + 1] < ht[c]) c++;
|
|
601
|
-
if (ht[c] >= lastT) break;
|
|
602
|
-
ht[i] = ht[c];
|
|
603
|
-
hid[i] = hid[c];
|
|
604
|
-
i = c;
|
|
605
|
-
}
|
|
606
|
-
ht[i] = lastT;
|
|
607
|
-
hid[i] = lastId;
|
|
608
|
-
}
|
|
609
|
-
function meets2(arr, o, d, q, qmin, qmax) {
|
|
610
|
-
for (let j = 0; j < d; j++) if (arr[o + j] > q[qmax + j] || q[qmin + j] > arr[o + d + j]) return false;
|
|
611
|
-
return true;
|
|
612
|
-
}
|
|
613
|
-
function queryOverlap(tree, pool2, q, qmin, qmax, visit) {
|
|
614
|
-
if (tree.root === -1) return;
|
|
615
|
-
const sc = pool2.take();
|
|
616
|
-
const d = tree.dim;
|
|
617
|
-
const s = 2 * d;
|
|
618
|
-
try {
|
|
619
|
-
let stack = sc.stackAt(0);
|
|
620
|
-
let sp = 0;
|
|
621
|
-
stack[sp++] = tree.root;
|
|
622
|
-
while (sp > 0) {
|
|
623
|
-
const n = stack[--sp];
|
|
624
|
-
if (!meets2(tree.nodeBox, n * s, d, q, qmin, qmax)) continue;
|
|
625
|
-
if (tree.left[n] === -1) {
|
|
626
|
-
const base = n * LEAF_SIZE;
|
|
627
|
-
for (let k = 0; k < tree.leafCount[n]; k++) {
|
|
628
|
-
const it = tree.leafItems[base + k];
|
|
629
|
-
if (meets2(tree.itemBox, it * s, d, q, qmin, qmax) && visit(tree.keys[it]) === false) return;
|
|
630
|
-
}
|
|
631
|
-
continue;
|
|
632
|
-
}
|
|
633
|
-
stack = sc.stackAt(sp + 1);
|
|
634
|
-
stack[sp++] = tree.left[n];
|
|
635
|
-
stack[sp++] = tree.right[n];
|
|
636
|
-
}
|
|
637
|
-
} finally {
|
|
638
|
-
sc.release();
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
function notOutside(arr, o, d, planes) {
|
|
642
|
-
for (let p = 0; p < planes.length; p++) {
|
|
643
|
-
const { normal, offset } = planes[p];
|
|
644
|
-
let v = offset;
|
|
645
|
-
for (let j = 0; j < d; j++) v += normal[j] * (normal[j] >= 0 ? arr[o + d + j] : arr[o + j]);
|
|
646
|
-
if (v < 0) return false;
|
|
647
|
-
}
|
|
299
|
+
// src/bvh/settledCache.ts
|
|
300
|
+
function sameKey(a, b) {
|
|
301
|
+
if (a.length !== b.length) return false;
|
|
302
|
+
for (let i = 0; i < a.length; i++) if (!Object.is(a[i], b[i])) return false;
|
|
648
303
|
return true;
|
|
649
304
|
}
|
|
650
|
-
function
|
|
651
|
-
if (
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
if (!notOutside(tree.nodeBox, n * s, d, planes)) continue;
|
|
662
|
-
if (tree.left[n] === -1) {
|
|
663
|
-
const base = n * LEAF_SIZE;
|
|
664
|
-
for (let k = 0; k < tree.leafCount[n]; k++) {
|
|
665
|
-
const it = tree.leafItems[base + k];
|
|
666
|
-
if (notOutside(tree.itemBox, it * s, d, planes) && visit(tree.keys[it]) === false) return;
|
|
667
|
-
}
|
|
668
|
-
continue;
|
|
669
|
-
}
|
|
670
|
-
stack = sc.stackAt(sp + 1);
|
|
671
|
-
stack[sp++] = tree.left[n];
|
|
672
|
-
stack[sp++] = tree.right[n];
|
|
673
|
-
}
|
|
674
|
-
} finally {
|
|
675
|
-
sc.release();
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
function entry(arr, o, d, org, dir, tMax) {
|
|
679
|
-
let near = 0;
|
|
680
|
-
let far = tMax;
|
|
681
|
-
for (let j = 0; j < d; j++) {
|
|
682
|
-
const lo = arr[o + j];
|
|
683
|
-
const hi = arr[o + d + j];
|
|
684
|
-
if (dir[j] === 0) {
|
|
685
|
-
if (org[j] < lo || org[j] > hi) return -1;
|
|
686
|
-
continue;
|
|
687
|
-
}
|
|
688
|
-
const t1 = (lo - org[j]) / dir[j];
|
|
689
|
-
const t2 = (hi - org[j]) / dir[j];
|
|
690
|
-
if (t1 < t2) {
|
|
691
|
-
if (t1 > near) near = t1;
|
|
692
|
-
if (t2 < far) far = t2;
|
|
693
|
-
} else {
|
|
694
|
-
if (t2 > near) near = t2;
|
|
695
|
-
if (t1 < far) far = t1;
|
|
696
|
-
}
|
|
697
|
-
if (near > far) return -1;
|
|
698
|
-
}
|
|
699
|
-
return near;
|
|
700
|
-
}
|
|
701
|
-
function raycast(tree, pool2, org, dir, visit, tMax) {
|
|
702
|
-
if (tree.root === -1) return;
|
|
703
|
-
const d = tree.dim;
|
|
704
|
-
const s = 2 * d;
|
|
705
|
-
const t0 = entry(tree.nodeBox, tree.root * s, d, org, dir, tMax);
|
|
706
|
-
if (t0 < 0) return;
|
|
707
|
-
const sc = pool2.take();
|
|
708
|
-
try {
|
|
709
|
-
let size = heapPush(sc, 0, t0, tree.root);
|
|
710
|
-
while (size > 0) {
|
|
711
|
-
const t = sc.heapT[0];
|
|
712
|
-
const id = sc.heapId[0];
|
|
713
|
-
heapPop(sc, size--);
|
|
714
|
-
if (id < 0) {
|
|
715
|
-
if (visit(tree.keys[-id - 1], t) === false) return;
|
|
716
|
-
continue;
|
|
717
|
-
}
|
|
718
|
-
if (tree.left[id] === -1) {
|
|
719
|
-
const base = id * LEAF_SIZE;
|
|
720
|
-
for (let k = 0; k < tree.leafCount[id]; k++) {
|
|
721
|
-
const it = tree.leafItems[base + k];
|
|
722
|
-
const ti = entry(tree.itemBox, it * s, d, org, dir, tMax);
|
|
723
|
-
if (ti >= 0) size = heapPush(sc, size, ti, -it - 1);
|
|
724
|
-
}
|
|
725
|
-
continue;
|
|
726
|
-
}
|
|
727
|
-
const l = tree.left[id];
|
|
728
|
-
const r = tree.right[id];
|
|
729
|
-
const tl = entry(tree.nodeBox, l * s, d, org, dir, tMax);
|
|
730
|
-
if (tl >= 0) size = heapPush(sc, size, tl, l);
|
|
731
|
-
const tr = entry(tree.nodeBox, r * s, d, org, dir, tMax);
|
|
732
|
-
if (tr >= 0) size = heapPush(sc, size, tr, r);
|
|
733
|
-
}
|
|
734
|
-
} finally {
|
|
735
|
-
sc.release();
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
// src/bvh/bvh.ts
|
|
740
|
-
var ALPHA = 0.7;
|
|
741
|
-
function depthLimit(n) {
|
|
742
|
-
return 2 * Math.ceil(Math.log2(Math.max(n, 2)));
|
|
743
|
-
}
|
|
744
|
-
function balancedHeight(m) {
|
|
745
|
-
let h = 0;
|
|
746
|
-
for (; m > LEAF_SIZE; m = Math.ceil(m / 2)) h++;
|
|
747
|
-
return h;
|
|
748
|
-
}
|
|
749
|
-
function validBox(box, dim) {
|
|
750
|
-
if (box.length < 2 * dim) return false;
|
|
751
|
-
for (let j = 0; j < dim; j++) {
|
|
752
|
-
const lo = box[j];
|
|
753
|
-
const hi = box[dim + j];
|
|
754
|
-
if (!Number.isFinite(lo) || !Number.isFinite(hi) || lo > hi) return false;
|
|
755
|
-
}
|
|
756
|
-
return true;
|
|
757
|
-
}
|
|
758
|
-
var BvhImpl = class {
|
|
759
|
-
dim;
|
|
760
|
-
tree;
|
|
761
|
-
items = /* @__PURE__ */ new Map();
|
|
762
|
-
scratch = new Scratch();
|
|
763
|
-
/** Largest size since the last whole rebuild; shrinking to half of it rebuilds again. */
|
|
764
|
-
peakSize = 0;
|
|
765
|
-
constructor(dim) {
|
|
766
|
-
this.dim = dim;
|
|
767
|
-
this.tree = new BvhTree(dim);
|
|
768
|
-
}
|
|
769
|
-
get size() {
|
|
770
|
-
return this.items.size;
|
|
771
|
-
}
|
|
772
|
-
/** Store without touching the topology; `rebuild` places them. */
|
|
773
|
-
load(entries) {
|
|
774
|
-
for (const [key, box] of entries) {
|
|
775
|
-
if (!validBox(box, this.dim)) continue;
|
|
776
|
-
const old = this.items.get(key);
|
|
777
|
-
if (old !== void 0) {
|
|
778
|
-
this.tree.writeItemBox(old, box);
|
|
779
|
-
continue;
|
|
780
|
-
}
|
|
781
|
-
const it = this.tree.allocItem(key, box);
|
|
782
|
-
this.tree.itemLeaf[it] = 0;
|
|
783
|
-
this.items.set(key, it);
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
insert(key, box) {
|
|
787
|
-
if (!validBox(box, this.dim)) return false;
|
|
788
|
-
if (this.items.has(key)) return this.update(key, box);
|
|
789
|
-
const it = this.tree.allocItem(key, box);
|
|
790
|
-
this.items.set(key, it);
|
|
791
|
-
if (this.items.size > this.peakSize) this.peakSize = this.items.size;
|
|
792
|
-
this.place(it);
|
|
793
|
-
return true;
|
|
794
|
-
}
|
|
795
|
-
update(key, box) {
|
|
796
|
-
const it = this.items.get(key);
|
|
797
|
-
if (it === void 0 || !validBox(box, this.dim)) return false;
|
|
798
|
-
const t = this.tree;
|
|
799
|
-
const leaf = t.itemLeaf[it];
|
|
800
|
-
if (this.leafHolds(leaf, box)) {
|
|
801
|
-
t.writeItemBox(it, box);
|
|
802
|
-
return true;
|
|
803
|
-
}
|
|
804
|
-
this.detach(it);
|
|
805
|
-
t.writeItemBox(it, box);
|
|
806
|
-
this.place(it);
|
|
807
|
-
return true;
|
|
808
|
-
}
|
|
809
|
-
remove(key) {
|
|
810
|
-
const it = this.items.get(key);
|
|
811
|
-
if (it === void 0) return false;
|
|
812
|
-
this.detach(it);
|
|
813
|
-
this.items.delete(key);
|
|
814
|
-
this.tree.freeItem(it);
|
|
815
|
-
if (this.items.size < this.peakSize >> 1) this.rebuild();
|
|
816
|
-
return true;
|
|
817
|
-
}
|
|
818
|
-
refit() {
|
|
819
|
-
const t = this.tree;
|
|
820
|
-
if (t.root === -1) return;
|
|
821
|
-
const sc = this.scratch.take();
|
|
822
|
-
try {
|
|
823
|
-
let order = sc.stackAt(0);
|
|
824
|
-
order[0] = t.root;
|
|
825
|
-
let len = 1;
|
|
826
|
-
for (let i = 0; i < len; i++) {
|
|
827
|
-
const n = order[i];
|
|
828
|
-
if (t.left[n] === -1) continue;
|
|
829
|
-
order = sc.stackAt(len + 1);
|
|
830
|
-
order[len++] = t.left[n];
|
|
831
|
-
order[len++] = t.right[n];
|
|
832
|
-
}
|
|
833
|
-
for (let i = len - 1; i >= 0; i--) t.computeBox(order[i]);
|
|
834
|
-
} finally {
|
|
835
|
-
sc.release();
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
rebuild() {
|
|
839
|
-
this.tree.build();
|
|
840
|
-
this.peakSize = this.items.size;
|
|
841
|
-
}
|
|
842
|
-
/** Deepest leaf, counting the root as depth 0. */
|
|
843
|
-
maxDepth() {
|
|
844
|
-
const t = this.tree;
|
|
845
|
-
if (t.root === -1) return 0;
|
|
846
|
-
let deepest = 0;
|
|
847
|
-
const stack = [[t.root, 0]];
|
|
848
|
-
while (stack.length > 0) {
|
|
849
|
-
const [n, d] = stack.pop();
|
|
850
|
-
if (t.left[n] === -1) deepest = Math.max(deepest, d);
|
|
851
|
-
else stack.push([t.left[n], d + 1], [t.right[n], d + 1]);
|
|
852
|
-
}
|
|
853
|
-
return deepest;
|
|
854
|
-
}
|
|
855
|
-
queryBox(box, visit) {
|
|
856
|
-
queryOverlap(this.tree, this.scratch, box, 0, this.dim, visit);
|
|
857
|
-
}
|
|
858
|
-
queryPoint(p, visit) {
|
|
859
|
-
queryOverlap(this.tree, this.scratch, p, 0, 0, visit);
|
|
860
|
-
}
|
|
861
|
-
raycast(origin, dir, visit, tMax = Infinity) {
|
|
862
|
-
raycast(this.tree, this.scratch, origin, dir, visit, tMax);
|
|
863
|
-
}
|
|
864
|
-
queryHalfSpaces(planes, visit) {
|
|
865
|
-
queryHalfSpaces(this.tree, this.scratch, planes, visit);
|
|
866
|
-
}
|
|
867
|
-
leafHolds(leaf, box) {
|
|
868
|
-
const d = this.dim;
|
|
869
|
-
const o = leaf * 2 * d;
|
|
870
|
-
const nb = this.tree.nodeBox;
|
|
871
|
-
for (let j = 0; j < d; j++) if (box[j] < nb[o + j] || box[d + j] > nb[o + d + j]) return false;
|
|
872
|
-
return true;
|
|
873
|
-
}
|
|
874
|
-
/** Descend by least enlargement and add `it` to a leaf, splitting it when full. */
|
|
875
|
-
place(it) {
|
|
876
|
-
const t = this.tree;
|
|
877
|
-
if (t.root === -1) {
|
|
878
|
-
t.root = t.allocNode(-1);
|
|
879
|
-
t.placeInLeaf(t.root, it);
|
|
880
|
-
t.computeBox(t.root);
|
|
881
|
-
return;
|
|
882
|
-
}
|
|
883
|
-
let n = t.root;
|
|
884
|
-
let depth = 0;
|
|
885
|
-
while (t.left[n] !== -1) {
|
|
886
|
-
const l = t.left[n];
|
|
887
|
-
const r = t.right[n];
|
|
888
|
-
const el = this.enlargement(l, it);
|
|
889
|
-
const er = this.enlargement(r, it);
|
|
890
|
-
n = el < er || el === er && t.count[l] <= t.count[r] ? l : r;
|
|
891
|
-
depth++;
|
|
892
|
-
}
|
|
893
|
-
if (t.leafCount[n] < LEAF_SIZE) t.placeInLeaf(n, it);
|
|
894
|
-
else {
|
|
895
|
-
t.splitLeaf(n, it);
|
|
896
|
-
depth++;
|
|
897
|
-
}
|
|
898
|
-
t.refitUp(n);
|
|
899
|
-
if (depth > depthLimit(this.items.size)) this.rebalance(t.itemLeaf[it], depth);
|
|
900
|
-
}
|
|
901
|
-
/** Least enlargement alone lets sorted or coincident inserts grow one
|
|
902
|
-
* spine. Rebuild the lowest ancestor whose balanced rebuild brings this
|
|
903
|
-
* leaf back within the limit, preferring a weight-unbalanced one. */
|
|
904
|
-
rebalance(leaf, depth) {
|
|
905
|
-
const t = this.tree;
|
|
906
|
-
const limit = depthLimit(this.items.size);
|
|
907
|
-
let fit = -1;
|
|
908
|
-
for (let c = leaf, a = t.parent[leaf], d = depth - 1; a !== -1; c = a, a = t.parent[a], d--) {
|
|
909
|
-
if (d + balancedHeight(t.count[a]) > limit) continue;
|
|
910
|
-
if (t.count[c] > ALPHA * t.count[a]) {
|
|
911
|
-
t.rebuildSubtree(a);
|
|
912
|
-
return;
|
|
305
|
+
function createSettledCache({ reads = 2 } = {}) {
|
|
306
|
+
if (!(reads >= 1) || !Number.isInteger(reads)) throw new RangeError(`createSettledCache: reads must be a positive integer, got ${reads}`);
|
|
307
|
+
let seen;
|
|
308
|
+
let count = 0;
|
|
309
|
+
let built = null;
|
|
310
|
+
return {
|
|
311
|
+
read(key, build) {
|
|
312
|
+
if (seen === void 0 || !sameKey(seen, key)) {
|
|
313
|
+
seen = key.slice();
|
|
314
|
+
count = 0;
|
|
315
|
+
built = null;
|
|
913
316
|
}
|
|
914
|
-
if (
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
const nb = this.tree.nodeBox;
|
|
924
|
-
const ib = this.tree.itemBox;
|
|
925
|
-
const o = n * 2 * d;
|
|
926
|
-
const io = it * 2 * d;
|
|
927
|
-
let grow2 = 0;
|
|
928
|
-
for (let j = 0; j < d; j++) {
|
|
929
|
-
const lo = Math.min(nb[o + j], ib[io + j]);
|
|
930
|
-
const hi = Math.max(nb[o + d + j], ib[io + d + j]);
|
|
931
|
-
grow2 += hi - lo - (nb[o + d + j] - nb[o + j]);
|
|
932
|
-
}
|
|
933
|
-
return grow2;
|
|
934
|
-
}
|
|
935
|
-
/** Take `it` out of its leaf, collapsing the leaf into its sibling when it empties. */
|
|
936
|
-
detach(it) {
|
|
937
|
-
const t = this.tree;
|
|
938
|
-
const leaf = t.itemLeaf[it];
|
|
939
|
-
const base = leaf * LEAF_SIZE;
|
|
940
|
-
const count = t.leafCount[leaf];
|
|
941
|
-
for (let k = 0; k < count; k++) {
|
|
942
|
-
if (t.leafItems[base + k] !== it) continue;
|
|
943
|
-
t.leafItems[base + k] = t.leafItems[base + count - 1];
|
|
944
|
-
break;
|
|
945
|
-
}
|
|
946
|
-
t.leafCount[leaf] = count - 1;
|
|
947
|
-
t.itemLeaf[it] = -1;
|
|
948
|
-
if (count > 1) {
|
|
949
|
-
t.refitUp(leaf);
|
|
950
|
-
return;
|
|
951
|
-
}
|
|
952
|
-
const p = t.parent[leaf];
|
|
953
|
-
t.freeNode(leaf);
|
|
954
|
-
if (p === -1) {
|
|
955
|
-
t.root = -1;
|
|
956
|
-
return;
|
|
317
|
+
if (built !== null) return built.value;
|
|
318
|
+
if (++count < reads) return null;
|
|
319
|
+
built = { value: build() };
|
|
320
|
+
return built.value;
|
|
321
|
+
},
|
|
322
|
+
clear() {
|
|
323
|
+
seen = void 0;
|
|
324
|
+
count = 0;
|
|
325
|
+
built = null;
|
|
957
326
|
}
|
|
958
|
-
|
|
959
|
-
const g = t.parent[p];
|
|
960
|
-
t.parent[sib] = g;
|
|
961
|
-
if (g === -1) t.root = sib;
|
|
962
|
-
else if (t.left[g] === p) t.left[g] = sib;
|
|
963
|
-
else t.right[g] = sib;
|
|
964
|
-
t.freeNode(p);
|
|
965
|
-
t.refitUp(g);
|
|
966
|
-
}
|
|
967
|
-
};
|
|
968
|
-
function createBvh(dim, entries) {
|
|
969
|
-
if (!Number.isInteger(dim) || dim < 1) throw new RangeError(`createBvh: dim must be a positive integer, got ${dim}`);
|
|
970
|
-
const bvh = new BvhImpl(dim);
|
|
971
|
-
if (entries) {
|
|
972
|
-
bvh.load(entries);
|
|
973
|
-
bvh.rebuild();
|
|
974
|
-
}
|
|
975
|
-
return bvh;
|
|
327
|
+
};
|
|
976
328
|
}
|
|
977
329
|
|
|
978
|
-
export { closestBetweenFlats, closestOnFlat,
|
|
330
|
+
export { closestBetweenFlats, closestOnFlat, createSettledCache, flatDim, intersectFlats, resolveSnap, stepLine };
|
|
979
331
|
//# sourceMappingURL=index.js.map
|
|
980
332
|
//# sourceMappingURL=index.js.map
|