@weasel-js/kernel3d 1.4.4 → 1.5.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/README.md +19 -5
- package/dist/index.d.ts +9 -4
- package/dist/index.js +115 -18
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -31,14 +31,28 @@ numbers: a 3D host passes the dispatcher an identity `clientToWorld`, so
|
|
|
31
31
|
`ctx.world` is the screen point, and each dep rebuilds the ray itself. Tools,
|
|
32
32
|
the select tool included, transfer untouched.
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
One thing does not, and the kernel says so rather than guessing:
|
|
35
35
|
|
|
36
36
|
- **`ViewApi` has no orientation**, so an orbit camera cannot travel through
|
|
37
37
|
the kit's `view`. The kernel declares a `camera3d` dep of its own.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
|
|
39
|
+
## The depth a screen rectangle does not name
|
|
40
|
+
|
|
41
|
+
`PoseDescriptor` reads `Bounds` as the screen box a solid covers. `getBounds`
|
|
42
|
+
and `intersectsRect` run that way and need nothing else — they are what drive
|
|
43
|
+
selection chrome through an orbit. `remapBounds` and `fromBounds` run the other
|
|
44
|
+
way, and a rectangle on screen names a pose only once something says how far
|
|
45
|
+
away it is.
|
|
46
|
+
|
|
47
|
+
**The answer is "as far as it already was."** Both resolve the rectangle on the
|
|
48
|
+
plane through the pose they were handed, facing the camera, so neither changes
|
|
49
|
+
depth. Two consequences worth knowing before you build on them:
|
|
50
|
+
|
|
51
|
+
- A resize scales uniformly. The rectangle names two extents and a pose has
|
|
52
|
+
three, so the factor is the geometric mean of the two it names.
|
|
53
|
+
- `fromBounds` returns a world-axis-aligned, unrotated box whose third extent
|
|
54
|
+
is the mean of the two the rectangle gives it, and it reads `scale` as world
|
|
55
|
+
extents — the unit-primitive assumption `aabbOfPose` makes by default.
|
|
42
56
|
|
|
43
57
|
## The bounds seam
|
|
44
58
|
|
package/dist/index.d.ts
CHANGED
|
@@ -104,11 +104,14 @@ declare function ndcToScreen(ndc: {
|
|
|
104
104
|
* The ray a screen point names, given the camera that drew the rect. This is
|
|
105
105
|
* the whole of what 3D picking needs from a pointer — which is why the world
|
|
106
106
|
* point the dispatcher carries stays two numbers.
|
|
107
|
+
*
|
|
108
|
+
* `null` when the point names no ray: the view-projection has no inverse (a
|
|
109
|
+
* camera on its own target, a degenerate projection) or the pane has no area.
|
|
107
110
|
*/
|
|
108
111
|
declare function rayThroughScreenPoint(point: {
|
|
109
112
|
x: number;
|
|
110
113
|
y: number;
|
|
111
|
-
}, rect: ViewportRect, viewProjection: Mat4, eye: Vec3): Ray;
|
|
114
|
+
}, rect: ViewportRect, viewProjection: Mat4, eye: Vec3): Ray | null;
|
|
112
115
|
/**
|
|
113
116
|
* The screen rectangle a world-space box covers — the currency weasel's chrome
|
|
114
117
|
* and `PoseDescriptor.getBounds` speak in. `null` when the box is entirely
|
|
@@ -220,9 +223,11 @@ declare function createInsert(opts: {
|
|
|
220
223
|
* which one this is. Unspecialized — the kit calling with only a pose in hand —
|
|
221
224
|
* it answers with the default local box, which is the looser of the two.
|
|
222
225
|
*
|
|
223
|
-
* The
|
|
224
|
-
* rectangle
|
|
225
|
-
*
|
|
226
|
+
* The two methods that run the other way — `remapBounds` and `fromBounds` —
|
|
227
|
+
* take a screen rectangle, which names a pose only once something supplies a
|
|
228
|
+
* depth. Both resolve it against the plane through the pose they were handed,
|
|
229
|
+
* facing the camera, so nothing they produce changes depth. See
|
|
230
|
+
* {@link viewPlaneAt}.
|
|
226
231
|
*/
|
|
227
232
|
declare function createPoseDescriptor<TData, TLayer extends string>(world: World3d<TData, TLayer>): PoseDescriptor<Pose3, Node3d<TData, TLayer>>;
|
|
228
233
|
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { lookAt, multiply, perspective, quatIdentity, compose, transformAabb, invert, transformPoint, normalize, sub, transformPoint4, intersectRayAabb, intersectRayPlane } from '@weasel-js/geom/3d';
|
|
1
|
+
import { lookAt, multiply, perspective, quatIdentity, compose, transformAabb, invert, transformPoint, normalize, sub, transformPoint4, intersectRayAabb, intersectRayPlane, len, add, scale, dot } from '@weasel-js/geom/3d';
|
|
2
2
|
import { resolveOverlays, defineTool } from '@weasel-js/core';
|
|
3
3
|
|
|
4
4
|
// src/camera.ts
|
|
@@ -55,7 +55,6 @@ function poseMatrix(pose) {
|
|
|
55
55
|
function aabbOfPose(pose, local = UNIT_CUBE) {
|
|
56
56
|
return transformAabb(poseMatrix(pose), local);
|
|
57
57
|
}
|
|
58
|
-
var EPS = 1e-9;
|
|
59
58
|
function screenToNdc(point, rect) {
|
|
60
59
|
return {
|
|
61
60
|
x: (point.x - rect.x) / rect.w * 2 - 1,
|
|
@@ -70,11 +69,13 @@ function ndcToScreen(ndc, rect) {
|
|
|
70
69
|
}
|
|
71
70
|
function rayThroughScreenPoint(point, rect, viewProjection, eye) {
|
|
72
71
|
const inv = invert(viewProjection);
|
|
73
|
-
if (!inv) return
|
|
72
|
+
if (!inv) return null;
|
|
74
73
|
const ndc = screenToNdc(point, rect);
|
|
75
74
|
const near = transformPoint(inv, [ndc.x, ndc.y, -1]);
|
|
76
75
|
const far = transformPoint(inv, [ndc.x, ndc.y, 1]);
|
|
77
|
-
|
|
76
|
+
const direction = normalize(sub(far, near));
|
|
77
|
+
if (direction[0] === 0 && direction[1] === 0 && direction[2] === 0) return null;
|
|
78
|
+
return { origin: eye, direction };
|
|
78
79
|
}
|
|
79
80
|
function nearSide(p) {
|
|
80
81
|
return p[2] + p[3];
|
|
@@ -104,7 +105,7 @@ function projectAabbToScreen(box, viewProjection, rect) {
|
|
|
104
105
|
let maxY = Number.NEGATIVE_INFINITY;
|
|
105
106
|
let any = false;
|
|
106
107
|
const extend = (p) => {
|
|
107
|
-
if (p[3]
|
|
108
|
+
if (!(p[3] > 0)) return;
|
|
108
109
|
const screen = ndcToScreen({ x: p[0] / p[3], y: p[1] / p[3] }, rect);
|
|
109
110
|
minX = Math.min(minX, screen.x);
|
|
110
111
|
minY = Math.min(minY, screen.y);
|
|
@@ -152,6 +153,27 @@ function rayAt(viewport, point) {
|
|
|
152
153
|
cameraEye(viewport.camera)
|
|
153
154
|
);
|
|
154
155
|
}
|
|
156
|
+
function viewPlaneAt(camera, p) {
|
|
157
|
+
const normal = normalize(sub(camera.target, cameraEye(camera)));
|
|
158
|
+
return { normal, offset: dot(normal, p) };
|
|
159
|
+
}
|
|
160
|
+
function onPlane(vp, plane, point) {
|
|
161
|
+
const ray = rayAt(vp, point);
|
|
162
|
+
if (!ray) return null;
|
|
163
|
+
const t = intersectRayPlane(ray, plane.normal, plane.offset);
|
|
164
|
+
return t === null ? null : pointOnRay(ray, t);
|
|
165
|
+
}
|
|
166
|
+
function aabbOfPoints(points) {
|
|
167
|
+
const min = [Infinity, Infinity, Infinity];
|
|
168
|
+
const max = [-Infinity, -Infinity, -Infinity];
|
|
169
|
+
for (const p of points) {
|
|
170
|
+
for (let i = 0; i < 3; i++) {
|
|
171
|
+
if (p[i] < min[i]) min[i] = p[i];
|
|
172
|
+
if (p[i] > max[i]) max[i] = p[i];
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return { min, max };
|
|
176
|
+
}
|
|
155
177
|
function pointOnRay(ray, t) {
|
|
156
178
|
return [
|
|
157
179
|
ray.origin[0] + ray.direction[0] * t,
|
|
@@ -176,6 +198,7 @@ function createNodeAtPoint(world) {
|
|
|
176
198
|
const excluded = exclude ? new Set(exclude) : null;
|
|
177
199
|
const vp = world.viewport();
|
|
178
200
|
const ray = rayAt(vp, point);
|
|
201
|
+
if (!ray) return null;
|
|
179
202
|
let best = null;
|
|
180
203
|
let bestT = Infinity;
|
|
181
204
|
for (const node of world.scene.renderOrderNodes()) {
|
|
@@ -224,6 +247,7 @@ function createInsert(opts) {
|
|
|
224
247
|
const hits = [];
|
|
225
248
|
for (const corner of corners) {
|
|
226
249
|
const ray = rayAt(vp, corner);
|
|
250
|
+
if (!ray) continue;
|
|
227
251
|
const t = intersectRayPlane(ray, up, 0);
|
|
228
252
|
if (t !== null) hits.push(pointOnRay(ray, t));
|
|
229
253
|
}
|
|
@@ -254,11 +278,16 @@ function createPoseDescriptor(world) {
|
|
|
254
278
|
const up = world.up ?? DEFAULT_UP;
|
|
255
279
|
const upAxis = up[1] !== 0 ? 1 : up[0] !== 0 ? 0 : 2;
|
|
256
280
|
const specialized = /* @__PURE__ */ new WeakMap();
|
|
281
|
+
let lastCastable = null;
|
|
282
|
+
function castableViewport() {
|
|
283
|
+
const vp = world.viewport();
|
|
284
|
+
const rect = rectOf(vp);
|
|
285
|
+
if (rayAt(vp, { x: rect.x + rect.w / 2, y: rect.y + rect.h / 2 })) lastCastable = vp;
|
|
286
|
+
return lastCastable;
|
|
287
|
+
}
|
|
257
288
|
function build(worldBox) {
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
return projectAabbToScreen(worldBox(pose), viewProjectionOf(vp), rectOf(vp));
|
|
261
|
-
};
|
|
289
|
+
const boundsIn = (vp, pose) => projectAabbToScreen(worldBox(pose), viewProjectionOf(vp), rectOf(vp));
|
|
290
|
+
const boundsOf = (pose) => boundsIn(world.viewport(), pose);
|
|
262
291
|
const descriptor = {
|
|
263
292
|
forNode(node) {
|
|
264
293
|
let d = specialized.get(node);
|
|
@@ -271,15 +300,80 @@ function createPoseDescriptor(world) {
|
|
|
271
300
|
getBounds(pose) {
|
|
272
301
|
return boundsOf(pose) ?? { x: 0, y: 0, width: 0, height: 0 };
|
|
273
302
|
},
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
303
|
+
/**
|
|
304
|
+
* The src→dst screen map, applied at the pose's own depth: the node's
|
|
305
|
+
* screen anchor moves the way the rectangle does, and both ends of that
|
|
306
|
+
* move are read back off the plane through its position, so the world
|
|
307
|
+
* delta is exact rather than a scaled screen one.
|
|
308
|
+
*
|
|
309
|
+
* The scale is uniform. The rectangle names two extents and a pose has
|
|
310
|
+
* three, so there is no reading in which a screen box resizes a solid
|
|
311
|
+
* per-axis; the geometric mean is the one factor that agrees with both
|
|
312
|
+
* of the extents it does name.
|
|
313
|
+
*/
|
|
314
|
+
remapBounds(pose, src, dst) {
|
|
315
|
+
const vp = castableViewport();
|
|
316
|
+
if (!vp) return pose;
|
|
317
|
+
const box = boundsIn(vp, pose);
|
|
318
|
+
if (!box) return pose;
|
|
319
|
+
const kx = src.width === 0 ? 1 : dst.width / src.width;
|
|
320
|
+
const ky = src.height === 0 ? 1 : dst.height / src.height;
|
|
321
|
+
const from = { x: box.x + box.width / 2, y: box.y + box.height / 2 };
|
|
322
|
+
const to = {
|
|
323
|
+
x: dst.x + (from.x - src.x) * kx,
|
|
324
|
+
y: dst.y + (from.y - src.y) * ky
|
|
325
|
+
};
|
|
326
|
+
const plane = viewPlaneAt(vp.camera, pose.position);
|
|
327
|
+
const before = onPlane(vp, plane, from);
|
|
328
|
+
const after = onPlane(vp, plane, to);
|
|
329
|
+
if (!before || !after) return pose;
|
|
330
|
+
const k = Math.sqrt(Math.abs(kx * ky));
|
|
331
|
+
return {
|
|
332
|
+
...pose,
|
|
333
|
+
position: add(pose.position, sub(after, before)),
|
|
334
|
+
scale: scale(pose.scale, k)
|
|
335
|
+
};
|
|
278
336
|
},
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
337
|
+
/**
|
|
338
|
+
* The world box the rectangle covers at the template's depth, as a pose.
|
|
339
|
+
*
|
|
340
|
+
* The rectangle is flat and a box is not, so the third extent — the one
|
|
341
|
+
* no screen rectangle names — is the mean of the two it does. The result
|
|
342
|
+
* is world-axis-aligned and unrotated: the pose carries none of the
|
|
343
|
+
* template's shape, which is the contract, and it takes only its depth.
|
|
344
|
+
*
|
|
345
|
+
* Extents are written as `scale`, so this reads a pose as carrying a
|
|
346
|
+
* unit primitive — the same assumption `aabbOfPose` makes by default.
|
|
347
|
+
*/
|
|
348
|
+
fromBounds(bounds, template) {
|
|
349
|
+
const vp = castableViewport();
|
|
350
|
+
if (!vp) return template;
|
|
351
|
+
const plane = viewPlaneAt(vp.camera, template.position);
|
|
352
|
+
const corners = [];
|
|
353
|
+
for (const [x, y] of [
|
|
354
|
+
[bounds.x, bounds.y],
|
|
355
|
+
[bounds.x + bounds.width, bounds.y],
|
|
356
|
+
[bounds.x, bounds.y + bounds.height],
|
|
357
|
+
[bounds.x + bounds.width, bounds.y + bounds.height]
|
|
358
|
+
]) {
|
|
359
|
+
const corner = onPlane(vp, plane, { x, y });
|
|
360
|
+
if (!corner) return template;
|
|
361
|
+
corners.push(corner);
|
|
362
|
+
}
|
|
363
|
+
const width = len(sub(corners[1], corners[0]));
|
|
364
|
+
const height = len(sub(corners[2], corners[0]));
|
|
365
|
+
const half = Math.sqrt(width * height) / 2;
|
|
366
|
+
const box = aabbOfPoints(
|
|
367
|
+
corners.flatMap((c) => [
|
|
368
|
+
add(c, scale(plane.normal, half)),
|
|
369
|
+
sub(c, scale(plane.normal, half))
|
|
370
|
+
])
|
|
282
371
|
);
|
|
372
|
+
return {
|
|
373
|
+
position: scale(add(box.min, box.max), 0.5),
|
|
374
|
+
rotation: quatIdentity(),
|
|
375
|
+
scale: sub(box.max, box.min)
|
|
376
|
+
};
|
|
283
377
|
},
|
|
284
378
|
/**
|
|
285
379
|
* Screen-space deltas resolve against the plane the node already sits on,
|
|
@@ -287,16 +381,19 @@ function createPoseDescriptor(world) {
|
|
|
287
381
|
* them into a world position is the one this dep closed over.
|
|
288
382
|
*/
|
|
289
383
|
translate(pose, dx, dy) {
|
|
290
|
-
const vp =
|
|
291
|
-
|
|
384
|
+
const vp = castableViewport();
|
|
385
|
+
if (!vp) return pose;
|
|
386
|
+
const box = boundsIn(vp, pose);
|
|
292
387
|
if (!box) return pose;
|
|
293
388
|
const from = { x: box.x + box.width / 2, y: box.y + box.height / 2 };
|
|
294
389
|
const to = { x: from.x + dx, y: from.y + dy };
|
|
295
390
|
const offset = pose.position[upAxis];
|
|
296
391
|
const ray = rayAt(vp, to);
|
|
392
|
+
if (!ray) return pose;
|
|
297
393
|
const t = intersectRayPlane(ray, up, offset);
|
|
298
394
|
if (t === null) return pose;
|
|
299
395
|
const anchorRay = rayAt(vp, from);
|
|
396
|
+
if (!anchorRay) return pose;
|
|
300
397
|
const anchorT = intersectRayPlane(anchorRay, up, offset);
|
|
301
398
|
if (anchorT === null) return pose;
|
|
302
399
|
const delta = sub(pointOnRay(ray, t), pointOnRay(anchorRay, anchorT));
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/camera.ts","../src/pose3.ts","../src/screen.ts","../src/deps.ts","../src/overlays.ts","../src/tools.ts"],"names":["sub"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * An orbit camera: a target, two angles and a distance. Immutable — every\n * gesture returns a new one, so it can live in instrument state.\n *\n * This is the shape `ViewApi` cannot hold, and the reason the kernel declares a\n * `camera3d` dep of its own rather than reusing the kit's `view`.\n */\n\nimport {\n lookAt,\n multiply,\n perspective,\n type Mat4,\n type Vec3,\n} from '@weasel-js/geom/3d';\n\nexport interface Camera3d {\n target: Vec3;\n /** Radians about +y, measured from +z. */\n yaw: number;\n /** Radians above the xz plane. */\n pitch: number;\n distance: number;\n fov: number;\n near: number;\n far: number;\n}\n\n/** Short of vertical: at exactly ±π/2 the eye lands on the up axis and `lookAt` degenerates. */\nexport const MAX_PITCH = Math.PI / 2 - 0.01;\nconst MIN_DISTANCE = 0.5;\nconst MAX_DISTANCE = 500;\nconst UP: Vec3 = [0, 1, 0];\n\nexport function createCamera(init: Partial<Camera3d>): Camera3d {\n return {\n target: init.target ?? [0, 0, 0],\n yaw: init.yaw ?? 0,\n pitch: init.pitch ?? 0,\n distance: init.distance ?? 10,\n fov: init.fov ?? Math.PI / 4,\n near: init.near ?? 0.1,\n far: init.far ?? 1000,\n };\n}\n\nexport function cameraEye(camera: Camera3d): Vec3 {\n const { target, yaw, pitch, distance } = camera;\n const horizontal = Math.cos(pitch) * distance;\n return [\n target[0] + horizontal * Math.sin(yaw),\n target[1] + Math.sin(pitch) * distance,\n target[2] + horizontal * Math.cos(yaw),\n ];\n}\n\nexport function cameraView(camera: Camera3d): Mat4 {\n return lookAt(cameraEye(camera), camera.target, UP);\n}\n\nexport function cameraViewProjection(camera: Camera3d, aspect: number): Mat4 {\n return multiply(perspective(camera.fov, aspect, camera.near, camera.far), cameraView(camera));\n}\n\nexport function orbitBy(camera: Camera3d, deltaYaw: number, deltaPitch: number): Camera3d {\n return {\n ...camera,\n yaw: camera.yaw + deltaYaw,\n pitch: Math.max(-MAX_PITCH, Math.min(MAX_PITCH, camera.pitch + deltaPitch)),\n };\n}\n\nexport function dollyBy(camera: Camera3d, factor: number): Camera3d {\n return {\n ...camera,\n distance: Math.max(MIN_DISTANCE, Math.min(MAX_DISTANCE, camera.distance * factor)),\n };\n}\n","/**\n * A 3D pose, and the two things every consumer of one needs: its matrix, and\n * the world box it occupies.\n *\n * `Scene<TData, TLayer, Pose3>` is the whole of how this reaches weasel —\n * `Scene` is generic over its pose and holds this one with no adapter, no\n * subclass and no special case.\n */\n\nimport {\n compose, quatIdentity, transformAabb, type Aabb, type Mat4, type Quat, type Vec3,\n} from '@weasel-js/geom/3d';\n\nexport interface Pose3 {\n position: Vec3;\n rotation: Quat;\n scale: Vec3;\n}\n\n/** The box a primitive occupies before its pose. A unit primitive centred on\n * its own origin — which is what the kernel assumes nothing about — is\n * `UNIT_CUBE`. */\nexport const UNIT_CUBE: Aabb = { min: [-0.5, -0.5, -0.5], max: [0.5, 0.5, 0.5] };\n\nexport function pose3(\n position: Vec3,\n scale: Vec3 = [1, 1, 1],\n rotation: Quat = quatIdentity(),\n): Pose3 {\n return { position, rotation, scale };\n}\n\nexport function poseMatrix(pose: Pose3): Mat4 {\n return compose(pose.position, pose.rotation, pose.scale);\n}\n\n/**\n * The world box a posed primitive occupies, given the box it occupies in its\n * own space.\n *\n * Which local box a primitive has is the consumer's answer, not the kernel's:\n * a sphere's is rotation-invariant and a mesh's comes from its vertices, and\n * neither is something a pose can tell you.\n */\nexport function aabbOfPose(pose: Pose3, local: Aabb = UNIT_CUBE): Aabb {\n return transformAabb(poseMatrix(pose), local);\n}\n","/**\n * The screen side of the kernel: where a pointer points, and what rectangle a\n * world box covers. This is the seam weasel's 2D chrome meets — everything\n * here speaks `Bounds`-shaped screen rectangles, which is why a 3D host can\n * pass the dispatcher's world point through unchanged.\n */\n\nimport {\n invert,\n normalize,\n sub,\n transformPoint,\n transformPoint4,\n type Aabb,\n type Mat4,\n type Ray,\n type Vec3,\n} from '@weasel-js/geom/3d';\n\n/** A pane's rectangle in CSS pixels, relative to the client. */\nexport interface ViewportRect {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\n/** A screen rectangle in the shape core's `Bounds` uses. */\nexport interface ScreenBox {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/** A `ScreenBox` a host is being asked to outline. `tint` says why: warm for\n * what is selected, cool for what the gesture in hand is proposing. */\nexport interface ChromeBox extends ScreenBox {\n tint?: 'selection' | 'gesture';\n}\n\nconst EPS = 1e-9;\n\n/** Screen point (y down, rect-relative) to normalized device coordinates (y up). */\nexport function screenToNdc(\n point: { x: number; y: number },\n rect: ViewportRect,\n): { x: number; y: number } {\n return {\n x: ((point.x - rect.x) / rect.w) * 2 - 1,\n y: 1 - ((point.y - rect.y) / rect.h) * 2,\n };\n}\n\nexport function ndcToScreen(\n ndc: { x: number; y: number },\n rect: ViewportRect,\n): { x: number; y: number } {\n return {\n x: rect.x + (ndc.x * 0.5 + 0.5) * rect.w,\n y: rect.y + (1 - (ndc.y * 0.5 + 0.5)) * rect.h,\n };\n}\n\n/**\n * The ray a screen point names, given the camera that drew the rect. This is\n * the whole of what 3D picking needs from a pointer — which is why the world\n * point the dispatcher carries stays two numbers.\n */\nexport function rayThroughScreenPoint(\n point: { x: number; y: number },\n rect: ViewportRect,\n viewProjection: Mat4,\n eye: Vec3,\n): Ray {\n const inv = invert(viewProjection);\n if (!inv) return { origin: eye, direction: [0, 0, -1] };\n const ndc = screenToNdc(point, rect);\n const near = transformPoint(inv, [ndc.x, ndc.y, -1]);\n const far = transformPoint(inv, [ndc.x, ndc.y, 1]);\n return { origin: eye, direction: normalize(sub(far, near)) };\n}\n\ntype Clip = readonly [number, number, number, number];\n\n/** Signed distance to the near plane in clip space; `>= 0` is in front of it.\n * GL puts the near plane at `z = -w`. */\nfunction nearSide(p: Clip): number {\n return p[2] + p[3];\n}\n\nfunction lerpClip(a: Clip, b: Clip, t: number): Clip {\n return [\n a[0] + (b[0] - a[0]) * t,\n a[1] + (b[1] - a[1]) * t,\n a[2] + (b[2] - a[2]) * t,\n a[3] + (b[3] - a[3]) * t,\n ];\n}\n\n/** The three edges leaving corner `i` toward a higher bit, as bit masks. */\nconst EDGE_BITS = [1, 2, 4] as const;\n\n/**\n * The screen rectangle a world-space box covers — the currency weasel's chrome\n * and `PoseDescriptor.getBounds` speak in. `null` when the box is entirely\n * behind the near plane.\n *\n * Each of the twelve edges is clipped against the near plane before it is\n * projected, rather than dropping the corners behind it. Dropping them reports\n * a box that is too small for anything straddling the near plane, and the error\n * grows as the camera moves in: a solid the pointer is inside of would outline\n * as a sliver, or as nothing.\n */\nexport function projectAabbToScreen(\n box: Aabb,\n viewProjection: Mat4,\n rect: ViewportRect,\n): ScreenBox | null {\n const corners: Clip[] = [];\n for (let i = 0; i < 8; i++) {\n const corner: Vec3 = [\n i & 1 ? box.max[0] : box.min[0],\n i & 2 ? box.max[1] : box.min[1],\n i & 4 ? box.max[2] : box.min[2],\n ];\n corners.push(transformPoint4(viewProjection, corner));\n }\n\n let minX = Number.POSITIVE_INFINITY;\n let minY = Number.POSITIVE_INFINITY;\n let maxX = Number.NEGATIVE_INFINITY;\n let maxY = Number.NEGATIVE_INFINITY;\n let any = false;\n\n const extend = (p: Clip): void => {\n if (p[3] <= EPS) return;\n const screen = ndcToScreen({ x: p[0] / p[3], y: p[1] / p[3] }, rect);\n minX = Math.min(minX, screen.x);\n minY = Math.min(minY, screen.y);\n maxX = Math.max(maxX, screen.x);\n maxY = Math.max(maxY, screen.y);\n any = true;\n };\n\n for (let i = 0; i < 8; i++) {\n for (const bit of EDGE_BITS) {\n if (i & bit) continue;\n let a = corners[i];\n let b = corners[i | bit];\n const da = nearSide(a);\n const db = nearSide(b);\n if (da < 0 && db < 0) continue;\n if (da < 0) a = lerpClip(a, b, da / (da - db));\n else if (db < 0) b = lerpClip(b, a, db / (db - da));\n extend(a);\n extend(b);\n }\n }\n\n if (!any) return null;\n return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };\n}\n","/**\n * The kit's deps, implemented against a 3D world.\n *\n * Every one of these takes its camera from a thunk rather than an argument.\n * That is the kernel's central claim in code: the dispatcher keeps handing\n * actions two numbers, and what makes them mean something in 3D is the camera\n * each dep already closes over. `InvocationCtx` needs no widening, and none of\n * these needs a ray passed in.\n *\n * Where a kit contract cannot be honored, the implementation throws rather than\n * guessing. A guess would report a fit the kernel does not have.\n */\n\nimport type {\n AreaSelectDep,\n InsertDep,\n InsertExtras,\n NodeAtPointDep,\n NodeId,\n PoseDescriptor,\n Scene,\n SceneNode,\n SnapDep,\n} from '@weasel-js/core';\nimport {\n intersectRayAabb,\n intersectRayPlane,\n sub,\n type Aabb,\n type Ray,\n type Vec3,\n} from '@weasel-js/geom/3d';\nimport { cameraEye, cameraViewProjection, type Camera3d } from './camera';\nimport { aabbOfPose, type Pose3 } from './pose3';\nimport {\n projectAabbToScreen,\n rayThroughScreenPoint,\n type ScreenBox,\n type ViewportRect,\n} from './screen';\n\nexport interface Viewport3d {\n camera: Camera3d;\n width: number;\n height: number;\n /**\n * Where the pane sits in client coordinates. A 3D host works in one space\n * from the event to the ray — it passes the dispatcher an identity\n * `clientToWorld` — so the deps subtract this rather than the dispatcher.\n */\n originX?: number;\n originY?: number;\n}\n\nexport type ViewportSource = () => Viewport3d;\n\n/** A 3D scene is core's scene with a `Pose3`. Nothing wraps it. */\nexport type Scene3d<TData, TLayer extends string> = Scene<TData, TLayer, Pose3>;\nexport type Node3d<TData, TLayer extends string> = SceneNode<TData, TLayer, Pose3>;\n\n/**\n * The world box a node occupies. The kernel cannot answer this, and the reason\n * is sharper than \"it does not know the mesh\": a sphere's box is the same under\n * every rotation, and no transform of a local box reproduces that — rotate a\n * cube and it widens. Asking for the world box directly is what lets a\n * primitive answer for its own symmetry.\n *\n * Omitted, a node is a unit cube carried by its pose.\n */\nexport type NodeBounds<TData, TLayer extends string> = (\n node: Node3d<TData, TLayer>,\n) => Aabb;\n\nexport interface World3d<TData, TLayer extends string> {\n scene: Scene3d<TData, TLayer>;\n viewport: ViewportSource;\n bounds?: NodeBounds<TData, TLayer>;\n /** The axis gestures resolve against. Default `+y`. */\n up?: Vec3;\n}\n\nconst DEFAULT_UP: Vec3 = [0, 1, 0];\n\nfunction boundsOfNode<TData, TLayer extends string>(\n world: World3d<TData, TLayer>,\n node: Node3d<TData, TLayer>,\n): Aabb {\n return world.bounds ? world.bounds(node) : aabbOfPose(node.pose);\n}\n\n/** The pane, in the coordinate space the host's points arrive in. */\nfunction rectOf(viewport: Viewport3d): ViewportRect {\n return {\n x: viewport.originX ?? 0,\n y: viewport.originY ?? 0,\n w: viewport.width,\n h: viewport.height,\n };\n}\n\nfunction viewProjectionOf(viewport: Viewport3d) {\n return cameraViewProjection(viewport.camera, viewport.width / Math.max(1, viewport.height));\n}\n\nfunction rayAt(viewport: Viewport3d, point: { x: number; y: number }): Ray {\n return rayThroughScreenPoint(\n point,\n rectOf(viewport),\n viewProjectionOf(viewport),\n cameraEye(viewport.camera),\n );\n}\n\nfunction pointOnRay(ray: Ray, t: number): Vec3 {\n return [\n ray.origin[0] + ray.direction[0] * t,\n ray.origin[1] + ray.direction[1] * t,\n ray.origin[2] + ray.direction[2] * t,\n ];\n}\n\nfunction overlaps(a: ScreenBox, b: ScreenBox): boolean {\n return (\n a.x < b.x + b.width && a.x + a.width > b.x && a.y < b.y + b.height && a.y + a.height > b.y\n );\n}\n\n/** The screen box a node covers, or null when it projects to nothing visible. */\nexport function screenBoxOf<TData, TLayer extends string>(\n world: World3d<TData, TLayer>,\n id: NodeId,\n viewport: Viewport3d,\n): ScreenBox | null {\n const node = world.scene.get(id) as Node3d<TData, TLayer> | undefined;\n if (!node) return null;\n return projectAabbToScreen(\n boundsOfNode(world, node),\n viewProjectionOf(viewport),\n rectOf(viewport),\n );\n}\n\n/**\n * Nearest node along the ray the screen point names.\n *\n * The kit calls this with what it believes is a world point; a 3D host feeds\n * the dispatcher an identity `clientToWorld`, so it is the pane-relative screen\n * point, which is exactly what a ray needs.\n */\nexport function createNodeAtPoint<TData, TLayer extends string>(\n world: World3d<TData, TLayer>,\n): NodeAtPointDep {\n return (point, exclude) => {\n const excluded = exclude ? new Set(exclude) : null;\n const vp = world.viewport();\n const ray = rayAt(vp, point);\n let best: NodeId | null = null;\n let bestT = Infinity;\n for (const node of world.scene.renderOrderNodes() as readonly Node3d<TData, TLayer>[]) {\n if (excluded?.has(node.id)) continue;\n const { min, max } = boundsOfNode(world, node);\n const t = intersectRayAabb(ray, min, max);\n if (t !== null && t < bestT) {\n bestT = t;\n best = node.id;\n }\n }\n return best;\n };\n}\n\n/** Marquee: the nodes whose projected box the screen rectangle touches. */\nexport function createAreaSelect<TData, TLayer extends string>(\n world: World3d<TData, TLayer>,\n selection: { get(): readonly NodeId[]; set(ids: NodeId[]): void },\n): AreaSelectDep {\n return {\n hitTestArea(bounds) {\n const vp = world.viewport();\n const hits: NodeId[] = [];\n for (const node of world.scene.renderOrderNodes()) {\n const box = screenBoxOf(world, node.id, vp);\n if (box && overlaps(box, bounds)) hits.push(node.id);\n }\n return hits;\n },\n getSelection: () => [...selection.get()],\n setSelection: (ids) => selection.set([...ids]),\n };\n}\n\n/** No grid in 3D yet; the identity keeps `insertAction` happy without lying. */\nexport function createSnap(): SnapDep {\n return { point: (p) => p };\n}\n\n/** The ground-plane rectangle a drag swept out, in world units. */\nexport interface Footprint {\n /** Centre of the swept rectangle, on the ground plane. */\n center: Vec3;\n /** Extent along the two axes the up axis is not. */\n width: number;\n depth: number;\n}\n\n/**\n * A drag rectangle becomes a footprint on the ground plane: its four corners\n * are cast onto the plane and the hit points give the extent. What node that\n * implies is the consumer's answer — the kernel does not know what it is\n * inserting.\n *\n * The kit's `InsertDep.commit` hands over a screen-space AABB and nothing else,\n * which turns out to be enough: the depth the 2D contract cannot express comes\n * from the plane, not from the caller.\n */\nexport function createInsert(opts: {\n viewport: ViewportSource;\n up?: Vec3;\n /** Smallest footprint a click-sized drag produces. Default 0.25. */\n minExtent?: number;\n mint(footprint: Footprint, extras: InsertExtras): NodeId | null;\n}): InsertDep {\n const up = opts.up ?? DEFAULT_UP;\n const minExtent = opts.minExtent ?? 0.25;\n // The two axes the up axis is not, so a footprint reads the same whichever\n // way up the world is.\n const axes: [number, number] = up[1] !== 0 ? [0, 2] : up[0] !== 0 ? [1, 2] : [0, 1];\n\n return {\n commit(bounds, extras) {\n const vp = opts.viewport();\n const corners = [\n { x: bounds.x, y: bounds.y },\n { x: bounds.x + bounds.width, y: bounds.y },\n { x: bounds.x, y: bounds.y + bounds.height },\n { x: bounds.x + bounds.width, y: bounds.y + bounds.height },\n ];\n\n const hits: Vec3[] = [];\n for (const corner of corners) {\n const ray = rayAt(vp, corner);\n const t = intersectRayPlane(ray, up, 0);\n if (t !== null) hits.push(pointOnRay(ray, t));\n }\n if (hits.length === 0) return null;\n\n const [u, v] = axes;\n let minU = Infinity, maxU = -Infinity, minV = Infinity, maxV = -Infinity;\n for (const h of hits) {\n minU = Math.min(minU, h[u]); maxU = Math.max(maxU, h[u]);\n minV = Math.min(minV, h[v]); maxV = Math.max(maxV, h[v]);\n }\n\n const center: [number, number, number] = [0, 0, 0];\n center[u] = (minU + maxU) / 2;\n center[v] = (minV + maxV) / 2;\n return opts.mint(\n {\n center,\n width: Math.max(minExtent, maxU - minU),\n depth: Math.max(minExtent, maxV - minV),\n },\n extras,\n );\n },\n };\n}\n\n/**\n * `PoseDescriptor` over a 3D pose, with `Bounds` read as the screen box the\n * node covers. Chrome and marquee math work unchanged; the two methods that\n * run the other way cannot.\n *\n * `forNode` is what lets a sphere bound itself as a sphere: every node is a\n * `Pose3`, so the shape lives on `node.data` and nothing but the node can say\n * which one this is. Unspecialized — the kit calling with only a pose in hand —\n * it answers with the default local box, which is the looser of the two.\n *\n * The contract mismatch that remains, recorded in the kernel doc: a screen\n * rectangle does not name a 3D pose without a depth, so `remapBounds` and\n * `fromBounds` have no honest answer.\n */\nexport function createPoseDescriptor<TData, TLayer extends string>(\n world: World3d<TData, TLayer>,\n): PoseDescriptor<Pose3, Node3d<TData, TLayer>> {\n const up = world.up ?? DEFAULT_UP;\n const upAxis = up[1] !== 0 ? 1 : up[0] !== 0 ? 0 : 2;\n const specialized = new WeakMap<object, PoseDescriptor<Pose3, Node3d<TData, TLayer>>>();\n\n function build(worldBox: (pose: Pose3) => Aabb): PoseDescriptor<Pose3, Node3d<TData, TLayer>> {\n const boundsOf = (pose: Pose3) => {\n const vp = world.viewport();\n return projectAabbToScreen(worldBox(pose), viewProjectionOf(vp), rectOf(vp));\n };\n\n const descriptor: PoseDescriptor<Pose3, Node3d<TData, TLayer>> = {\n forNode(node) {\n let d = specialized.get(node);\n if (!d) {\n // The pose arriving here is often an in-flight override rather than\n // `node.pose`, so the consumer is asked about that pose on this node\n // rather than about the node as committed.\n d = build((pose) => boundsOfNode(world, { ...node, pose }));\n specialized.set(node, d);\n }\n return d;\n },\n\n getBounds(pose) {\n return boundsOf(pose) ?? { x: 0, y: 0, width: 0, height: 0 };\n },\n\n remapBounds() {\n throw new Error(\n 'kernel3d: remapBounds has no 3D answer — a screen rectangle does not name a pose without a depth.',\n );\n },\n\n fromBounds() {\n throw new Error(\n 'kernel3d: fromBounds has no 3D answer — a screen rectangle does not name a pose without a depth.',\n );\n },\n\n /**\n * Screen-space deltas resolve against the plane the node already sits on,\n * so the two numbers the kit passes are enough: the camera that turns\n * them into a world position is the one this dep closed over.\n */\n translate(pose, dx, dy) {\n const vp = world.viewport();\n const box = boundsOf(pose);\n if (!box) return pose;\n const from = { x: box.x + box.width / 2, y: box.y + box.height / 2 };\n const to = { x: from.x + dx, y: from.y + dy };\n const offset = pose.position[upAxis];\n\n const ray = rayAt(vp, to);\n const t = intersectRayPlane(ray, up, offset);\n if (t === null) return pose;\n\n const anchorRay = rayAt(vp, from);\n const anchorT = intersectRayPlane(anchorRay, up, offset);\n if (anchorT === null) return pose;\n\n const delta = sub(pointOnRay(ray, t), pointOnRay(anchorRay, anchorT));\n const position: [number, number, number] = [\n pose.position[0] + delta[0],\n pose.position[1] + delta[1],\n pose.position[2] + delta[2],\n ];\n position[upAxis] = pose.position[upAxis];\n return { ...pose, position };\n },\n\n intersectsRect(pose, rect) {\n const box = boundsOf(pose);\n return box ? overlaps(box, rect) : false;\n },\n\n supportsRotation() {\n return false;\n },\n };\n\n return descriptor;\n }\n\n return build((pose) => aabbOfPose(pose));\n}\n","/**\n * What a host paints for a gesture that proposes chrome rather than a pose.\n *\n * A marquee sweep and a box-drag both publish an `OngoingOverlay`, which used\n * to reach paint only through core's own 2D layer. `resolveOverlays` hands back\n * world geometry with no renderer in it, so the same call `<SceneCanvas>` makes\n * serves a consumer-owned 3D renderer.\n *\n * A 3D host's `clientToWorld` is identity, so an overlay's world bounds are the\n * client rect the deps already work in, and the pane origin comes off exactly\n * once. Only the rectangular variants resolve here: a lasso, a pencil and a run\n * of points are paths, and a path is geometry this cannot flatten to a box.\n */\n\nimport { resolveOverlays, type OngoingHandle } from '@weasel-js/core';\nimport type { ChromeBox } from './screen';\n\n/** Gesture chrome, in CSS pixels relative to the pane's top-left. */\nexport function collectOverlayBoxes(\n handles: Iterable<OngoingHandle>,\n origin: { x: number; y: number },\n): ChromeBox[] {\n const overlays = [];\n for (const handle of handles) {\n const ov = handle.overlay?.();\n if (ov) overlays.push(ov);\n }\n\n const boxes: ChromeBox[] = [];\n for (const resolved of resolveOverlays(overlays)) {\n if (resolved.kind !== 'marquee' && resolved.kind !== 'insertPreview') continue;\n const { x, y, width, height } = resolved.bounds;\n boxes.push({ x: x - origin.x, y: y - origin.y, width, height, tint: 'gesture' });\n }\n return boxes;\n}\n","/**\n * Camera tools and their actions.\n *\n * Shape tools transfer from the kit untouched — a 3D host mounts core's own\n * `useSelectTool` and a rectangle-style insert binding and neither notices the\n * dimension. Only the viewport tools are written here, which is the one\n * exception the kernel doc predicted: `useHandTool` routes to\n * `viewport.dragPan`, and that wants the 2D `view`.\n */\n\nimport { defineTool, type Action, type Tool } from '@weasel-js/core';\nimport { dollyBy, orbitBy, type Camera3d } from './camera';\nimport type { Camera3dDep } from './cameraDep';\n\n/** Radians per CSS pixel of drag. A full pane width is a bit over half a turn. */\nconst ORBIT_RATE = 0.006;\nconst DOLLY_RATE = 0.0015;\n\nfunction cameraDep(deps: unknown): Camera3dDep | undefined {\n return (deps as { camera3d?: Camera3dDep }).camera3d;\n}\n\nexport const orbitAction: Action = {\n id: 'camera.orbit',\n label: 'Orbit',\n requires: ['camera3d'],\n invoker: {\n timing: 'ongoing',\n start(ctx) {\n const dep = cameraDep(ctx.deps);\n const origin: Camera3d | undefined = dep?.get();\n return {\n kind: 'orbit',\n onMove(move) {\n if (!dep || !origin) return;\n // `drag.delta` runs from the drag origin, not the previous move, so\n // this composes against the camera as it was when the drag began.\n const delta = move.drag?.screenDelta ?? move.drag?.delta;\n if (!delta) return;\n dep.set(orbitBy(origin, -delta.x * ORBIT_RATE, delta.y * ORBIT_RATE));\n },\n };\n },\n },\n};\n\nexport const dollyAction: Action = {\n id: 'camera.dolly',\n label: 'Dolly',\n group: 'viewport',\n // Wheel, in any tool — the dispatcher merges the event's deltas into params.\n defaultBinding: [{ spec: { kind: 'wheel' }, opts: {} }],\n requires: ['camera3d'],\n invoker: {\n timing: 'immediate',\n run(deps, params) {\n const dep = cameraDep(deps);\n if (!dep) return;\n const amount = typeof params?.deltaY === 'number' ? params.deltaY : 0;\n dep.set(dollyBy(dep.get(), Math.exp(amount * DOLLY_RATE)));\n },\n },\n};\n\nexport function useOrbitTool(): Tool<null> {\n return defineTool<null>({\n id: 'orbit',\n hookName: 'useOrbitTool',\n cursor: 'grab',\n presentation: { label: 'Orbit', group: 'viewport' },\n bindings: [{ spec: { kind: 'drag' }, actionId: 'camera.orbit' }],\n });\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/camera.ts","../src/pose3.ts","../src/screen.ts","../src/deps.ts","../src/overlays.ts","../src/tools.ts"],"names":["normalize","sub","scaleBy","quatIdentity"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * An orbit camera: a target, two angles and a distance. Immutable — every\n * gesture returns a new one, so it can live in instrument state.\n *\n * This is the shape `ViewApi` cannot hold, and the reason the kernel declares a\n * `camera3d` dep of its own rather than reusing the kit's `view`.\n */\n\nimport {\n lookAt,\n multiply,\n perspective,\n type Mat4,\n type Vec3,\n} from '@weasel-js/geom/3d';\n\nexport interface Camera3d {\n target: Vec3;\n /** Radians about +y, measured from +z. */\n yaw: number;\n /** Radians above the xz plane. */\n pitch: number;\n distance: number;\n fov: number;\n near: number;\n far: number;\n}\n\n/** Short of vertical: at exactly ±π/2 the eye lands on the up axis and `lookAt` degenerates. */\nexport const MAX_PITCH = Math.PI / 2 - 0.01;\nconst MIN_DISTANCE = 0.5;\nconst MAX_DISTANCE = 500;\nconst UP: Vec3 = [0, 1, 0];\n\nexport function createCamera(init: Partial<Camera3d>): Camera3d {\n return {\n target: init.target ?? [0, 0, 0],\n yaw: init.yaw ?? 0,\n pitch: init.pitch ?? 0,\n distance: init.distance ?? 10,\n fov: init.fov ?? Math.PI / 4,\n near: init.near ?? 0.1,\n far: init.far ?? 1000,\n };\n}\n\nexport function cameraEye(camera: Camera3d): Vec3 {\n const { target, yaw, pitch, distance } = camera;\n const horizontal = Math.cos(pitch) * distance;\n return [\n target[0] + horizontal * Math.sin(yaw),\n target[1] + Math.sin(pitch) * distance,\n target[2] + horizontal * Math.cos(yaw),\n ];\n}\n\nexport function cameraView(camera: Camera3d): Mat4 {\n return lookAt(cameraEye(camera), camera.target, UP);\n}\n\nexport function cameraViewProjection(camera: Camera3d, aspect: number): Mat4 {\n return multiply(perspective(camera.fov, aspect, camera.near, camera.far), cameraView(camera));\n}\n\nexport function orbitBy(camera: Camera3d, deltaYaw: number, deltaPitch: number): Camera3d {\n return {\n ...camera,\n yaw: camera.yaw + deltaYaw,\n pitch: Math.max(-MAX_PITCH, Math.min(MAX_PITCH, camera.pitch + deltaPitch)),\n };\n}\n\nexport function dollyBy(camera: Camera3d, factor: number): Camera3d {\n return {\n ...camera,\n distance: Math.max(MIN_DISTANCE, Math.min(MAX_DISTANCE, camera.distance * factor)),\n };\n}\n","/**\n * A 3D pose, and the two things every consumer of one needs: its matrix, and\n * the world box it occupies.\n *\n * `Scene<TData, TLayer, Pose3>` is the whole of how this reaches weasel —\n * `Scene` is generic over its pose and holds this one with no adapter, no\n * subclass and no special case.\n */\n\nimport {\n compose, quatIdentity, transformAabb, type Aabb, type Mat4, type Quat, type Vec3,\n} from '@weasel-js/geom/3d';\n\nexport interface Pose3 {\n position: Vec3;\n rotation: Quat;\n scale: Vec3;\n}\n\n/** The box a primitive occupies before its pose. A unit primitive centred on\n * its own origin — which is what the kernel assumes nothing about — is\n * `UNIT_CUBE`. */\nexport const UNIT_CUBE: Aabb = { min: [-0.5, -0.5, -0.5], max: [0.5, 0.5, 0.5] };\n\nexport function pose3(\n position: Vec3,\n scale: Vec3 = [1, 1, 1],\n rotation: Quat = quatIdentity(),\n): Pose3 {\n return { position, rotation, scale };\n}\n\nexport function poseMatrix(pose: Pose3): Mat4 {\n return compose(pose.position, pose.rotation, pose.scale);\n}\n\n/**\n * The world box a posed primitive occupies, given the box it occupies in its\n * own space.\n *\n * Which local box a primitive has is the consumer's answer, not the kernel's:\n * a sphere's is rotation-invariant and a mesh's comes from its vertices, and\n * neither is something a pose can tell you.\n */\nexport function aabbOfPose(pose: Pose3, local: Aabb = UNIT_CUBE): Aabb {\n return transformAabb(poseMatrix(pose), local);\n}\n","/**\n * The screen side of the kernel: where a pointer points, and what rectangle a\n * world box covers. This is the seam weasel's 2D chrome meets — everything\n * here speaks `Bounds`-shaped screen rectangles, which is why a 3D host can\n * pass the dispatcher's world point through unchanged.\n */\n\nimport {\n invert,\n normalize,\n sub,\n transformPoint,\n transformPoint4,\n type Aabb,\n type Mat4,\n type Ray,\n type Vec3,\n} from '@weasel-js/geom/3d';\n\n/** A pane's rectangle in CSS pixels, relative to the client. */\nexport interface ViewportRect {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\n/** A screen rectangle in the shape core's `Bounds` uses. */\nexport interface ScreenBox {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/** A `ScreenBox` a host is being asked to outline. `tint` says why: warm for\n * what is selected, cool for what the gesture in hand is proposing. */\nexport interface ChromeBox extends ScreenBox {\n tint?: 'selection' | 'gesture';\n}\n\n/** Screen point (y down, rect-relative) to normalized device coordinates (y up). */\nexport function screenToNdc(\n point: { x: number; y: number },\n rect: ViewportRect,\n): { x: number; y: number } {\n return {\n x: ((point.x - rect.x) / rect.w) * 2 - 1,\n y: 1 - ((point.y - rect.y) / rect.h) * 2,\n };\n}\n\nexport function ndcToScreen(\n ndc: { x: number; y: number },\n rect: ViewportRect,\n): { x: number; y: number } {\n return {\n x: rect.x + (ndc.x * 0.5 + 0.5) * rect.w,\n y: rect.y + (1 - (ndc.y * 0.5 + 0.5)) * rect.h,\n };\n}\n\n/**\n * The ray a screen point names, given the camera that drew the rect. This is\n * the whole of what 3D picking needs from a pointer — which is why the world\n * point the dispatcher carries stays two numbers.\n *\n * `null` when the point names no ray: the view-projection has no inverse (a\n * camera on its own target, a degenerate projection) or the pane has no area.\n */\nexport function rayThroughScreenPoint(\n point: { x: number; y: number },\n rect: ViewportRect,\n viewProjection: Mat4,\n eye: Vec3,\n): Ray | null {\n const inv = invert(viewProjection);\n if (!inv) return null;\n const ndc = screenToNdc(point, rect);\n const near = transformPoint(inv, [ndc.x, ndc.y, -1]);\n const far = transformPoint(inv, [ndc.x, ndc.y, 1]);\n const direction = normalize(sub(far, near));\n // normalize answers zero for a non-finite difference, which is no direction.\n if (direction[0] === 0 && direction[1] === 0 && direction[2] === 0) return null;\n return { origin: eye, direction };\n}\n\ntype Clip = readonly [number, number, number, number];\n\n/** Signed distance to the near plane in clip space; `>= 0` is in front of it.\n * GL puts the near plane at `z = -w`. */\nfunction nearSide(p: Clip): number {\n return p[2] + p[3];\n}\n\nfunction lerpClip(a: Clip, b: Clip, t: number): Clip {\n return [\n a[0] + (b[0] - a[0]) * t,\n a[1] + (b[1] - a[1]) * t,\n a[2] + (b[2] - a[2]) * t,\n a[3] + (b[3] - a[3]) * t,\n ];\n}\n\n/** The three edges leaving corner `i` toward a higher bit, as bit masks. */\nconst EDGE_BITS = [1, 2, 4] as const;\n\n/**\n * The screen rectangle a world-space box covers — the currency weasel's chrome\n * and `PoseDescriptor.getBounds` speak in. `null` when the box is entirely\n * behind the near plane.\n *\n * Each of the twelve edges is clipped against the near plane before it is\n * projected, rather than dropping the corners behind it. Dropping them reports\n * a box that is too small for anything straddling the near plane, and the error\n * grows as the camera moves in: a solid the pointer is inside of would outline\n * as a sliver, or as nothing.\n */\nexport function projectAabbToScreen(\n box: Aabb,\n viewProjection: Mat4,\n rect: ViewportRect,\n): ScreenBox | null {\n const corners: Clip[] = [];\n for (let i = 0; i < 8; i++) {\n const corner: Vec3 = [\n i & 1 ? box.max[0] : box.min[0],\n i & 2 ? box.max[1] : box.min[1],\n i & 4 ? box.max[2] : box.min[2],\n ];\n corners.push(transformPoint4(viewProjection, corner));\n }\n\n let minX = Number.POSITIVE_INFINITY;\n let minY = Number.POSITIVE_INFINITY;\n let maxX = Number.NEGATIVE_INFINITY;\n let maxY = Number.NEGATIVE_INFINITY;\n let any = false;\n\n const extend = (p: Clip): void => {\n if (!(p[3] > 0)) return;\n const screen = ndcToScreen({ x: p[0] / p[3], y: p[1] / p[3] }, rect);\n minX = Math.min(minX, screen.x);\n minY = Math.min(minY, screen.y);\n maxX = Math.max(maxX, screen.x);\n maxY = Math.max(maxY, screen.y);\n any = true;\n };\n\n for (let i = 0; i < 8; i++) {\n for (const bit of EDGE_BITS) {\n if (i & bit) continue;\n let a = corners[i];\n let b = corners[i | bit];\n const da = nearSide(a);\n const db = nearSide(b);\n if (da < 0 && db < 0) continue;\n if (da < 0) a = lerpClip(a, b, da / (da - db));\n else if (db < 0) b = lerpClip(b, a, db / (db - da));\n extend(a);\n extend(b);\n }\n }\n\n if (!any) return null;\n return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };\n}\n","/**\n * The kit's deps, implemented against a 3D world.\n *\n * Every one of these takes its camera from a thunk rather than an argument.\n * That is the kernel's central claim in code: the dispatcher keeps handing\n * actions two numbers, and what makes them mean something in 3D is the camera\n * each dep already closes over. `InvocationCtx` needs no widening, and none of\n * these needs a ray passed in.\n *\n * Where a kit contract cannot be honored, the implementation throws rather than\n * guessing. A guess would report a fit the kernel does not have.\n */\n\nimport type {\n AreaSelectDep,\n InsertDep,\n InsertExtras,\n NodeAtPointDep,\n NodeId,\n PoseDescriptor,\n Scene,\n SceneNode,\n SnapDep,\n} from '@weasel-js/core';\nimport {\n add,\n dot,\n intersectRayAabb,\n intersectRayPlane,\n len,\n normalize,\n quatIdentity,\n scale as scaleBy,\n sub,\n type Aabb,\n type Ray,\n type Vec3,\n} from '@weasel-js/geom/3d';\nimport { cameraEye, cameraViewProjection, type Camera3d } from './camera';\nimport { aabbOfPose, type Pose3 } from './pose3';\nimport {\n projectAabbToScreen,\n rayThroughScreenPoint,\n type ScreenBox,\n type ViewportRect,\n} from './screen';\n\nexport interface Viewport3d {\n camera: Camera3d;\n width: number;\n height: number;\n /**\n * Where the pane sits in client coordinates. A 3D host works in one space\n * from the event to the ray — it passes the dispatcher an identity\n * `clientToWorld` — so the deps subtract this rather than the dispatcher.\n */\n originX?: number;\n originY?: number;\n}\n\nexport type ViewportSource = () => Viewport3d;\n\n/** A 3D scene is core's scene with a `Pose3`. Nothing wraps it. */\nexport type Scene3d<TData, TLayer extends string> = Scene<TData, TLayer, Pose3>;\nexport type Node3d<TData, TLayer extends string> = SceneNode<TData, TLayer, Pose3>;\n\n/**\n * The world box a node occupies. The kernel cannot answer this, and the reason\n * is sharper than \"it does not know the mesh\": a sphere's box is the same under\n * every rotation, and no transform of a local box reproduces that — rotate a\n * cube and it widens. Asking for the world box directly is what lets a\n * primitive answer for its own symmetry.\n *\n * Omitted, a node is a unit cube carried by its pose.\n */\nexport type NodeBounds<TData, TLayer extends string> = (\n node: Node3d<TData, TLayer>,\n) => Aabb;\n\nexport interface World3d<TData, TLayer extends string> {\n scene: Scene3d<TData, TLayer>;\n viewport: ViewportSource;\n bounds?: NodeBounds<TData, TLayer>;\n /** The axis gestures resolve against. Default `+y`. */\n up?: Vec3;\n}\n\nconst DEFAULT_UP: Vec3 = [0, 1, 0];\n\nfunction boundsOfNode<TData, TLayer extends string>(\n world: World3d<TData, TLayer>,\n node: Node3d<TData, TLayer>,\n): Aabb {\n return world.bounds ? world.bounds(node) : aabbOfPose(node.pose);\n}\n\n/** The pane, in the coordinate space the host's points arrive in. */\nfunction rectOf(viewport: Viewport3d): ViewportRect {\n return {\n x: viewport.originX ?? 0,\n y: viewport.originY ?? 0,\n w: viewport.width,\n h: viewport.height,\n };\n}\n\nfunction viewProjectionOf(viewport: Viewport3d) {\n return cameraViewProjection(viewport.camera, viewport.width / Math.max(1, viewport.height));\n}\n\nfunction rayAt(viewport: Viewport3d, point: { x: number; y: number }): Ray | null {\n return rayThroughScreenPoint(\n point,\n rectOf(viewport),\n viewProjectionOf(viewport),\n cameraEye(viewport.camera),\n );\n}\n\n/**\n * The plane through `p` that faces the camera.\n *\n * This is the depth choice the kit's reverse contracts need: a screen\n * rectangle names a pose only once something says how far away it is, and the\n * answer is \"as far as it already was\". Nothing resolved on this plane changes\n * depth, and one screen pixel is the same world distance everywhere on it.\n */\nfunction viewPlaneAt(camera: Camera3d, p: Vec3): { normal: Vec3; offset: number } {\n const normal = normalize(sub(camera.target, cameraEye(camera)));\n return { normal, offset: dot(normal, p) };\n}\n\n/** Where a screen point lands on `plane`, or `null` when it casts no ray. */\nfunction onPlane(\n vp: Viewport3d,\n plane: { normal: Vec3; offset: number },\n point: { x: number; y: number },\n): Vec3 | null {\n const ray = rayAt(vp, point);\n if (!ray) return null;\n const t = intersectRayPlane(ray, plane.normal, plane.offset);\n return t === null ? null : pointOnRay(ray, t);\n}\n\n/** The world box containing `points`. */\nfunction aabbOfPoints(points: readonly Vec3[]): Aabb {\n const min: [number, number, number] = [Infinity, Infinity, Infinity];\n const max: [number, number, number] = [-Infinity, -Infinity, -Infinity];\n for (const p of points) {\n for (let i = 0; i < 3; i++) {\n if (p[i] < min[i]) min[i] = p[i];\n if (p[i] > max[i]) max[i] = p[i];\n }\n }\n return { min, max };\n}\n\nfunction pointOnRay(ray: Ray, t: number): Vec3 {\n return [\n ray.origin[0] + ray.direction[0] * t,\n ray.origin[1] + ray.direction[1] * t,\n ray.origin[2] + ray.direction[2] * t,\n ];\n}\n\nfunction overlaps(a: ScreenBox, b: ScreenBox): boolean {\n return (\n a.x < b.x + b.width && a.x + a.width > b.x && a.y < b.y + b.height && a.y + a.height > b.y\n );\n}\n\n/** The screen box a node covers, or null when it projects to nothing visible. */\nexport function screenBoxOf<TData, TLayer extends string>(\n world: World3d<TData, TLayer>,\n id: NodeId,\n viewport: Viewport3d,\n): ScreenBox | null {\n const node = world.scene.get(id) as Node3d<TData, TLayer> | undefined;\n if (!node) return null;\n return projectAabbToScreen(\n boundsOfNode(world, node),\n viewProjectionOf(viewport),\n rectOf(viewport),\n );\n}\n\n/**\n * Nearest node along the ray the screen point names.\n *\n * The kit calls this with what it believes is a world point; a 3D host feeds\n * the dispatcher an identity `clientToWorld`, so it is the pane-relative screen\n * point, which is exactly what a ray needs.\n */\nexport function createNodeAtPoint<TData, TLayer extends string>(\n world: World3d<TData, TLayer>,\n): NodeAtPointDep {\n return (point, exclude) => {\n const excluded = exclude ? new Set(exclude) : null;\n const vp = world.viewport();\n const ray = rayAt(vp, point);\n if (!ray) return null;\n let best: NodeId | null = null;\n let bestT = Infinity;\n for (const node of world.scene.renderOrderNodes() as readonly Node3d<TData, TLayer>[]) {\n if (excluded?.has(node.id)) continue;\n const { min, max } = boundsOfNode(world, node);\n const t = intersectRayAabb(ray, min, max);\n if (t !== null && t < bestT) {\n bestT = t;\n best = node.id;\n }\n }\n return best;\n };\n}\n\n/** Marquee: the nodes whose projected box the screen rectangle touches. */\nexport function createAreaSelect<TData, TLayer extends string>(\n world: World3d<TData, TLayer>,\n selection: { get(): readonly NodeId[]; set(ids: NodeId[]): void },\n): AreaSelectDep {\n return {\n hitTestArea(bounds) {\n const vp = world.viewport();\n const hits: NodeId[] = [];\n for (const node of world.scene.renderOrderNodes()) {\n const box = screenBoxOf(world, node.id, vp);\n if (box && overlaps(box, bounds)) hits.push(node.id);\n }\n return hits;\n },\n getSelection: () => [...selection.get()],\n setSelection: (ids) => selection.set([...ids]),\n };\n}\n\n/** No grid in 3D yet; the identity keeps `insertAction` happy without lying. */\nexport function createSnap(): SnapDep {\n return { point: (p) => p };\n}\n\n/** The ground-plane rectangle a drag swept out, in world units. */\nexport interface Footprint {\n /** Centre of the swept rectangle, on the ground plane. */\n center: Vec3;\n /** Extent along the two axes the up axis is not. */\n width: number;\n depth: number;\n}\n\n/**\n * A drag rectangle becomes a footprint on the ground plane: its four corners\n * are cast onto the plane and the hit points give the extent. What node that\n * implies is the consumer's answer — the kernel does not know what it is\n * inserting.\n *\n * The kit's `InsertDep.commit` hands over a screen-space AABB and nothing else,\n * which turns out to be enough: the depth the 2D contract cannot express comes\n * from the plane, not from the caller.\n */\nexport function createInsert(opts: {\n viewport: ViewportSource;\n up?: Vec3;\n /** Smallest footprint a click-sized drag produces. Default 0.25. */\n minExtent?: number;\n mint(footprint: Footprint, extras: InsertExtras): NodeId | null;\n}): InsertDep {\n const up = opts.up ?? DEFAULT_UP;\n const minExtent = opts.minExtent ?? 0.25;\n // The two axes the up axis is not, so a footprint reads the same whichever\n // way up the world is.\n const axes: [number, number] = up[1] !== 0 ? [0, 2] : up[0] !== 0 ? [1, 2] : [0, 1];\n\n return {\n commit(bounds, extras) {\n const vp = opts.viewport();\n const corners = [\n { x: bounds.x, y: bounds.y },\n { x: bounds.x + bounds.width, y: bounds.y },\n { x: bounds.x, y: bounds.y + bounds.height },\n { x: bounds.x + bounds.width, y: bounds.y + bounds.height },\n ];\n\n const hits: Vec3[] = [];\n for (const corner of corners) {\n const ray = rayAt(vp, corner);\n if (!ray) continue;\n const t = intersectRayPlane(ray, up, 0);\n if (t !== null) hits.push(pointOnRay(ray, t));\n }\n if (hits.length === 0) return null;\n\n const [u, v] = axes;\n let minU = Infinity, maxU = -Infinity, minV = Infinity, maxV = -Infinity;\n for (const h of hits) {\n minU = Math.min(minU, h[u]); maxU = Math.max(maxU, h[u]);\n minV = Math.min(minV, h[v]); maxV = Math.max(maxV, h[v]);\n }\n\n const center: [number, number, number] = [0, 0, 0];\n center[u] = (minU + maxU) / 2;\n center[v] = (minV + maxV) / 2;\n return opts.mint(\n {\n center,\n width: Math.max(minExtent, maxU - minU),\n depth: Math.max(minExtent, maxV - minV),\n },\n extras,\n );\n },\n };\n}\n\n/**\n * `PoseDescriptor` over a 3D pose, with `Bounds` read as the screen box the\n * node covers. Chrome and marquee math work unchanged; the two methods that\n * run the other way cannot.\n *\n * `forNode` is what lets a sphere bound itself as a sphere: every node is a\n * `Pose3`, so the shape lives on `node.data` and nothing but the node can say\n * which one this is. Unspecialized — the kit calling with only a pose in hand —\n * it answers with the default local box, which is the looser of the two.\n *\n * The two methods that run the other way — `remapBounds` and `fromBounds` —\n * take a screen rectangle, which names a pose only once something supplies a\n * depth. Both resolve it against the plane through the pose they were handed,\n * facing the camera, so nothing they produce changes depth. See\n * {@link viewPlaneAt}.\n */\nexport function createPoseDescriptor<TData, TLayer extends string>(\n world: World3d<TData, TLayer>,\n): PoseDescriptor<Pose3, Node3d<TData, TLayer>> {\n const up = world.up ?? DEFAULT_UP;\n const upAxis = up[1] !== 0 ? 1 : up[0] !== 0 ? 0 : 2;\n const specialized = new WeakMap<object, PoseDescriptor<Pose3, Node3d<TData, TLayer>>>();\n\n // A drag whose camera stops casting rays mid-gesture keeps moving through the\n // last one that did, rather than snapping the node back.\n let lastCastable: Viewport3d | null = null;\n function castableViewport(): Viewport3d | null {\n const vp = world.viewport();\n const rect = rectOf(vp);\n if (rayAt(vp, { x: rect.x + rect.w / 2, y: rect.y + rect.h / 2 })) lastCastable = vp;\n return lastCastable;\n }\n\n function build(worldBox: (pose: Pose3) => Aabb): PoseDescriptor<Pose3, Node3d<TData, TLayer>> {\n const boundsIn = (vp: Viewport3d, pose: Pose3) =>\n projectAabbToScreen(worldBox(pose), viewProjectionOf(vp), rectOf(vp));\n const boundsOf = (pose: Pose3) => boundsIn(world.viewport(), pose);\n\n const descriptor: PoseDescriptor<Pose3, Node3d<TData, TLayer>> = {\n forNode(node) {\n let d = specialized.get(node);\n if (!d) {\n // The pose arriving here is often an in-flight override rather than\n // `node.pose`, so the consumer is asked about that pose on this node\n // rather than about the node as committed.\n d = build((pose) => boundsOfNode(world, { ...node, pose }));\n specialized.set(node, d);\n }\n return d;\n },\n\n getBounds(pose) {\n return boundsOf(pose) ?? { x: 0, y: 0, width: 0, height: 0 };\n },\n\n /**\n * The src→dst screen map, applied at the pose's own depth: the node's\n * screen anchor moves the way the rectangle does, and both ends of that\n * move are read back off the plane through its position, so the world\n * delta is exact rather than a scaled screen one.\n *\n * The scale is uniform. The rectangle names two extents and a pose has\n * three, so there is no reading in which a screen box resizes a solid\n * per-axis; the geometric mean is the one factor that agrees with both\n * of the extents it does name.\n */\n remapBounds(pose, src, dst) {\n const vp = castableViewport();\n if (!vp) return pose;\n const box = boundsIn(vp, pose);\n if (!box) return pose;\n\n const kx = src.width === 0 ? 1 : dst.width / src.width;\n const ky = src.height === 0 ? 1 : dst.height / src.height;\n const from = { x: box.x + box.width / 2, y: box.y + box.height / 2 };\n const to = {\n x: dst.x + (from.x - src.x) * kx,\n y: dst.y + (from.y - src.y) * ky,\n };\n\n const plane = viewPlaneAt(vp.camera, pose.position);\n const before = onPlane(vp, plane, from);\n const after = onPlane(vp, plane, to);\n if (!before || !after) return pose;\n\n const k = Math.sqrt(Math.abs(kx * ky));\n return {\n ...pose,\n position: add(pose.position, sub(after, before)),\n scale: scaleBy(pose.scale, k),\n };\n },\n\n /**\n * The world box the rectangle covers at the template's depth, as a pose.\n *\n * The rectangle is flat and a box is not, so the third extent — the one\n * no screen rectangle names — is the mean of the two it does. The result\n * is world-axis-aligned and unrotated: the pose carries none of the\n * template's shape, which is the contract, and it takes only its depth.\n *\n * Extents are written as `scale`, so this reads a pose as carrying a\n * unit primitive — the same assumption `aabbOfPose` makes by default.\n */\n fromBounds(bounds, template) {\n const vp = castableViewport();\n if (!vp) return template;\n const plane = viewPlaneAt(vp.camera, template.position);\n\n const corners: Vec3[] = [];\n for (const [x, y] of [\n [bounds.x, bounds.y],\n [bounds.x + bounds.width, bounds.y],\n [bounds.x, bounds.y + bounds.height],\n [bounds.x + bounds.width, bounds.y + bounds.height],\n ]) {\n const corner = onPlane(vp, plane, { x, y });\n if (!corner) return template;\n corners.push(corner);\n }\n\n const width = len(sub(corners[1]!, corners[0]!));\n const height = len(sub(corners[2]!, corners[0]!));\n const half = Math.sqrt(width * height) / 2;\n const box = aabbOfPoints(\n corners.flatMap((c) => [\n add(c, scaleBy(plane.normal, half)),\n sub(c, scaleBy(plane.normal, half)),\n ]),\n );\n return {\n position: scaleBy(add(box.min, box.max), 0.5),\n rotation: quatIdentity(),\n scale: sub(box.max, box.min),\n };\n },\n\n /**\n * Screen-space deltas resolve against the plane the node already sits on,\n * so the two numbers the kit passes are enough: the camera that turns\n * them into a world position is the one this dep closed over.\n */\n translate(pose, dx, dy) {\n const vp = castableViewport();\n if (!vp) return pose;\n const box = boundsIn(vp, pose);\n if (!box) return pose;\n const from = { x: box.x + box.width / 2, y: box.y + box.height / 2 };\n const to = { x: from.x + dx, y: from.y + dy };\n const offset = pose.position[upAxis];\n\n const ray = rayAt(vp, to);\n if (!ray) return pose;\n const t = intersectRayPlane(ray, up, offset);\n if (t === null) return pose;\n\n const anchorRay = rayAt(vp, from);\n if (!anchorRay) return pose;\n const anchorT = intersectRayPlane(anchorRay, up, offset);\n if (anchorT === null) return pose;\n\n const delta = sub(pointOnRay(ray, t), pointOnRay(anchorRay, anchorT));\n const position: [number, number, number] = [\n pose.position[0] + delta[0],\n pose.position[1] + delta[1],\n pose.position[2] + delta[2],\n ];\n position[upAxis] = pose.position[upAxis];\n return { ...pose, position };\n },\n\n intersectsRect(pose, rect) {\n const box = boundsOf(pose);\n return box ? overlaps(box, rect) : false;\n },\n\n supportsRotation() {\n return false;\n },\n };\n\n return descriptor;\n }\n\n return build((pose) => aabbOfPose(pose));\n}\n","/**\n * What a host paints for a gesture that proposes chrome rather than a pose.\n *\n * A marquee sweep and a box-drag both publish an `OngoingOverlay`, which used\n * to reach paint only through core's own 2D layer. `resolveOverlays` hands back\n * world geometry with no renderer in it, so the same call `<SceneCanvas>` makes\n * serves a consumer-owned 3D renderer.\n *\n * A 3D host's `clientToWorld` is identity, so an overlay's world bounds are the\n * client rect the deps already work in, and the pane origin comes off exactly\n * once. Only the rectangular variants resolve here: a lasso, a pencil and a run\n * of points are paths, and a path is geometry this cannot flatten to a box.\n */\n\nimport { resolveOverlays, type OngoingHandle } from '@weasel-js/core';\nimport type { ChromeBox } from './screen';\n\n/** Gesture chrome, in CSS pixels relative to the pane's top-left. */\nexport function collectOverlayBoxes(\n handles: Iterable<OngoingHandle>,\n origin: { x: number; y: number },\n): ChromeBox[] {\n const overlays = [];\n for (const handle of handles) {\n const ov = handle.overlay?.();\n if (ov) overlays.push(ov);\n }\n\n const boxes: ChromeBox[] = [];\n for (const resolved of resolveOverlays(overlays)) {\n if (resolved.kind !== 'marquee' && resolved.kind !== 'insertPreview') continue;\n const { x, y, width, height } = resolved.bounds;\n boxes.push({ x: x - origin.x, y: y - origin.y, width, height, tint: 'gesture' });\n }\n return boxes;\n}\n","/**\n * Camera tools and their actions.\n *\n * Shape tools transfer from the kit untouched — a 3D host mounts core's own\n * `useSelectTool` and a rectangle-style insert binding and neither notices the\n * dimension. Only the viewport tools are written here, which is the one\n * exception the kernel doc predicted: `useHandTool` routes to\n * `viewport.dragPan`, and that wants the 2D `view`.\n */\n\nimport { defineTool, type Action, type Tool } from '@weasel-js/core';\nimport { dollyBy, orbitBy, type Camera3d } from './camera';\nimport type { Camera3dDep } from './cameraDep';\n\n/** Radians per CSS pixel of drag. A full pane width is a bit over half a turn. */\nconst ORBIT_RATE = 0.006;\nconst DOLLY_RATE = 0.0015;\n\nfunction cameraDep(deps: unknown): Camera3dDep | undefined {\n return (deps as { camera3d?: Camera3dDep }).camera3d;\n}\n\nexport const orbitAction: Action = {\n id: 'camera.orbit',\n label: 'Orbit',\n requires: ['camera3d'],\n invoker: {\n timing: 'ongoing',\n start(ctx) {\n const dep = cameraDep(ctx.deps);\n const origin: Camera3d | undefined = dep?.get();\n return {\n kind: 'orbit',\n onMove(move) {\n if (!dep || !origin) return;\n // `drag.delta` runs from the drag origin, not the previous move, so\n // this composes against the camera as it was when the drag began.\n const delta = move.drag?.screenDelta ?? move.drag?.delta;\n if (!delta) return;\n dep.set(orbitBy(origin, -delta.x * ORBIT_RATE, delta.y * ORBIT_RATE));\n },\n };\n },\n },\n};\n\nexport const dollyAction: Action = {\n id: 'camera.dolly',\n label: 'Dolly',\n group: 'viewport',\n // Wheel, in any tool — the dispatcher merges the event's deltas into params.\n defaultBinding: [{ spec: { kind: 'wheel' }, opts: {} }],\n requires: ['camera3d'],\n invoker: {\n timing: 'immediate',\n run(deps, params) {\n const dep = cameraDep(deps);\n if (!dep) return;\n const amount = typeof params?.deltaY === 'number' ? params.deltaY : 0;\n dep.set(dollyBy(dep.get(), Math.exp(amount * DOLLY_RATE)));\n },\n },\n};\n\nexport function useOrbitTool(): Tool<null> {\n return defineTool<null>({\n id: 'orbit',\n hookName: 'useOrbitTool',\n cursor: 'grab',\n presentation: { label: 'Orbit', group: 'viewport' },\n bindings: [{ spec: { kind: 'drag' }, actionId: 'camera.orbit' }],\n });\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weasel-js/kernel3d",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "3D kernel for weasel: poses, orbit camera, ray picking and screen-projected chrome geometry over core's scene graph and dispatcher. Hosts a renderer rather than owning one.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -37,10 +37,10 @@
|
|
|
37
37
|
"build": "tsup"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@weasel-js/geom": "1.
|
|
40
|
+
"@weasel-js/geom": "1.5.0"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"@weasel-js/core": "1.
|
|
43
|
+
"@weasel-js/core": "1.5.0"
|
|
44
44
|
},
|
|
45
45
|
"publishConfig": {
|
|
46
46
|
"access": "public",
|