@nakednous/tree 0.0.25 → 0.0.26

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # `@nakednous/tree`
2
2
 
3
- Pure numeric core for animation, coordinate-space mapping, and visibility — **zero dependencies**, runs anywhere.
3
+ Pure numeric core for animation, rate-driven control, coordinate-space mapping, and visibility — **zero dependencies**, runs anywhere.
4
4
 
5
5
  ---
6
6
 
@@ -33,13 +33,14 @@ import * as tree from '@nakednous/tree'
33
33
 
34
34
  The dependency direction is strict: `@nakednous/tree` never imports from the bridge or the DOM layer. This is what lets the same `PoseTrack` that drives a camera path also animate any object — headless, server-side, or in a future renderer.
35
35
 
36
- Source is organised into five focused modules:
36
+ Source is organised into six focused modules:
37
37
 
38
38
  ```
39
39
  form.js — you have specs, you want a matrix
40
40
  query.js — you have a matrix, you want information
41
41
  quat.js — quaternion algebra and mat4/mat3 conversions
42
42
  track.js — spline math and keyframe animation state machines
43
+ helm.js — 6-DOF rate-stream integrator — the Track family's live-input sibling
43
44
  handle.js — constraint solver + ray primitives for interactive manipulators
44
45
  ```
45
46
 
@@ -296,6 +297,68 @@ One-keyframe behaviour: `play()` with exactly one keyframe snaps `eval()` to tha
296
297
 
297
298
  ---
298
299
 
300
+ ### PoseHelm — 6-DOF rate-driven pose
301
+
302
+ The rate-stream sibling of the Track family. Where a track produces a pose from keyframes over time, a `PoseHelm` produces one from a live 6-DOF delta stream — a SpaceNavigator, a tracked hand, an agent policy. It holds a profile plus the integrated pose; there is no timeline (no keyframes, no `play` / `seek` / `loop`), and it never learns about a camera — the host hands it a resolved `basis` each step.
303
+
304
+ ```js
305
+ import { PoseHelm } from '@nakednous/tree'
306
+
307
+ const helm = new PoseHelm()
308
+ const out = { pos: [0,0,0], rot: [0,0,0,1] }
309
+
310
+ // a transport feeds raw lane rates — either half may be omitted:
311
+ helm.feed([tx, ty, tz], [rx, ry, rz])
312
+
313
+ // per-frame — host-driven, zero allocation:
314
+ helm.step(out, dt, basis) // integrate dt seconds, write the new { pos, rot }
315
+ helm.eval(out) // read the current pose without integrating
316
+ ```
317
+
318
+ `feed` is the input (as `add` is a track's); `step` + `eval` parallel `tick` + `eval`. `step` is host-driven — the bridge calls it each frame, exactly as a sketch never calls `track.tick()`.
319
+
320
+ **Profile — sign · sens · lane.** The whole sign / sensitivity / axis-map question is one flat declarative object. Six channels — three translation (Tx Ty Tz), three rotation (Rp pitch, Ry yaw, Rr roll) — each `{ sign, sens, lane }`:
321
+
322
+ ```js
323
+ helm.profile = {
324
+ Tx: { sign: +1, sens: 0.30, lane: 0 }, // lane = which fed channel drives +X
325
+ Ty: { sign: +1, sens: 0.30, lane: 2 },
326
+ Tz: { sign: -1, sens: 0.30, lane: 1 },
327
+ Rp: { sign: -1, sens: 0.0025, lane: 0 },
328
+ Ry: { sign: -1, sens: 0.0025, lane: 2 },
329
+ Rr: { sign: +1, sens: 0.0018, lane: 1 },
330
+ }
331
+ ```
332
+
333
+ - **sign** — per-app direction (camera-fly vs object-grab invert).
334
+ - **sens** — per-axis sensitivity (tame roll without touching the rest).
335
+ - **lane** — input-channel permutation: which fed channel drives this DOF. `T*` lanes index the translation triple, `R*` the rotation triple.
336
+
337
+ `sens` does all the scaling, so the same raw `feed()` suits any transport — only the profile changes. The default is SpaceNavigator-tuned and meant to be replaced wholesale for a different device. `HELM_CHANNELS` is the frozen order `['Tx','Ty','Tz','Rp','Ry','Rr']`.
338
+
339
+ **Frame — `from`.** `helm.from` names the space fed rates are interpreted in — a declaration the host reads to resolve the per-step `basis` (the core stays camera-agnostic):
340
+
341
+ ```
342
+ WORLD world axes — the identity basis (step's basis is null)
343
+ EYE a viewing camera's frame — screen-relative (default)
344
+ SELF the helm's OWN evolving pose — body-relative
345
+ <mat4> an explicit fixed frame
346
+ ```
347
+
348
+ `step` rotates both linear and angular rates through `basis`, then composes the quaternion world-frame — one code path covering body-fly and screen-relative manipulation. `SELF` is body-relative (a per-frame-rebuilt pose matrix); it is a helm `from` value only, not a general mapping space.
349
+
350
+ **Rest of the surface.**
351
+
352
+ ```js
353
+ helm.deadzone = 8 // rest-drift floor — |rate| ≤ deadzone reads as 0
354
+ helm.activity(out6) // six effective rates (post deadzone·sign·sens), channel order
355
+ helm.home([pose]) // re-home pos + rot (NOT reset — no keyframes); clears pending rate
356
+ ```
357
+
358
+ The `p5.tree` bridge wraps this into `createCameraHelm` / `createPoseHelm` (transport, camera basis, draw-loop player) plus the `helmRig` gizmo and the `createPanel(helm)` profile editor.
359
+
360
+ ---
361
+
299
362
  ### Coordinate-space mapping
300
363
 
301
364
  `mapLocation` and `mapDirection` convert points and vectors between any pair of named spaces. All work is done in flat scalar arithmetic — no objects created per call.
@@ -455,6 +518,9 @@ projLeft projRight projTop projBottom
455
518
  // Coordinate spaces
456
519
  WORLD, EYE, NDC, SCREEN, MODEL, MATRIX
457
520
 
521
+ // Helm integrator frame (helm `from` only — body-relative, not a mapping space)
522
+ SELF
523
+
458
524
  // NDC Z convention
459
525
  WEBGL // −1 (z ∈ [−1, 1])
460
526
  WEBGPU // 0 (z ∈ [0, 1])
package/dist/index.js CHANGED
@@ -12,6 +12,10 @@ const SCREEN = 'SCREEN';
12
12
  const MODEL = 'MODEL';
13
13
  const MATRIX = 'MATRIX';
14
14
 
15
+ // Integrator frame source (helm `from` only): the construct's own current pose
16
+ // — body-relative; not a general mapDirection space.
17
+ const SELF = 'SELF';
18
+
15
19
  // NDC Z convention (only difference between backends)
16
20
  const WEBGL = -1; // z ∈ [−1, 1]
17
21
  const WEBGPU = 0; // z ∈ [0, 1]
@@ -3021,6 +3025,273 @@ function createConstraint(kind, opts) {
3021
3025
  return new Constraint(kind, opts);
3022
3026
  }
3023
3027
 
3028
+ /**
3029
+ * @file Source-agnostic 6-DOF pose helm — integrates a live rate stream into a
3030
+ * { pos, rot } pose. Renderer- and transport-agnostic. Zero dependencies.
3031
+ * @module tree/helm
3032
+ * @license AGPL-3.0-only
3033
+ *
3034
+ * A helm is the rate-stream sibling of the Track family: where a Track produces
3035
+ * a pose from keyframes over time, a helm produces a pose from a live 6-DOF
3036
+ * delta stream (a SpaceNavigator, a tracked hand, an agent policy). It is NOT a
3037
+ * handle — a handle reports a `vec3` solved from a pointer ray; a helm reports a
3038
+ * pose (position + orientation).
3039
+ *
3040
+ * Family placement (peer of PoseTrack, NOT a Track subclass — no timeline):
3041
+ * feed(translation, rotation) push the latest raw device rate (input, as Track.add)
3042
+ * step(out, dt, basis) integrate the rate by dt into the pose (BRIDGE-DRIVEN,
3043
+ * as Track.tick) — writes the new pose into out
3044
+ * eval(out) read the current { pos, rot } (zero-alloc, as Track.eval)
3045
+ * home(pose?) re-home the integrated pose (NOT reset — no keyframes to clear)
3046
+ *
3047
+ * Quaternion algebra is provided by quat.js. Out-first throughout; no allocation
3048
+ * in feed/step/eval. Storage convention (matches the rest of the core): vec3 and
3049
+ * quat state are plain number[] (f64) — the same shape as track.js keyframes and
3050
+ * the frozen basis-vector constants.
3051
+ *
3052
+ * ── Frame discipline (`from` / `basis`) ──────────────────────────────────────
3053
+ * The space the rates are interpreted in is named by `from` (WORLD | EYE | mat4),
3054
+ * a declaration the BRIDGE reads — the core never learns about a camera. The
3055
+ * resolved orientation arrives per-step as `basis`: an eye→world mat4 (or null
3056
+ * for WORLD ≡ the identity basis). Both the linear and angular rates are rotated
3057
+ * through that basis, then the quaternion is composed world-frame. This is the
3058
+ * single code path that covers both manipulation conventions:
3059
+ *
3060
+ * - camera body-fly — basis is the driven camera's own eye matrix (which this
3061
+ * helm produced last frame ⇒ equals the integrated `q`,
3062
+ * zero staleness); rotating a body delta through `q` then
3063
+ * world-composing is algebraically a body-frame compose.
3064
+ * - screen-relative — basis is the *viewing* camera's eye matrix (an external
3065
+ * frame); a push moves the target relative to the screen.
3066
+ *
3067
+ * Per-channel `from` ({ translation, rotation }) is a deferred, non-breaking
3068
+ * extension (one basis suffices today — pose-helm-design.md §8.3).
3069
+ */
3070
+
3071
+
3072
+ // =========================================================================
3073
+ // Module-level scratch — shared across helm instances (non-reentrant; step
3074
+ // is synchronous and called at most once per helm per frame).
3075
+ // =========================================================================
3076
+
3077
+ const _dq = [0, 0, 0, 1];
3078
+
3079
+ // Deadzone gate — |v| > dz keeps v, else 0. Module-level so step allocates no
3080
+ // closure. Strictly-greater matches the e7 reference (rest reads exact 0).
3081
+ const _dz = (v, dz) => (v > dz || v < -dz) ? v : 0;
3082
+
3083
+ /**
3084
+ * Build the canonical default profile. SpaceNavigator-tuned lane permutation
3085
+ * and the eye-frame manipulation feel found in the e7 experiments; it is the
3086
+ * per-app / per-device mapping layer and is meant to be overwritten wholesale
3087
+ * (`helm.profile = { … }`) for a different transport.
3088
+ *
3089
+ * sign — per-app direction (camera-fly vs object-grab invert; flip live).
3090
+ * sens — per-axis sensitivity (tame roll without touching the rest).
3091
+ * lane — input-channel permutation: which fed channel drives this DOF.
3092
+ * T* lanes index the translation triple; R* lanes the rotation triple.
3093
+ */
3094
+ function _defaultProfile() {
3095
+ return {
3096
+ Tx: { sign: 1, sens: 0.30, lane: 0 },
3097
+ Ty: { sign: 1, sens: 0.30, lane: 2 },
3098
+ Tz: { sign: -1, sens: 0.30, lane: 1 },
3099
+ Rp: { sign: -1, sens: 0.0025, lane: 0 },
3100
+ Ry: { sign: -1, sens: 0.0025, lane: 2 },
3101
+ Rr: { sign: 1, sens: 0.0018, lane: 1 },
3102
+ };
3103
+ }
3104
+
3105
+ /** Channel order — readout / iteration convenience (Tx Ty Tz Rp Ry Rr). */
3106
+ const HELM_CHANNELS = Object.freeze(['Tx', 'Ty', 'Tz', 'Rp', 'Ry', 'Rr']);
3107
+
3108
+ // =========================================================================
3109
+ // PoseHelm
3110
+ // =========================================================================
3111
+
3112
+ /**
3113
+ * Source-agnostic 6-DOF pose producer. Holds a profile + the integrated pose;
3114
+ * no timeline (no keyframes, no play/seek/loop).
3115
+ */
3116
+ class PoseHelm {
3117
+ constructor() {
3118
+ /**
3119
+ * DOF mapping table. Six channels, each `{ sign, sens, lane }`.
3120
+ * Public and mutable — set wholesale to re-map a device or app.
3121
+ * @type {{ Tx:Object, Ty:Object, Tz:Object, Rp:Object, Ry:Object, Rr:Object }}
3122
+ */
3123
+ this.profile = _defaultProfile();
3124
+
3125
+ /**
3126
+ * Rest-drift floor. A fed rate with |value| ≤ deadzone reads as 0. Devices
3127
+ * that rest at exact 0 need only a small floor. @type {number}
3128
+ */
3129
+ this.deadzone = 8;
3130
+
3131
+ /**
3132
+ * The space fed rates are interpreted in: a space constant (WORLD | EYE) or
3133
+ * a mat4 frame. Declarative only — the bridge reads this to resolve the
3134
+ * per-step `basis`; the core stays camera-agnostic. @type {string|ArrayLike<number>}
3135
+ */
3136
+ this.from = 'EYE';
3137
+
3138
+ // Latest fed raw rates (refreshed by feed; persist until the next feed —
3139
+ // the transport is responsible for feeding 0 when motion should stop).
3140
+ this._lin = [0, 0, 0];
3141
+ this._ang = [0, 0, 0];
3142
+
3143
+ // Integrated pose — the source of truth.
3144
+ this._pos = [0, 0, 0];
3145
+ this._q = [0, 0, 0, 1];
3146
+ }
3147
+
3148
+ /**
3149
+ * Push the latest raw 6-DOF device rate. Either argument may be omitted to
3150
+ * leave that half unchanged (devices report the two halves on separate
3151
+ * frames). The value persists until the next feed. Raw device units — the
3152
+ * profile's `sens` does the scaling, so the same feed() suits any transport.
3153
+ *
3154
+ * @param {ArrayLike<number>} [translation] [tx, ty, tz] raw lane rates.
3155
+ * @param {ArrayLike<number>} [rotation] [rx, ry, rz] raw lane rates.
3156
+ * @returns {PoseHelm} this
3157
+ */
3158
+ feed(translation, rotation) {
3159
+ if (translation) {
3160
+ this._lin[0] = translation[0] || 0;
3161
+ this._lin[1] = translation[1] || 0;
3162
+ this._lin[2] = translation[2] || 0;
3163
+ }
3164
+ if (rotation) {
3165
+ this._ang[0] = rotation[0] || 0;
3166
+ this._ang[1] = rotation[1] || 0;
3167
+ this._ang[2] = rotation[2] || 0;
3168
+ }
3169
+ return this;
3170
+ }
3171
+
3172
+ /**
3173
+ * Integrate the latest fed rate by `dt` seconds into the pose, then write the
3174
+ * pose into `out`. Bridge-driven (the player calls it each frame; a sketch
3175
+ * never does, as it never calls track.tick()).
3176
+ *
3177
+ * `basis` is the resolved `from` frame: an eye→world column-major mat4
3178
+ * (16-element ArrayLike), or null/omitted for WORLD (the identity basis). Its
3179
+ * columns supply the right/up/back axes; forward is −col2. Both the linear and
3180
+ * angular rates are rotated through it, then `q` is composed world-frame.
3181
+ *
3182
+ * Zero-allocation. `out` is `{ pos:number[3], rot:number[4] }`; omit it for a
3183
+ * fresh object.
3184
+ *
3185
+ * @param {{ pos:number[], rot:number[] }} [out]
3186
+ * @param {number} dt Elapsed time in seconds.
3187
+ * @param {ArrayLike<number>} [basis] Eye→world mat4, or null for WORLD.
3188
+ * @returns {{ pos:number[], rot:number[] }} out
3189
+ */
3190
+ step(out, dt, basis) {
3191
+ const p = this.profile, dz = this.deadzone;
3192
+ const lin = this._lin, ang = this._ang;
3193
+
3194
+ // Basis axes (right / up / forward). No basis ⇒ the identity eye matrix:
3195
+ // right +X, up +Y, forward −Z. Forward is −col2 (an eye→world matrix stores
3196
+ // the camera's BACK in col2), and the identity's col2 is +Z, so forward is
3197
+ // −Z — making step(out, dt, null) identical to step(out, dt, IDENTITY_MAT4),
3198
+ // as the `from` contract ("null ≡ the identity basis") promises.
3199
+ let rX = 1, rY = 0, rZ = 0;
3200
+ let uX = 0, uY = 1, uZ = 0;
3201
+ let fX = 0, fY = 0, fZ = -1;
3202
+ if (basis) {
3203
+ rX = basis[0]; rY = basis[1]; rZ = basis[2];
3204
+ uX = basis[4]; uY = basis[5]; uZ = basis[6];
3205
+ fX = -basis[8]; fY = -basis[9]; fZ = -basis[10];
3206
+ }
3207
+
3208
+ // Angular: three rates → one delta quat, rotated into world, world-composed.
3209
+ const wx = _dz(ang[p.Rp.lane], dz) * p.Rp.sign * p.Rp.sens;
3210
+ const wy = _dz(ang[p.Ry.lane], dz) * p.Ry.sign * p.Ry.sens;
3211
+ const wz = _dz(ang[p.Rr.lane], dz) * p.Rr.sign * p.Rr.sens;
3212
+ const w = Math.sqrt(wx * wx + wy * wy + wz * wz);
3213
+ if (w > 0) {
3214
+ // Axis in world = basis · (wx,wy,wz); its length is w (basis orthonormal),
3215
+ // so the rotation angle is w·dt. qFromAxisAngle renormalizes the axis.
3216
+ const ax = wx * rX + wy * uX + wz * fX;
3217
+ const ay = wx * rY + wy * uY + wz * fY;
3218
+ const az = wx * rZ + wy * uZ + wz * fZ;
3219
+ qFromAxisAngle(_dq, ax, ay, az, w * dt);
3220
+ qMul(this._q, _dq, this._q); // delta already in world → world-frame compose
3221
+ qNormalize(this._q);
3222
+ }
3223
+
3224
+ // Linear: three rates rotated into world, integrated by dt.
3225
+ const tx = _dz(lin[p.Tx.lane], dz) * p.Tx.sign * p.Tx.sens;
3226
+ const ty = _dz(lin[p.Ty.lane], dz) * p.Ty.sign * p.Ty.sens;
3227
+ const tz = _dz(lin[p.Tz.lane], dz) * p.Tz.sign * p.Tz.sens;
3228
+ this._pos[0] += (tx * rX + ty * uX + tz * fX) * dt;
3229
+ this._pos[1] += (tx * rY + ty * uY + tz * fY) * dt;
3230
+ this._pos[2] += (tx * rZ + ty * uZ + tz * fZ) * dt;
3231
+
3232
+ return this.eval(out);
3233
+ }
3234
+
3235
+ /**
3236
+ * Read the current pose into `out` (zero-alloc). The shared output contract
3237
+ * that feeds applyPose — mirrors Track.eval.
3238
+ *
3239
+ * @param {{ pos:number[], rot:number[] }} [out]
3240
+ * @returns {{ pos:number[], rot:number[] }} out
3241
+ */
3242
+ eval(out) {
3243
+ out = out || { pos: [0, 0, 0], rot: [0, 0, 0, 1] };
3244
+ out.pos[0] = this._pos[0]; out.pos[1] = this._pos[1]; out.pos[2] = this._pos[2];
3245
+ out.rot[0] = this._q[0]; out.rot[1] = this._q[1];
3246
+ out.rot[2] = this._q[2]; out.rot[3] = this._q[3];
3247
+ return out;
3248
+ }
3249
+
3250
+ /**
3251
+ * Write the six effective channel rates (post deadzone·sign·sens) into `out6`,
3252
+ * in channel order [Tx, Ty, Tz, Rp, Ry, Rr]. The gizmo reads this to light the
3253
+ * DOFs being driven; readouts can show signed magnitudes. Zero-alloc.
3254
+ *
3255
+ * @param {number[]} out6 6-element destination.
3256
+ * @returns {number[]} out6
3257
+ */
3258
+ activity(out6) {
3259
+ const p = this.profile, dz = this.deadzone, lin = this._lin, ang = this._ang;
3260
+ out6[0] = _dz(lin[p.Tx.lane], dz) * p.Tx.sign * p.Tx.sens;
3261
+ out6[1] = _dz(lin[p.Ty.lane], dz) * p.Ty.sign * p.Ty.sens;
3262
+ out6[2] = _dz(lin[p.Tz.lane], dz) * p.Tz.sign * p.Tz.sens;
3263
+ out6[3] = _dz(ang[p.Rp.lane], dz) * p.Rp.sign * p.Rp.sens;
3264
+ out6[4] = _dz(ang[p.Ry.lane], dz) * p.Ry.sign * p.Ry.sens;
3265
+ out6[5] = _dz(ang[p.Rr.lane], dz) * p.Rr.sign * p.Rr.sens;
3266
+ return out6;
3267
+ }
3268
+
3269
+ /**
3270
+ * Re-home the integrated pose. Sets position + orientation to `pose` (or the
3271
+ * identity pose when omitted) and clears any pending rate so the helm rests at
3272
+ * a clean, known state. NOT a reset — there are no keyframes to clear.
3273
+ *
3274
+ * @param {{ pos?:ArrayLike<number>, rot?:ArrayLike<number> }} [pose]
3275
+ * @returns {PoseHelm} this
3276
+ */
3277
+ home(pose) {
3278
+ if (pose && pose.pos) {
3279
+ this._pos[0] = pose.pos[0]; this._pos[1] = pose.pos[1]; this._pos[2] = pose.pos[2];
3280
+ } else {
3281
+ this._pos[0] = 0; this._pos[1] = 0; this._pos[2] = 0;
3282
+ }
3283
+ if (pose && pose.rot) {
3284
+ this._q[0] = pose.rot[0]; this._q[1] = pose.rot[1];
3285
+ this._q[2] = pose.rot[2]; this._q[3] = pose.rot[3];
3286
+ } else {
3287
+ this._q[0] = 0; this._q[1] = 0; this._q[2] = 0; this._q[3] = 1;
3288
+ }
3289
+ this._lin[0] = this._lin[1] = this._lin[2] = 0;
3290
+ this._ang[0] = this._ang[1] = this._ang[2] = 0;
3291
+ return this;
3292
+ }
3293
+ }
3294
+
3024
3295
  /**
3025
3296
  * @file Frustum planes and visibility tests — zero allocations.
3026
3297
  * @module tree/visibility
@@ -3180,5 +3451,5 @@ function boxVisibility(planes, x0, y0, z0, x1, y1, z1) {
3180
3451
  return allIn ? VISIBLE : SEMIVISIBLE;
3181
3452
  }
3182
3453
 
3183
- export { AXIS, CameraTrack, Constraint, DIAL, DIRECTION, EYE, INVISIBLE, MATRIX, MODEL, NDC, ORIGIN, PLANE, PLANE_BOTTOM, PLANE_FAR, PLANE_LEFT, PLANE_NEAR, PLANE_RIGHT, PLANE_TOP, POINT, PoseTrack, SCREEN, SEMIVISIBLE, SPHERE, VISIBLE, WEBGL, WEBGPU, WORLD, _i, _j, _k, azElFromDir, boxVisibility, createConstraint, dirFromAzEl, distanceToPlane, frustumPlanes, hermiteVec3, i, j, k, lerpVec3, mapDirection, mapLocation, mat3Direction, mat3NormalFromMat4, mat4Bias, mat4Eye, mat4FromBasis, mat4FromScale, mat4FromTRS, mat4FromTranslation, mat4Invert, mat4Location, mat4MV, mat4Mul, mat4MulDir, mat4MulPoint, mat4Ortho, mat4PV, mat4Persp, mat4Pick, mat4Reflect, mat4ToRotation, mat4ToScale, mat4ToTransform, mat4ToTranslation, mat4View, pixelRatio, pointVisibility, projBottom, projFar, projFov, projHfov, projIsOrtho, projLeft, projNear, projRight, projTop, qConjugate, qCopy, qDot, qFromAxisAngle, qFromLookDir, qFromMat4, qFromRotMat3x3, qFromUnitVectors, qMul, qNegate, qNlerp, qNormalize, qRotateVec3, qSet, qSlerp, qToAxisAngle, qToMat4, rayClosestPointOnAxis, rayPlane, raySphere, sphereVisibility, transformToMat4 };
3454
+ export { AXIS, CameraTrack, Constraint, DIAL, DIRECTION, EYE, HELM_CHANNELS, INVISIBLE, MATRIX, MODEL, NDC, ORIGIN, PLANE, PLANE_BOTTOM, PLANE_FAR, PLANE_LEFT, PLANE_NEAR, PLANE_RIGHT, PLANE_TOP, POINT, PoseHelm, PoseTrack, SCREEN, SELF, SEMIVISIBLE, SPHERE, VISIBLE, WEBGL, WEBGPU, WORLD, _i, _j, _k, azElFromDir, boxVisibility, createConstraint, dirFromAzEl, distanceToPlane, frustumPlanes, hermiteVec3, i, j, k, lerpVec3, mapDirection, mapLocation, mat3Direction, mat3NormalFromMat4, mat4Bias, mat4Eye, mat4FromBasis, mat4FromScale, mat4FromTRS, mat4FromTranslation, mat4Invert, mat4Location, mat4MV, mat4Mul, mat4MulDir, mat4MulPoint, mat4Ortho, mat4PV, mat4Persp, mat4Pick, mat4Reflect, mat4ToRotation, mat4ToScale, mat4ToTransform, mat4ToTranslation, mat4View, pixelRatio, pointVisibility, projBottom, projFar, projFov, projHfov, projIsOrtho, projLeft, projNear, projRight, projTop, qConjugate, qCopy, qDot, qFromAxisAngle, qFromLookDir, qFromMat4, qFromRotMat3x3, qFromUnitVectors, qMul, qNegate, qNlerp, qNormalize, qRotateVec3, qSet, qSlerp, qToAxisAngle, qToMat4, rayClosestPointOnAxis, rayPlane, raySphere, sphereVisibility, transformToMat4 };
3184
3455
  //# sourceMappingURL=index.js.map