@weasel-js/kernel3d 1.17.3 → 1.18.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/index.d.ts +8 -8
- package/dist/index.js +118 -98
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
package/dist/index.d.ts
CHANGED
|
@@ -184,14 +184,14 @@ declare function rayThroughScreenPoint(point: {
|
|
|
184
184
|
}, rect: ScreenBox, viewProjection: Mat4): Ray | null;
|
|
185
185
|
/**
|
|
186
186
|
* The screen rectangle a world-space box covers — the currency weasel's chrome
|
|
187
|
-
* and `PoseDescriptor.getBounds` speak in. `null` when the box
|
|
188
|
-
*
|
|
187
|
+
* and `PoseDescriptor.getBounds` speak in. `null` when none of the box lies
|
|
188
|
+
* between the near and far planes, which is when the camera draws none of it.
|
|
189
189
|
*
|
|
190
|
-
* Each of the twelve edges is clipped against the near
|
|
191
|
-
* projected, rather than dropping the corners behind
|
|
192
|
-
* a box that is too small for anything straddling the
|
|
193
|
-
* grows as the camera moves in: a solid the pointer
|
|
194
|
-
* as a sliver, or as nothing.
|
|
190
|
+
* Each of the twelve edges is clipped against the near and far planes before
|
|
191
|
+
* it is projected, rather than dropping the corners behind the camera.
|
|
192
|
+
* Dropping them reports a box that is too small for anything straddling the
|
|
193
|
+
* near plane, and the error grows as the camera moves in: a solid the pointer
|
|
194
|
+
* is inside of would outline as a sliver, or as nothing.
|
|
195
195
|
*/
|
|
196
196
|
declare function projectAabbToScreen(box: Aabb, viewProjection: Mat4, rect: ScreenBox): ScreenBox | null;
|
|
197
197
|
|
|
@@ -342,7 +342,7 @@ interface Footprint {
|
|
|
342
342
|
/**
|
|
343
343
|
* A drag becomes a footprint on the ground plane: its ends, or without them
|
|
344
344
|
* its box's four corners, are cast onto the plane and the hit points give the
|
|
345
|
-
* extent. What node that implies is the consumer's answer — the kernel does
|
|
345
|
+
* extent. One that misses the plane, past the horizon, declines the insert. What node that implies is the consumer's answer — the kernel does
|
|
346
346
|
* not know what it is inserting. The depth the 2D contract cannot express
|
|
347
347
|
* comes from the plane, not from the caller.
|
|
348
348
|
*/
|
package/dist/index.js
CHANGED
|
@@ -139,9 +139,10 @@ function rayThroughScreenPoint(point, rect, viewProjection) {
|
|
|
139
139
|
if (unit.x === 0 && unit.y === 0 && unit.z === 0) return null;
|
|
140
140
|
return { origin: near, direction };
|
|
141
141
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
142
|
+
var CLIP_SIDES = [
|
|
143
|
+
(p) => p[2] + p[3],
|
|
144
|
+
(p) => p[3] - p[2]
|
|
145
|
+
];
|
|
145
146
|
function lerpClip(a, b, t) {
|
|
146
147
|
return [
|
|
147
148
|
a[0] + (b[0] - a[0]) * t,
|
|
@@ -178,15 +179,24 @@ function projectAabbToScreen(box, viewProjection, rect) {
|
|
|
178
179
|
for (let i = 0; i < 8; i++) {
|
|
179
180
|
for (const bit of EDGE_BITS) {
|
|
180
181
|
if (i & bit) continue;
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
182
|
+
const a = corners[i];
|
|
183
|
+
const b = corners[i | bit];
|
|
184
|
+
let t0 = 0;
|
|
185
|
+
let t1 = 1;
|
|
186
|
+
for (const side of CLIP_SIDES) {
|
|
187
|
+
const da = side(a);
|
|
188
|
+
const db = side(b);
|
|
189
|
+
if (da < 0 && db < 0) {
|
|
190
|
+
t0 = 1;
|
|
191
|
+
t1 = 0;
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
if (da < 0) t0 = Math.max(t0, da / (da - db));
|
|
195
|
+
else if (db < 0) t1 = Math.min(t1, da / (da - db));
|
|
196
|
+
}
|
|
197
|
+
if (t0 > t1) continue;
|
|
198
|
+
extend(t0 === 0 ? a : lerpClip(a, b, t0));
|
|
199
|
+
extend(t1 === 1 ? b : lerpClip(a, b, t1));
|
|
190
200
|
}
|
|
191
201
|
}
|
|
192
202
|
if (!any) return null;
|
|
@@ -272,10 +282,65 @@ function withDescendants(scene, ids) {
|
|
|
272
282
|
}
|
|
273
283
|
return out;
|
|
274
284
|
}
|
|
285
|
+
function boxIndexable({ min, max }) {
|
|
286
|
+
return Number.isFinite(min.x) && Number.isFinite(min.y) && Number.isFinite(min.z) && Number.isFinite(max.x) && Number.isFinite(max.y) && Number.isFinite(max.z) && min.x <= max.x && min.y <= max.y && min.z <= max.z;
|
|
287
|
+
}
|
|
275
288
|
function pointBox(p) {
|
|
276
289
|
return { min: p, max: p };
|
|
277
290
|
}
|
|
278
291
|
|
|
292
|
+
// src/snap/boxIndex.ts
|
|
293
|
+
var CACHE = /* @__PURE__ */ new WeakMap();
|
|
294
|
+
function nodesOf(world) {
|
|
295
|
+
return world.scene.renderOrderNodes();
|
|
296
|
+
}
|
|
297
|
+
function build(world) {
|
|
298
|
+
const nodes = nodesOf(world);
|
|
299
|
+
const boxes = nodes.map(worldBoxes(world, "document"));
|
|
300
|
+
const entries = [];
|
|
301
|
+
const always = [];
|
|
302
|
+
boxes.forEach((box, i) => {
|
|
303
|
+
if (box === null) return;
|
|
304
|
+
const { min, max } = box;
|
|
305
|
+
if (boxIndexable(box)) entries.push([i, [min.x, min.y, min.z, max.x, max.y, max.z]]);
|
|
306
|
+
else always.push(i);
|
|
307
|
+
});
|
|
308
|
+
return { ids: nodes.map((n) => n.id), boxes, tree: createBvh(3, entries), always };
|
|
309
|
+
}
|
|
310
|
+
function current(world) {
|
|
311
|
+
let cache = CACHE.get(world);
|
|
312
|
+
if (cache === void 0) CACHE.set(world, cache = createSettledCache());
|
|
313
|
+
return cache.read([world.scene.getVersion(), world.bounds], () => build(world));
|
|
314
|
+
}
|
|
315
|
+
function padQuery(box) {
|
|
316
|
+
let m = 0;
|
|
317
|
+
for (const v of box) if (Number.isFinite(v)) m = Math.max(m, Math.abs(v));
|
|
318
|
+
const e = 1e-9 * (1 + m);
|
|
319
|
+
return [box[0] - e, box[1] - e, box[2] - e, box[3] + e, box[4] + e, box[5] + e];
|
|
320
|
+
}
|
|
321
|
+
function snapBoxes(world, exclude, near) {
|
|
322
|
+
const excluded = withDescendants(world.scene, exclude);
|
|
323
|
+
const index = near.every((b) => Array.prototype.every.call(b, (v) => !Number.isNaN(v))) ? current(world) : null;
|
|
324
|
+
if (index === null) {
|
|
325
|
+
const boxOf = worldBoxes(world, "document");
|
|
326
|
+
const out2 = [];
|
|
327
|
+
for (const node of nodesOf(world)) {
|
|
328
|
+
const box = excluded.has(node.id) ? null : boxOf(node);
|
|
329
|
+
if (box) out2.push(box);
|
|
330
|
+
}
|
|
331
|
+
return out2;
|
|
332
|
+
}
|
|
333
|
+
const found = new Set(index.always);
|
|
334
|
+
for (const box of near) index.tree.queryBox(box, (i) => {
|
|
335
|
+
found.add(i);
|
|
336
|
+
});
|
|
337
|
+
const out = [];
|
|
338
|
+
for (const i of [...found].sort((a, b) => a - b)) {
|
|
339
|
+
if (!excluded.has(index.ids[i])) out.push(index.boxes[i]);
|
|
340
|
+
}
|
|
341
|
+
return out;
|
|
342
|
+
}
|
|
343
|
+
|
|
279
344
|
// src/pickIndex.ts
|
|
280
345
|
function screenBoxOf(world, id, viewport) {
|
|
281
346
|
const node = world.scene.get(id);
|
|
@@ -283,9 +348,6 @@ function screenBoxOf(world, id, viewport) {
|
|
|
283
348
|
const box = node && pose && boxAt(world, node, pose);
|
|
284
349
|
return box ? projectAabbToScreen(box, viewProjectionOf(viewport), rectOf(viewport)) : null;
|
|
285
350
|
}
|
|
286
|
-
function boxValid({ min, max }) {
|
|
287
|
-
return Number.isFinite(min.x) && Number.isFinite(min.y) && Number.isFinite(min.z) && Number.isFinite(max.x) && Number.isFinite(max.y) && Number.isFinite(max.z) && min.x <= max.x && min.y <= max.y && min.z <= max.z;
|
|
288
|
-
}
|
|
289
351
|
var NDC_SLACK = 1e-9;
|
|
290
352
|
function sidePlanes(vp, rect) {
|
|
291
353
|
const m = viewProjectionOf(vp);
|
|
@@ -355,27 +417,15 @@ var BuiltIndex = class {
|
|
|
355
417
|
return hits;
|
|
356
418
|
}
|
|
357
419
|
};
|
|
358
|
-
var
|
|
359
|
-
function
|
|
360
|
-
const m = Math.max(
|
|
361
|
-
Math.abs(min.x),
|
|
362
|
-
Math.abs(min.y),
|
|
363
|
-
Math.abs(min.z),
|
|
364
|
-
Math.abs(max.x),
|
|
365
|
-
Math.abs(max.y),
|
|
366
|
-
Math.abs(max.z)
|
|
367
|
-
);
|
|
368
|
-
const e = 1e-9 * (1 + m);
|
|
369
|
-
return [min.x - e, min.y - e, min.z - e, max.x + e, max.y + e, max.z + e];
|
|
370
|
-
}
|
|
371
|
-
function build(world, order) {
|
|
420
|
+
var CACHE2 = /* @__PURE__ */ new WeakMap();
|
|
421
|
+
function build2(world, order) {
|
|
372
422
|
const boxOf = worldBoxes(world, "shown");
|
|
373
423
|
const boxes = order.map(boxOf);
|
|
374
424
|
const entries = [];
|
|
375
425
|
const always = [];
|
|
376
426
|
boxes.forEach((b, i) => {
|
|
377
427
|
if (b === null) return;
|
|
378
|
-
if (
|
|
428
|
+
if (boxIndexable(b)) entries.push([i, padQuery([b.min.x, b.min.y, b.min.z, b.max.x, b.max.y, b.max.z])]);
|
|
379
429
|
else always.push(i);
|
|
380
430
|
});
|
|
381
431
|
return new BuiltIndex(order, boxes, createBvh(3, entries), always);
|
|
@@ -385,12 +435,12 @@ function worldPickIndex(world) {
|
|
|
385
435
|
const version = scene.getVersion();
|
|
386
436
|
const overrides = scene.overrides.getGeneration();
|
|
387
437
|
const order = scene.renderOrderNodes();
|
|
388
|
-
const hit =
|
|
438
|
+
const hit = CACHE2.get(world);
|
|
389
439
|
if (hit && hit.version === version && hit.overrides === overrides && hit.bounds === world.bounds && hit.order === order) {
|
|
390
440
|
return hit.index;
|
|
391
441
|
}
|
|
392
|
-
const index =
|
|
393
|
-
|
|
442
|
+
const index = build2(world, order);
|
|
443
|
+
CACHE2.set(world, { version, overrides, bounds: world.bounds, order, index });
|
|
394
444
|
return index;
|
|
395
445
|
}
|
|
396
446
|
var ZERO = { x: 0, y: 0, z: 0 };
|
|
@@ -544,7 +594,7 @@ function candidatesOf(inputs, sources, probes, delta0, vp, origin, up) {
|
|
|
544
594
|
gesture: req.gesture,
|
|
545
595
|
probes: offered,
|
|
546
596
|
delta0,
|
|
547
|
-
exclude: req.ids ?? [],
|
|
597
|
+
exclude: req.exclude ?? req.ids ?? [],
|
|
548
598
|
...req.ids ? { ids: req.ids } : {},
|
|
549
599
|
...req.parent !== void 0 ? { parent: req.parent } : {},
|
|
550
600
|
...origin ? { origin } : {},
|
|
@@ -760,7 +810,13 @@ function footprintPoints(bounds, ends, cast) {
|
|
|
760
810
|
{ x: bounds.x, y: bounds.y + bounds.height },
|
|
761
811
|
{ x: bounds.x + bounds.width, y: bounds.y + bounds.height }
|
|
762
812
|
];
|
|
763
|
-
|
|
813
|
+
const hits = [];
|
|
814
|
+
for (const corner of corners) {
|
|
815
|
+
const hit = cast(corner);
|
|
816
|
+
if (!hit) return [];
|
|
817
|
+
hits.push(hit);
|
|
818
|
+
}
|
|
819
|
+
return hits;
|
|
764
820
|
}
|
|
765
821
|
function createInsert(opts) {
|
|
766
822
|
const up = opts.up ?? DEFAULT_UP;
|
|
@@ -891,61 +947,6 @@ function aimSpace3d(viewport, viewId, opts = {}) {
|
|
|
891
947
|
function cameraOfAim3d(aim) {
|
|
892
948
|
return aim.viewport.camera;
|
|
893
949
|
}
|
|
894
|
-
var CACHE2 = /* @__PURE__ */ new WeakMap();
|
|
895
|
-
function nodesOf(world) {
|
|
896
|
-
return world.scene.renderOrderNodes();
|
|
897
|
-
}
|
|
898
|
-
function boxValid2({ min, max }) {
|
|
899
|
-
return Number.isFinite(min.x) && Number.isFinite(min.y) && Number.isFinite(min.z) && Number.isFinite(max.x) && Number.isFinite(max.y) && Number.isFinite(max.z) && min.x <= max.x && min.y <= max.y && min.z <= max.z;
|
|
900
|
-
}
|
|
901
|
-
function build2(world) {
|
|
902
|
-
const nodes = nodesOf(world);
|
|
903
|
-
const boxes = nodes.map(worldBoxes(world, "document"));
|
|
904
|
-
const entries = [];
|
|
905
|
-
const always = [];
|
|
906
|
-
boxes.forEach((box, i) => {
|
|
907
|
-
if (box === null) return;
|
|
908
|
-
const { min, max } = box;
|
|
909
|
-
if (boxValid2(box)) entries.push([i, [min.x, min.y, min.z, max.x, max.y, max.z]]);
|
|
910
|
-
else always.push(i);
|
|
911
|
-
});
|
|
912
|
-
return { ids: nodes.map((n) => n.id), boxes, tree: createBvh(3, entries), always };
|
|
913
|
-
}
|
|
914
|
-
function current(world) {
|
|
915
|
-
let cache = CACHE2.get(world);
|
|
916
|
-
if (cache === void 0) CACHE2.set(world, cache = createSettledCache());
|
|
917
|
-
return cache.read([world.scene.getVersion(), world.bounds], () => build2(world));
|
|
918
|
-
}
|
|
919
|
-
function padQuery(box) {
|
|
920
|
-
let m = 0;
|
|
921
|
-
for (const v of box) if (Number.isFinite(v)) m = Math.max(m, Math.abs(v));
|
|
922
|
-
const e = 1e-9 * (1 + m);
|
|
923
|
-
return [box[0] - e, box[1] - e, box[2] - e, box[3] + e, box[4] + e, box[5] + e];
|
|
924
|
-
}
|
|
925
|
-
function snapBoxes(world, exclude, near) {
|
|
926
|
-
const excluded = withDescendants(world.scene, exclude);
|
|
927
|
-
const index = near.every((b) => Array.prototype.every.call(b, (v) => !Number.isNaN(v))) ? current(world) : null;
|
|
928
|
-
if (index === null) {
|
|
929
|
-
const boxOf = worldBoxes(world, "document");
|
|
930
|
-
const out2 = [];
|
|
931
|
-
for (const node of nodesOf(world)) {
|
|
932
|
-
const box = excluded.has(node.id) ? null : boxOf(node);
|
|
933
|
-
if (box) out2.push(box);
|
|
934
|
-
}
|
|
935
|
-
return out2;
|
|
936
|
-
}
|
|
937
|
-
const found = new Set(index.always);
|
|
938
|
-
for (const box of near) index.tree.queryBox(box, (i) => {
|
|
939
|
-
found.add(i);
|
|
940
|
-
});
|
|
941
|
-
const out = [];
|
|
942
|
-
for (const i of [...found].sort((a, b) => a - b)) {
|
|
943
|
-
if (!excluded.has(index.ids[i])) out.push(index.boxes[i]);
|
|
944
|
-
}
|
|
945
|
-
return out;
|
|
946
|
-
}
|
|
947
|
-
|
|
948
|
-
// src/snap/sources.ts
|
|
949
950
|
var AXES = ["x", "y", "z"];
|
|
950
951
|
var BITS = [1, 2, 4];
|
|
951
952
|
var UNIT = { x: { x: 1, y: 0, z: 0 }, y: { x: 0, y: 1, z: 0 }, z: { x: 0, y: 0, z: 1 } };
|
|
@@ -1467,15 +1468,32 @@ var DOLLY_RATE = 15e-4;
|
|
|
1467
1468
|
function screenDelta(move) {
|
|
1468
1469
|
return move.drag?.screenDelta ?? move.drag?.delta;
|
|
1469
1470
|
}
|
|
1471
|
+
function cameraStepper(dep, zero, relative, step) {
|
|
1472
|
+
let origin = dep?.get();
|
|
1473
|
+
let from = zero;
|
|
1474
|
+
let reached = zero;
|
|
1475
|
+
let written = origin;
|
|
1476
|
+
let prior = origin;
|
|
1477
|
+
return (at) => {
|
|
1478
|
+
if (!dep || !origin) return;
|
|
1479
|
+
const now = dep.get();
|
|
1480
|
+
if (now !== written && now !== prior) {
|
|
1481
|
+
origin = now;
|
|
1482
|
+
from = reached;
|
|
1483
|
+
}
|
|
1484
|
+
prior = now;
|
|
1485
|
+
written = step(origin, relative(at, from), dep);
|
|
1486
|
+
reached = at;
|
|
1487
|
+
dep.set(written);
|
|
1488
|
+
};
|
|
1489
|
+
}
|
|
1470
1490
|
function dragCamera(ctx, kind, step) {
|
|
1471
|
-
const
|
|
1472
|
-
const origin = dep?.get();
|
|
1491
|
+
const apply = cameraStepper(ctx.deps.camera3d, { x: 0, y: 0 }, (a, b) => ({ x: a.x - b.x, y: a.y - b.y }), step);
|
|
1473
1492
|
return {
|
|
1474
1493
|
kind,
|
|
1475
1494
|
onMove(move) {
|
|
1476
1495
|
const delta = screenDelta(move);
|
|
1477
|
-
if (
|
|
1478
|
-
dep.set(step(origin, delta, dep));
|
|
1496
|
+
if (delta) apply(delta);
|
|
1479
1497
|
}
|
|
1480
1498
|
};
|
|
1481
1499
|
}
|
|
@@ -1552,16 +1570,18 @@ var pinchDollyAction = {
|
|
|
1552
1570
|
timing: "ongoing",
|
|
1553
1571
|
start(ctx) {
|
|
1554
1572
|
const dep = ctx.deps.camera3d;
|
|
1555
|
-
const origin = dep?.get();
|
|
1556
1573
|
const startSpread = ctx.multiTouch?.spread ?? 0;
|
|
1557
1574
|
const at = ctx.multiTouch?.centroid;
|
|
1575
|
+
const apply = cameraStepper(dep, 1, (a, b) => a / b, (origin, factor, d) => {
|
|
1576
|
+
const pane = d.size();
|
|
1577
|
+
return dollyToward(origin, factor, paneAnchor(at, d, pane), pane, d.limits?.());
|
|
1578
|
+
});
|
|
1558
1579
|
return {
|
|
1559
1580
|
kind: "pinch",
|
|
1560
1581
|
onMove(move) {
|
|
1561
1582
|
const spread = move.multiTouch?.pinch?.currentSpread ?? 0;
|
|
1562
|
-
if (!
|
|
1563
|
-
|
|
1564
|
-
dep.set(dollyToward(origin, startSpread / spread, paneAnchor(at, dep, pane), pane, dep.limits?.()));
|
|
1583
|
+
if (!(startSpread > 0) || !(spread > 0)) return;
|
|
1584
|
+
apply(startSpread / spread);
|
|
1565
1585
|
}
|
|
1566
1586
|
};
|
|
1567
1587
|
}
|