@weasel-js/kernel3d 1.10.1 → 1.11.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 +12 -0
- package/dist/index.d.ts +212 -42
- package/dist/index.js +556 -105
- package/dist/index.js.map +1 -1
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -36,6 +36,18 @@ One thing does not:
|
|
|
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
38
|
|
|
39
|
+
## The camera's actions
|
|
40
|
+
|
|
41
|
+
`camera.orbit` turns the `camera3d` dep on a drag, `camera.pan` slides its
|
|
42
|
+
target across the screen plane, `camera.dolly` answers the wheel and both
|
|
43
|
+
trackpad pinches, `camera.pinchDolly` two fingers, and `camera.home` returns it
|
|
44
|
+
to the dep's `home()`. Both dollies go through `dollyToward`, which keeps the
|
|
45
|
+
point in the target's plane under the pointer still, as a 2D wheel zoom does,
|
|
46
|
+
measured against the dep's `size()`. Each holds the camera inside the dep's `limits()` — an
|
|
47
|
+
`OrbitLimits` in the scene's own units, defaulting to `DEFAULT_ORBIT_LIMITS` —
|
|
48
|
+
through `clampCamera`, which `orbitBy` and `dollyBy` take as well. labkit's
|
|
49
|
+
`orbit` capability is these actions on a dep the trial owns.
|
|
50
|
+
|
|
39
51
|
## The depth a screen rectangle does not name
|
|
40
52
|
|
|
41
53
|
`PoseDescriptor` reads `Bounds` as the screen box a solid covers. `getBounds`
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Vec3, Mat4, Quat, Aabb, Ray } from '@weasel-js/geom/3d';
|
|
2
|
-
import { SceneNode, Scene,
|
|
2
|
+
import { SceneNode, Scene, SnapSource, SnapOutcomeStore, NodeId, AreaSelectDep, NodeAtPointDep, PoseDescriptor, SnapDep, InsertExtras, InsertDep, SnapMark, OngoingHandle, Action, Tool } from '@weasel-js/core';
|
|
3
|
+
import * as _weasel_js_prefs from '@weasel-js/prefs';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* An orbit camera: a target, two angles and a distance. Immutable — every
|
|
@@ -34,11 +35,45 @@ declare function cameraEye(camera: Camera3d): Vec3;
|
|
|
34
35
|
declare function cameraView(camera: Camera3d): Mat4;
|
|
35
36
|
/** World-to-clip matrix for a pane of the given width/height `aspect`. */
|
|
36
37
|
declare function cameraViewProjection(camera: Camera3d, aspect: number): Mat4;
|
|
37
|
-
/**
|
|
38
|
-
*
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
/** How far an orbit camera may turn and travel. Distances are in world units,
|
|
39
|
+
* so a scene measured in millimeters and one in kilometers want different ones. */
|
|
40
|
+
interface OrbitLimits {
|
|
41
|
+
minDistance: number;
|
|
42
|
+
maxDistance: number;
|
|
43
|
+
/** Radians. Held inside ±`MAX_PITCH` whatever is asked for. */
|
|
44
|
+
minPitch: number;
|
|
45
|
+
maxPitch: number;
|
|
46
|
+
}
|
|
47
|
+
/** Pitch short of the poles, distance 0.5..500. */
|
|
48
|
+
declare const DEFAULT_ORBIT_LIMITS: OrbitLimits;
|
|
49
|
+
/** The camera with its pitch and distance inside `limits`. Returns `camera`
|
|
50
|
+
* itself when it already holds. */
|
|
51
|
+
declare function clampCamera(camera: Camera3d, limits?: Partial<OrbitLimits>): Camera3d;
|
|
52
|
+
/** The camera turned about its target by the given radians, pitch held inside
|
|
53
|
+
* `limits`. Yaw is not wrapped. */
|
|
54
|
+
declare function orbitBy(camera: Camera3d, deltaYaw: number, deltaPitch: number, limits?: Partial<OrbitLimits>): Camera3d;
|
|
55
|
+
/** The camera with its distance multiplied by `factor`, held inside `limits`. */
|
|
56
|
+
declare function dollyBy(camera: Camera3d, factor: number, limits?: Partial<OrbitLimits>): Camera3d;
|
|
57
|
+
/**
|
|
58
|
+
* The camera with its target slid across the screen plane, so the point at the
|
|
59
|
+
* target follows a pointer that moved `dx`, `dy` CSS pixels in a pane
|
|
60
|
+
* `paneHeight` pixels tall. Neither angle nor distance changes.
|
|
61
|
+
*/
|
|
62
|
+
declare function panBy(camera: Camera3d, dx: number, dy: number, paneHeight: number): Camera3d;
|
|
63
|
+
/**
|
|
64
|
+
* `dollyBy` about a point on the pane rather than its middle: the point in the
|
|
65
|
+
* target's screen-facing plane under `at` — CSS pixels from the pane's
|
|
66
|
+
* top-left, in a pane of `pane` size — stays under it, as a 2D camera's wheel
|
|
67
|
+
* zoom keeps the point under the pointer. The target slides toward that point
|
|
68
|
+
* by as much as the distance shrank, so the next turn pivots nearer it.
|
|
69
|
+
*/
|
|
70
|
+
declare function dollyToward(camera: Camera3d, factor: number, at: {
|
|
71
|
+
x: number;
|
|
72
|
+
y: number;
|
|
73
|
+
}, pane: {
|
|
74
|
+
width: number;
|
|
75
|
+
height: number;
|
|
76
|
+
}, limits?: Partial<OrbitLimits>): Camera3d;
|
|
42
77
|
|
|
43
78
|
/**
|
|
44
79
|
* A 3D pose, and the two things every consumer of one needs: its matrix, and
|
|
@@ -137,14 +172,6 @@ declare function rayThroughScreenPoint(point: {
|
|
|
137
172
|
*/
|
|
138
173
|
declare function projectAabbToScreen(box: Aabb, viewProjection: Mat4, rect: ScreenBox): ScreenBox | null;
|
|
139
174
|
|
|
140
|
-
/**
|
|
141
|
-
* The kit's deps, implemented against a 3D world.
|
|
142
|
-
*
|
|
143
|
-
* Every one of these takes its camera from a thunk rather than an argument:
|
|
144
|
-
* the dispatcher keeps handing actions two numbers, and what makes them mean
|
|
145
|
-
* something in 3D is the camera each dep already closes over.
|
|
146
|
-
*/
|
|
147
|
-
|
|
148
175
|
/** The camera and the pane it draws into; `width`/`height` are the pane's
|
|
149
176
|
* size in the same units as the pointer points. */
|
|
150
177
|
interface Viewport3d {
|
|
@@ -161,6 +188,17 @@ interface Viewport3d {
|
|
|
161
188
|
}
|
|
162
189
|
/** Read on every dep call, so a dep always sees the current camera and pane. */
|
|
163
190
|
type ViewportSource = () => Viewport3d;
|
|
191
|
+
type Point$1 = {
|
|
192
|
+
x: number;
|
|
193
|
+
y: number;
|
|
194
|
+
};
|
|
195
|
+
/** Where `p` draws in the viewport, or null when it is behind the near plane
|
|
196
|
+
* or projects to nothing finite. */
|
|
197
|
+
declare function projectPoint(p: Vec3, viewport: Viewport3d): Point$1 | null;
|
|
198
|
+
|
|
199
|
+
/** The 3D world every dep is built from: core's scene with a `Pose3`, the
|
|
200
|
+
* viewport it is seen through, and how wide each node is. */
|
|
201
|
+
|
|
164
202
|
/** A 3D scene is core's scene with a `Pose3`. Nothing wraps it. */
|
|
165
203
|
type Scene3d<TData, TLayer extends string> = Scene<TData, TLayer, Pose3>;
|
|
166
204
|
/** A node of a `Scene3d`. */
|
|
@@ -182,6 +220,15 @@ interface World3d<TData, TLayer extends string> {
|
|
|
182
220
|
/** The axis gestures resolve against. Default `+y`. */
|
|
183
221
|
up?: Vec3;
|
|
184
222
|
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* The kit's deps, implemented against a 3D world.
|
|
226
|
+
*
|
|
227
|
+
* Every one of these takes its camera from a thunk rather than an argument:
|
|
228
|
+
* the dispatcher keeps handing actions two numbers, and what makes them mean
|
|
229
|
+
* something in 3D is the camera each dep already closes over.
|
|
230
|
+
*/
|
|
231
|
+
|
|
185
232
|
/** The screen box a node covers, or null when it projects to nothing visible. */
|
|
186
233
|
declare function screenBoxOf<TData, TLayer extends string>(world: World3d<TData, TLayer>, id: NodeId, viewport: Viewport3d): ScreenBox | null;
|
|
187
234
|
/**
|
|
@@ -197,8 +244,41 @@ declare function createAreaSelect<TData, TLayer extends string>(world: World3d<T
|
|
|
197
244
|
get(): readonly NodeId[];
|
|
198
245
|
set(ids: NodeId[]): void;
|
|
199
246
|
}): AreaSelectDep;
|
|
200
|
-
/**
|
|
201
|
-
|
|
247
|
+
/** Options for `createSnap`. */
|
|
248
|
+
interface Snap3dOptions {
|
|
249
|
+
/** What a gesture snaps to, in order: earlier wins a full tie. Default none,
|
|
250
|
+
* which leaves only the gesture's own constraint. */
|
|
251
|
+
sources?: readonly SnapSource<Vec3>[];
|
|
252
|
+
/** The reach of a source that sets none, in screen px. Default 8. */
|
|
253
|
+
reachPx?: number;
|
|
254
|
+
/** Where the marks of the snap in flight are written, for a host to draw
|
|
255
|
+
* with `projectSnapMarks`. */
|
|
256
|
+
store?: SnapOutcomeStore<Vec3>;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* The `snap` dep over a 3D world. A move snaps its subject's world box on the
|
|
260
|
+
* plane it already moves on; an insert snaps the pointer on the ground. Both
|
|
261
|
+
* answer in screen points, which `translate` and `createInsert` cast back
|
|
262
|
+
* onto the same plane, landing exactly where the snap put them.
|
|
263
|
+
*/
|
|
264
|
+
declare function createSnap<TData, TLayer extends string>(world: World3d<TData, TLayer>, opts?: Snap3dOptions): SnapDep;
|
|
265
|
+
/**
|
|
266
|
+
* `PoseDescriptor` over a 3D pose, with `Bounds` read as the screen box the
|
|
267
|
+
* node covers.
|
|
268
|
+
*
|
|
269
|
+
* `forNode` is what lets a sphere bound itself as a sphere: the shape lives
|
|
270
|
+
* on `node.data`. Unspecialized — the kit calling with only a pose in hand —
|
|
271
|
+
* it answers with the default local box, which is the looser of the two.
|
|
272
|
+
*
|
|
273
|
+
* `remapBounds` and `fromBounds` take a screen rectangle, which names a pose
|
|
274
|
+
* only once something supplies a depth. Both resolve it against the plane
|
|
275
|
+
* through the pose they were handed, facing the camera, so nothing they
|
|
276
|
+
* produce changes depth.
|
|
277
|
+
*/
|
|
278
|
+
declare function createPoseDescriptor<TData, TLayer extends string>(world: World3d<TData, TLayer>): PoseDescriptor<Pose3, Node3d<TData, TLayer>>;
|
|
279
|
+
|
|
280
|
+
/** The `insert` dep over a 3D world: a drag becomes a footprint on the ground. */
|
|
281
|
+
|
|
202
282
|
/** The ground-plane rectangle a drag swept out, in world units. */
|
|
203
283
|
interface Footprint {
|
|
204
284
|
/** Centre of the swept rectangle, on the ground plane. */
|
|
@@ -208,14 +288,11 @@ interface Footprint {
|
|
|
208
288
|
depth: number;
|
|
209
289
|
}
|
|
210
290
|
/**
|
|
211
|
-
* A drag
|
|
212
|
-
* are cast onto the plane and the hit points give the
|
|
213
|
-
* implies is the consumer's answer — the kernel does
|
|
214
|
-
* inserting.
|
|
215
|
-
*
|
|
216
|
-
* The kit's `InsertDep.commit` hands over a screen-space AABB and nothing else:
|
|
217
|
-
* the depth the 2D contract cannot express comes from the plane, not from the
|
|
218
|
-
* caller.
|
|
291
|
+
* A drag becomes a footprint on the ground plane: its ends, or without them
|
|
292
|
+
* its box's four corners, are cast onto the plane and the hit points give the
|
|
293
|
+
* extent. What node that implies is the consumer's answer — the kernel does
|
|
294
|
+
* not know what it is inserting. The depth the 2D contract cannot express
|
|
295
|
+
* comes from the plane, not from the caller.
|
|
219
296
|
*/
|
|
220
297
|
declare function createInsert(opts: {
|
|
221
298
|
viewport: ViewportSource;
|
|
@@ -224,20 +301,67 @@ declare function createInsert(opts: {
|
|
|
224
301
|
minExtent?: number;
|
|
225
302
|
mint(footprint: Footprint, extras: InsertExtras): NodeId | null;
|
|
226
303
|
}): InsertDep;
|
|
304
|
+
|
|
305
|
+
/** What a 3D scene offers a snap session: its boxes' corners, edges, faces
|
|
306
|
+
* and centers, the planes through their sides and centers, and a grid on
|
|
307
|
+
* the ground. */
|
|
308
|
+
|
|
227
309
|
/**
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
* `forNode` is what lets a sphere bound itself as a sphere: the shape lives
|
|
232
|
-
* on `node.data`. Unspecialized — the kit calling with only a pose in hand —
|
|
233
|
-
* it answers with the default local box, which is the looser of the two.
|
|
234
|
-
*
|
|
235
|
-
* `remapBounds` and `fromBounds` take a screen rectangle, which names a pose
|
|
236
|
-
* only once something supplies a depth. Both resolve it against the plane
|
|
237
|
-
* through the pose they were handed, facing the camera, so nothing they
|
|
238
|
-
* produce changes depth.
|
|
310
|
+
* Each node's world box as targets for geometry and pointer probes: its 8
|
|
311
|
+
* corners and its center as points, its 12 edges as segments, and its 6
|
|
312
|
+
* faces as bounded planes. Targets past the reach are not offered.
|
|
239
313
|
*/
|
|
240
|
-
declare function
|
|
314
|
+
declare function boxSnapSource<TData, TLayer extends string>(world: World3d<TData, TLayer>): SnapSource<Vec3>;
|
|
315
|
+
/**
|
|
316
|
+
* The planes through each node's world box at its min, center and max on
|
|
317
|
+
* every axis, as targets for box and pointer probes: alignment in depth as
|
|
318
|
+
* well as across. Boxes sharing a plane offer it once, and its mark reaches
|
|
319
|
+
* each of them.
|
|
320
|
+
*/
|
|
321
|
+
declare function alignPlaneSource<TData, TLayer extends string>(world: World3d<TData, TLayer>): SnapSource<Vec3>;
|
|
322
|
+
/** Options for `groundGridSource`. */
|
|
323
|
+
interface GroundGridSourceOptions {
|
|
324
|
+
/** The grid's pitch, in world units. */
|
|
325
|
+
step: number;
|
|
326
|
+
/** The ground's normal; the grid lies on `up · p = 0`. Default `+y`. */
|
|
327
|
+
up?: Vec3;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* A grid on the ground plane as snap targets: for each pointer or geometry
|
|
331
|
+
* probe on the ground, the grid line on either side of it along each of the
|
|
332
|
+
* ground's two axes. Its reach is unlimited and it is a fallback, so it
|
|
333
|
+
* holds every axis no target claims and never displaces one.
|
|
334
|
+
*/
|
|
335
|
+
declare function groundGridSource(opts: GroundGridSourceOptions): SnapSource<Vec3>;
|
|
336
|
+
|
|
337
|
+
type Point = {
|
|
338
|
+
x: number;
|
|
339
|
+
y: number;
|
|
340
|
+
};
|
|
341
|
+
/** A snap mark's segment on screen: a line, a gap, or a distance. */
|
|
342
|
+
interface ScreenLine {
|
|
343
|
+
from: Point;
|
|
344
|
+
to: Point;
|
|
345
|
+
/** A gap's or distance's value, in world units. */
|
|
346
|
+
value?: number;
|
|
347
|
+
color?: string;
|
|
348
|
+
}
|
|
349
|
+
/** A snap mark's point on screen. */
|
|
350
|
+
interface ScreenPoint {
|
|
351
|
+
at: Point;
|
|
352
|
+
glyph: string;
|
|
353
|
+
label?: string;
|
|
354
|
+
color?: string;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* The marks a 3D snap session draws, in the viewport's screen space, for a
|
|
358
|
+
* host to draw through its own renderer. A mark with any point that does not
|
|
359
|
+
* project — behind the camera — is dropped whole.
|
|
360
|
+
*/
|
|
361
|
+
declare function projectSnapMarks(marks: readonly SnapMark<Vec3>[], viewport: Viewport3d): {
|
|
362
|
+
lines: ScreenLine[];
|
|
363
|
+
points: ScreenPoint[];
|
|
364
|
+
};
|
|
241
365
|
|
|
242
366
|
/**
|
|
243
367
|
* What a host paints for a gesture that proposes chrome rather than a pose.
|
|
@@ -269,13 +393,26 @@ declare function collectOverlayBoxes(handles: Iterable<OngoingHandle>, origin: {
|
|
|
269
393
|
*/
|
|
270
394
|
|
|
271
395
|
/** `camera.orbit`: an ongoing drag that turns the `camera3d` dep about its
|
|
272
|
-
* target
|
|
396
|
+
* target. */
|
|
273
397
|
declare const orbitAction: Action;
|
|
274
|
-
/** `camera.
|
|
275
|
-
* the
|
|
398
|
+
/** `camera.pan`: an ongoing drag that slides the `camera3d` dep's target
|
|
399
|
+
* across the screen plane, so the point at the target follows the pointer. */
|
|
400
|
+
declare const panAction: Action;
|
|
401
|
+
/** `camera.dolly`: moves the `camera3d` dep in or out about the point under
|
|
402
|
+
* the pointer, as a 2D camera's wheel zooms about it. A wheel's `deltaY`, and
|
|
403
|
+
* a trackpad pinch delivered as ctrl+wheel, dolly by `exp(deltaY × rate)` —
|
|
404
|
+
* one rate for both, as a 2D camera zooms by both. Safari's pinch arrives as
|
|
405
|
+
* a `scale` and divides the distance by it. Bound to all three by default, in
|
|
406
|
+
* every tool. */
|
|
276
407
|
declare const dollyAction: Action;
|
|
277
|
-
/**
|
|
278
|
-
* it
|
|
408
|
+
/** `camera.pinchDolly`: two fingers spreading dolly the `camera3d` dep in and
|
|
409
|
+
* closing dolly it out, by the ratio of their spread, about where they began. */
|
|
410
|
+
declare const pinchDollyAction: Action;
|
|
411
|
+
/** `camera.home`: returns the `camera3d` dep to its `home`. Bound to a double
|
|
412
|
+
* click by default. */
|
|
413
|
+
declare const homeAction: Action;
|
|
414
|
+
/** A tool that binds a drag to `camera.orbit` and Shift+drag to `camera.pan`.
|
|
415
|
+
* Register both actions alongside it; the tool only declares the bindings. */
|
|
279
416
|
declare function useOrbitTool(): Tool;
|
|
280
417
|
|
|
281
418
|
/**
|
|
@@ -297,6 +434,11 @@ interface Camera3dDep {
|
|
|
297
434
|
width: number;
|
|
298
435
|
height: number;
|
|
299
436
|
};
|
|
437
|
+
/** How far the camera actions may turn and dolly it. Omitted,
|
|
438
|
+
* `DEFAULT_ORBIT_LIMITS`. */
|
|
439
|
+
limits?(): Partial<OrbitLimits>;
|
|
440
|
+
/** Where `camera.home` returns it. Omitted, `camera.home` does nothing. */
|
|
441
|
+
home?(): Camera3d;
|
|
300
442
|
}
|
|
301
443
|
declare module '@weasel-js/core' {
|
|
302
444
|
interface DepSchema {
|
|
@@ -304,4 +446,32 @@ declare module '@weasel-js/core' {
|
|
|
304
446
|
}
|
|
305
447
|
}
|
|
306
448
|
|
|
307
|
-
|
|
449
|
+
/** The `orbit3d` kit settings area: how far the camera tools move per input. */
|
|
450
|
+
interface Orbit3dKitSettings {
|
|
451
|
+
/** Radians of orbit per CSS pixel of drag. */
|
|
452
|
+
orbitRate: number;
|
|
453
|
+
/** Dolly exponent per unit of wheel `deltaY`. */
|
|
454
|
+
dollyRate: number;
|
|
455
|
+
}
|
|
456
|
+
declare module '@weasel-js/react' {
|
|
457
|
+
interface KitSettings {
|
|
458
|
+
orbit3d: Orbit3dKitSettings;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
/** The `orbit3d` area as a prefs group whose paths are the `KitSettings` paths. */
|
|
462
|
+
declare const kernel3dKitSettingsSchema: Omit<_weasel_js_prefs.PrefGroup, "children"> & {
|
|
463
|
+
children: {
|
|
464
|
+
readonly orbit3d: Omit<_weasel_js_prefs.PrefGroup, "children"> & {
|
|
465
|
+
children: {
|
|
466
|
+
readonly orbitRate: _weasel_js_prefs.PrefNumber & {
|
|
467
|
+
default: number;
|
|
468
|
+
};
|
|
469
|
+
readonly dollyRate: _weasel_js_prefs.PrefNumber & {
|
|
470
|
+
default: number;
|
|
471
|
+
};
|
|
472
|
+
};
|
|
473
|
+
};
|
|
474
|
+
};
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
export { type Camera3d, type Camera3dDep, type ChromeBox, DEFAULT_ORBIT_LIMITS, type Footprint, type GroundGridSourceOptions, MAX_PITCH, type Node3d, type NodeBounds, type Orbit3dKitSettings, type OrbitLimits, type Pose3, type Scene3d, type ScreenBox, type ScreenLine, type ScreenPoint, type Snap3dOptions, UNIT_CUBE, type Viewport3d, type ViewportSource, type World3d, aabbOfPose, alignPlaneSource, boxSnapSource, cameraEye, cameraView, cameraViewProjection, clampCamera, collectOverlayBoxes, createAreaSelect, createCamera, createInsert, createNodeAtPoint, createPoseDescriptor, createSnap, dollyAction, dollyBy, dollyToward, groundGridSource, homeAction, kernel3dKitSettingsSchema, ndcToScreen, orbitAction, orbitBy, panAction, panBy, pinchDollyAction, pose3, poseMatrix, projectAabbToScreen, projectPoint, projectSnapMarks, rayThroughScreenPoint, screenBoxOf, screenToNdc, useOrbitTool };
|