@nakednous/tree 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -65,45 +65,36 @@ const _k = Object.freeze([0, 0, -1]);
65
65
  *
66
66
  * Every function uses only stack locals for intermediates (zero shared state).
67
67
  * Every mutating function writes to a caller-provided `out` and returns `out`.
68
+ * Returns null on degeneracy (singular matrix, etc.).
68
69
  */
69
70
 
70
71
 
71
72
  // ═══════════════════════════════════════════════════════════════════════════
72
- // Mat4 operations
73
+ // Mat4 math
73
74
  // ═══════════════════════════════════════════════════════════════════════════
74
75
 
75
- /** out = identity 4×4 */
76
- function mat4Identity(out) {
77
- out[0]=1;out[1]=0;out[2]=0;out[3]=0;
78
- out[4]=0;out[5]=1;out[6]=0;out[7]=0;
79
- out[8]=0;out[9]=0;out[10]=1;out[11]=0;
80
- out[12]=0;out[13]=0;out[14]=0;out[15]=1;
81
- return out;
82
- }
83
-
84
- /** out = a * b (column-major) */
85
- function mat4Mul(out, a, b) {
86
- const a0=a[0],a1=a[1],a2=a[2],a3=a[3],
87
- a4=a[4],a5=a[5],a6=a[6],a7=a[7],
88
- a8=a[8],a9=a[9],a10=a[10],a11=a[11],
89
- a12=a[12],a13=a[13],a14=a[14],a15=a[15];
90
- let b0,b1,b2,b3;
91
- b0=b[0];b1=b[1];b2=b[2];b3=b[3];
76
+ /** out = A · B (column-major, standard math order) */
77
+ function mat4Mul(out, A, B) {
78
+ const a0=A[0],a1=A[1],a2=A[2],a3=A[3],
79
+ a4=A[4],a5=A[5],a6=A[6],a7=A[7],
80
+ a8=A[8],a9=A[9],a10=A[10],a11=A[11],
81
+ a12=A[12],a13=A[13],a14=A[14],a15=A[15];
82
+ let b0=B[0],b1=B[1],b2=B[2],b3=B[3];
92
83
  out[0]=a0*b0+a4*b1+a8*b2+a12*b3;
93
84
  out[1]=a1*b0+a5*b1+a9*b2+a13*b3;
94
85
  out[2]=a2*b0+a6*b1+a10*b2+a14*b3;
95
86
  out[3]=a3*b0+a7*b1+a11*b2+a15*b3;
96
- b0=b[4];b1=b[5];b2=b[6];b3=b[7];
87
+ b0=B[4];b1=B[5];b2=B[6];b3=B[7];
97
88
  out[4]=a0*b0+a4*b1+a8*b2+a12*b3;
98
89
  out[5]=a1*b0+a5*b1+a9*b2+a13*b3;
99
90
  out[6]=a2*b0+a6*b1+a10*b2+a14*b3;
100
91
  out[7]=a3*b0+a7*b1+a11*b2+a15*b3;
101
- b0=b[8];b1=b[9];b2=b[10];b3=b[11];
92
+ b0=B[8];b1=B[9];b2=B[10];b3=B[11];
102
93
  out[8]=a0*b0+a4*b1+a8*b2+a12*b3;
103
94
  out[9]=a1*b0+a5*b1+a9*b2+a13*b3;
104
95
  out[10]=a2*b0+a6*b1+a10*b2+a14*b3;
105
96
  out[11]=a3*b0+a7*b1+a11*b2+a15*b3;
106
- b0=b[12];b1=b[13];b2=b[14];b3=b[15];
97
+ b0=B[12];b1=B[13];b2=B[14];b3=B[15];
107
98
  out[12]=a0*b0+a4*b1+a8*b2+a12*b3;
108
99
  out[13]=a1*b0+a5*b1+a9*b2+a13*b3;
109
100
  out[14]=a2*b0+a6*b1+a10*b2+a14*b3;
@@ -111,7 +102,7 @@ function mat4Mul(out, a, b) {
111
102
  return out;
112
103
  }
113
104
 
114
- /** out = inverse(src). Returns null if singular. */
105
+ /** out = inverse(src). Returns null if singular. */
115
106
  function mat4Invert(out, src) {
116
107
  const s=src;
117
108
  const a00=s[0],a01=s[1],a02=s[2],a03=s[3],
@@ -202,32 +193,8 @@ function mat4MulPoint(out, m, x, y, z) {
202
193
  return out;
203
194
  }
204
195
 
205
- /** out = mat4 * [x,y,z,0] (direction, no translation) */
206
- function mat4MulDir(out, m, x, y, z) {
207
- out[0] = m[0]*x + m[4]*y + m[8]*z;
208
- out[1] = m[1]*x + m[5]*y + m[9]*z;
209
- out[2] = m[2]*x + m[6]*y + m[10]*z;
210
- return out;
211
- }
212
-
213
- /** out = upper-left 3×3 transposed from mat4 (direction / dMatrix extraction) */
214
- function mat3FromMat4T(out, m) {
215
- out[0]=m[0]; out[1]=m[4]; out[2]=m[8];
216
- out[3]=m[1]; out[4]=m[5]; out[5]=m[9];
217
- out[6]=m[2]; out[7]=m[6]; out[8]=m[10];
218
- return out;
219
- }
220
-
221
- /** out = mat3 * vec3 */
222
- function mat3MulVec3(out, m, x, y, z) {
223
- out[0] = m[0]*x + m[3]*y + m[6]*z;
224
- out[1] = m[1]*x + m[4]*y + m[7]*z;
225
- out[2] = m[2]*x + m[5]*y + m[8]*z;
226
- return out;
227
- }
228
-
229
196
  // ═══════════════════════════════════════════════════════════════════════════
230
- // Projection queries
197
+ // Projection queries (read scalars from a projection mat4)
231
198
  // ═══════════════════════════════════════════════════════════════════════════
232
199
 
233
200
  /** @returns {boolean} true if orthographic */
@@ -235,8 +202,8 @@ function projIsOrtho(p) { return p[15] !== 0; }
235
202
 
236
203
  /**
237
204
  * Near plane distance.
238
- * @param {ArrayLike<number>} p Projection Mat4
239
- * @param {number} ndcZMin WEBGL (−1) or WEBGPU (0)
205
+ * @param {ArrayLike<number>} p Projection Mat4.
206
+ * @param {number} ndcZMin WEBGL (−1) or WEBGPU (0).
240
207
  */
241
208
  function projNear(p, ndcZMin) {
242
209
  return p[15] === 0
@@ -289,107 +256,34 @@ function projHfov(p) {
289
256
  // Derived matrices (convenience)
290
257
  // ═══════════════════════════════════════════════════════════════════════════
291
258
 
292
- /** out = proj * view = P · V (standard GL) */
259
+ /** out = P · V */
293
260
  function mat4PV(out, proj, view) { return mat4Mul(out, proj, view); }
294
261
 
295
- /** out = view * model = V · M (standard GL) */
262
+ /** out = V · M */
296
263
  function mat4MV(out, model, view) { return mat4Mul(out, view, model); }
297
264
 
298
- /** out = proj * view * model = P · V · M (standard GL) */
299
- function mat4PMV(out, proj, model, view) {
300
- // MV = view * model (V · M)
301
- const t0=view[0],t1=view[1],t2=view[2],t3=view[3],
302
- t4=view[4],t5=view[5],t6=view[6],t7=view[7],
303
- t8=view[8],t9=view[9],t10=view[10],t11=view[11],
304
- t12=view[12],t13=view[13],t14=view[14],t15=view[15];
305
- let b0,b1,b2,b3;
306
- b0=model[0];b1=model[1];b2=model[2];b3=model[3];
307
- const mv0=t0*b0+t4*b1+t8*b2+t12*b3, mv1=t1*b0+t5*b1+t9*b2+t13*b3,
308
- mv2=t2*b0+t6*b1+t10*b2+t14*b3, mv3=t3*b0+t7*b1+t11*b2+t15*b3;
309
- b0=model[4];b1=model[5];b2=model[6];b3=model[7];
310
- const mv4=t0*b0+t4*b1+t8*b2+t12*b3, mv5=t1*b0+t5*b1+t9*b2+t13*b3,
311
- mv6=t2*b0+t6*b1+t10*b2+t14*b3, mv7=t3*b0+t7*b1+t11*b2+t15*b3;
312
- b0=model[8];b1=model[9];b2=model[10];b3=model[11];
313
- const mv8=t0*b0+t4*b1+t8*b2+t12*b3, mv9=t1*b0+t5*b1+t9*b2+t13*b3,
314
- mv10=t2*b0+t6*b1+t10*b2+t14*b3, mv11=t3*b0+t7*b1+t11*b2+t15*b3;
315
- b0=model[12];b1=model[13];b2=model[14];b3=model[15];
316
- const mv12=t0*b0+t4*b1+t8*b2+t12*b3, mv13=t1*b0+t5*b1+t9*b2+t13*b3,
317
- mv14=t2*b0+t6*b1+t10*b2+t14*b3, mv15=t3*b0+t7*b1+t11*b2+t15*b3;
318
- // PMV = proj * MV (P · V · M)
319
- const p0=proj[0],p1=proj[1],p2=proj[2],p3=proj[3],
320
- p4=proj[4],p5=proj[5],p6=proj[6],p7=proj[7],
321
- p8=proj[8],p9=proj[9],p10=proj[10],p11=proj[11],
322
- p12=proj[12],p13=proj[13],p14=proj[14],p15=proj[15];
323
- out[0]=p0*mv0+p4*mv1+p8*mv2+p12*mv3;
324
- out[1]=p1*mv0+p5*mv1+p9*mv2+p13*mv3;
325
- out[2]=p2*mv0+p6*mv1+p10*mv2+p14*mv3;
326
- out[3]=p3*mv0+p7*mv1+p11*mv2+p15*mv3;
327
- out[4]=p0*mv4+p4*mv5+p8*mv6+p12*mv7;
328
- out[5]=p1*mv4+p5*mv5+p9*mv6+p13*mv7;
329
- out[6]=p2*mv4+p6*mv5+p10*mv6+p14*mv7;
330
- out[7]=p3*mv4+p7*mv5+p11*mv6+p15*mv7;
331
- out[8]=p0*mv8+p4*mv9+p8*mv10+p12*mv11;
332
- out[9]=p1*mv8+p5*mv9+p9*mv10+p13*mv11;
333
- out[10]=p2*mv8+p6*mv9+p10*mv10+p14*mv11;
334
- out[11]=p3*mv8+p7*mv9+p11*mv10+p15*mv11;
335
- out[12]=p0*mv12+p4*mv13+p8*mv14+p12*mv15;
336
- out[13]=p1*mv12+p5*mv13+p9*mv14+p13*mv15;
337
- out[14]=p2*mv12+p6*mv13+p10*mv14+p14*mv15;
338
- out[15]=p3*mv12+p7*mv13+p11*mv14+p15*mv15;
339
- return out;
340
- }
341
-
342
265
  // ═══════════════════════════════════════════════════════════════════════════
343
- // Space transforms mapLocation / mapDirection
266
+ // Location / Direction transforms
344
267
  // ═══════════════════════════════════════════════════════════════════════════
345
- //
346
- // FLAT DISPATCH: every from→to pair is a self-contained leaf.
347
- // No path calls back into mapLocation/mapDirection (no reentrancy).
348
- // All intermediates are stack locals (zero shared state).
349
- //
350
-
351
- // ── Location Transform ───────────────────────────────────────────────────
352
268
 
353
269
  /**
354
- * Relative transform for locations (points).
355
- *
356
- * out = inv(to) · from
357
- *
358
- * Maps a point from the `from` frame into the `to` frame:
359
- *
360
- * p_to = out · p_from
361
- *
362
- * @param {ArrayLike<number>} out Destination 4×4 matrix (length 16).
363
- * @param {ArrayLike<number>} from Source frame transform.
364
- * @param {ArrayLike<number>} to Destination frame transform.
365
- * @returns {ArrayLike<number>|null} `out`, or `null` if `to` is singular.
270
+ * Relative transform for locations (points): out = inv(to) · from.
271
+ * @param {ArrayLike<number>} out 16-element destination.
272
+ * @param {ArrayLike<number>} from Source frame transform.
273
+ * @param {ArrayLike<number>} to Destination frame transform.
274
+ * @returns {ArrayLike<number>|null} out, or null if to is singular.
366
275
  */
367
276
  function mat4Location(out, from, to) {
368
277
  return mat4Invert(out, to) && mat4Mul(out, out, from);
369
278
  }
370
279
 
371
- // ── Direction Transform ──────────────────────────────────────────────────
372
-
373
280
  /**
374
- * Relative transform for directions (vectors).
375
- *
281
+ * Relative transform for directions (vectors): out = to₃ · inv(from₃).
376
282
  * Uses only the upper-left 3×3 blocks, ignoring translation.
377
- *
378
- * Corresponds to:
379
- *
380
- * out = to₃ · inv(from₃)
381
- *
382
- * and maps directions as:
383
- *
384
- * d_to = out · d_from
385
- *
386
- * Note: the final write is transposed so the result matches this module's
387
- * matrix layout and multiplication convention.
388
- *
389
- * @param {ArrayLike<number>} out Destination 3×3 matrix (length 9).
390
- * @param {ArrayLike<number>} from Source frame transform.
391
- * @param {ArrayLike<number>} to Destination frame transform.
392
- * @returns {ArrayLike<number>|null} `out`, or `null` if `from` is singular.
283
+ * @param {ArrayLike<number>} out 9-element destination.
284
+ * @param {ArrayLike<number>} from Source frame transform.
285
+ * @param {ArrayLike<number>} to Destination frame transform.
286
+ * @returns {ArrayLike<number>|null} out, or null if from is singular.
393
287
  */
394
288
  function mat3Direction(out, from, to) {
395
289
  const a00=from[0], a01=from[1], a02=from[2],
@@ -401,33 +295,41 @@ function mat3Direction(out, from, to) {
401
295
  let det=a00*b01+a01*b11+a02*b21;
402
296
  if (Math.abs(det) < 1e-12) return null;
403
297
  det=1/det;
404
- const i00=b01*det;
405
- const i01=(a02*a21-a22*a01)*det;
406
- const i02=(a12*a01-a02*a11)*det;
407
- const i10=b11*det;
408
- const i11=(a22*a00-a02*a20)*det;
409
- const i12=(a02*a10-a12*a00)*det;
410
- const i20=b21*det;
411
- const i21=(a01*a20-a21*a00)*det;
412
- const i22=(a11*a00-a01*a10)*det;
298
+ const i00=b01*det, i01=(a02*a21-a22*a01)*det, i02=(a12*a01-a02*a11)*det;
299
+ const i10=b11*det, i11=(a22*a00-a02*a20)*det, i12=(a02*a10-a12*a00)*det;
300
+ const i20=b21*det, i21=(a01*a20-a21*a00)*det, i22=(a11*a00-a01*a10)*det;
413
301
  const t00=to[0], t01=to[1], t02=to[2],
414
302
  t10=to[4], t11=to[5], t12=to[6],
415
303
  t20=to[8], t21=to[9], t22=to[10];
416
- const m00=t00*i00+t10*i01+t20*i02;
417
- const m01=t01*i00+t11*i01+t21*i02;
418
- const m02=t02*i00+t12*i01+t22*i02;
419
- const m10=t00*i10+t10*i11+t20*i12;
420
- const m11=t01*i10+t11*i11+t21*i12;
421
- const m12=t02*i10+t12*i11+t22*i12;
422
- const m20=t00*i20+t10*i21+t20*i22;
423
- const m21=t01*i20+t11*i21+t21*i22;
424
- const m22=t02*i20+t12*i21+t22*i22;
304
+ const m00=t00*i00+t10*i01+t20*i02, m01=t01*i00+t11*i01+t21*i02, m02=t02*i00+t12*i01+t22*i02;
305
+ const m10=t00*i10+t10*i11+t20*i12, m11=t01*i10+t11*i11+t21*i12, m12=t02*i10+t12*i11+t22*i12;
306
+ const m20=t00*i20+t10*i21+t20*i22, m21=t01*i20+t11*i21+t21*i22, m22=t02*i20+t12*i21+t22*i22;
425
307
  out[0]=m00; out[1]=m10; out[2]=m20;
426
308
  out[3]=m01; out[4]=m11; out[5]=m21;
427
309
  out[6]=m02; out[7]=m12; out[8]=m22;
428
310
  return out;
429
311
  }
430
312
 
313
+ // ═══════════════════════════════════════════════════════════════════════════
314
+ // Space transforms — mapLocation / mapDirection
315
+ // ═══════════════════════════════════════════════════════════════════════════
316
+ //
317
+ // FLAT DISPATCH: every from→to pair is a self-contained leaf.
318
+ // No path calls back into mapLocation/mapDirection (no reentrancy).
319
+ // All intermediates are stack locals (zero shared state).
320
+ //
321
+ // Matrices bag m:
322
+ // {
323
+ // pMatrix: Float32Array(16) — projection (eye → clip)
324
+ // vMatrix: Float32Array(16) — view (world → eye)
325
+ // eMatrix?: Float32Array(16) — eye (eye → world, inv view); lazy
326
+ // pvMatrix?: Float32Array(16) — P · V; lazy
327
+ // ipvMatrix?:Float32Array(16) — inv(P · V); lazy
328
+ // fromFrame?:Float32Array(16) — MATRIX source frame (custom space)
329
+ // toFrameInv?:Float32Array(16) — inv(MATRIX dest frame)
330
+ // }
331
+ //
332
+
431
333
  // ── Location leaf helpers ────────────────────────────────────────────────
432
334
 
433
335
  function _worldToScreen(out, px, py, pz, pv, vp, ndcZMin) {
@@ -435,45 +337,43 @@ function _worldToScreen(out, px, py, pz, pv, vp, ndcZMin) {
435
337
  const y = pv[1]*px+pv[5]*py+pv[9]*pz+pv[13];
436
338
  const z = pv[2]*px+pv[6]*py+pv[10]*pz+pv[14];
437
339
  const w = pv[3]*px+pv[7]*py+pv[11]*pz+pv[15];
438
- if (w === 0) { out[0]=px; out[1]=py; out[2]=pz; return out; }
439
- const nx=x/w, ny=y/w, nz=z/w;
340
+ const xi = (w !== 0 && w !== 1) ? x/w : x;
341
+ const yi = (w !== 0 && w !== 1) ? y/w : y;
342
+ const zi = (w !== 0 && w !== 1) ? z/w : z;
440
343
  const ndcZRange = 1 - ndcZMin;
441
- out[0] = (nx*0.5+0.5)*vp[2]+vp[0];
442
- out[1] = (ny*0.5+0.5)*vp[3]+vp[1];
443
- out[2] = (nz - ndcZMin) / ndcZRange;
344
+ out[0] = (xi*0.5+0.5)*vp[2]+vp[0];
345
+ out[1] = (yi*0.5+0.5)*vp[3]+vp[1];
346
+ out[2] = (zi - ndcZMin) / ndcZRange;
444
347
  return out;
445
348
  }
446
349
 
447
350
  function _screenToWorld(out, px, py, pz, ipv, vp, ndcZMin) {
448
- const sx=(px-vp[0])/vp[2], sy=(py-vp[1])/vp[3];
449
- const nx=sx*2-1, ny=sy*2-1;
450
351
  const ndcZRange = 1 - ndcZMin;
352
+ const nx = ((px-vp[0])/vp[2])*2-1;
353
+ const ny = ((py-vp[1])/vp[3])*2-1;
451
354
  const nz = pz * ndcZRange + ndcZMin;
452
- const x=ipv[0]*nx+ipv[4]*ny+ipv[8]*nz+ipv[12];
453
- const y=ipv[1]*nx+ipv[5]*ny+ipv[9]*nz+ipv[13];
454
- const z=ipv[2]*nx+ipv[6]*ny+ipv[10]*nz+ipv[14];
455
- const w=ipv[3]*nx+ipv[7]*ny+ipv[11]*nz+ipv[15];
456
- if (w === 0) { out[0]=px; out[1]=py; out[2]=pz; return out; }
355
+ const x = ipv[0]*nx+ipv[4]*ny+ipv[8]*nz+ipv[12];
356
+ const y = ipv[1]*nx+ipv[5]*ny+ipv[9]*nz+ipv[13];
357
+ const z = ipv[2]*nx+ipv[6]*ny+ipv[10]*nz+ipv[14];
358
+ const w = ipv[3]*nx+ipv[7]*ny+ipv[11]*nz+ipv[15];
457
359
  out[0]=x/w; out[1]=y/w; out[2]=z/w;
458
360
  return out;
459
361
  }
460
362
 
461
363
  function _worldToNDC(out, px, py, pz, pv) {
462
- const x=pv[0]*px+pv[4]*py+pv[8]*pz+pv[12];
463
- const y=pv[1]*px+pv[5]*py+pv[9]*pz+pv[13];
464
- const z=pv[2]*px+pv[6]*py+pv[10]*pz+pv[14];
465
- const w=pv[3]*px+pv[7]*py+pv[11]*pz+pv[15];
466
- if (w === 0) { out[0]=px; out[1]=py; out[2]=pz; return out; }
364
+ const x = pv[0]*px+pv[4]*py+pv[8]*pz+pv[12];
365
+ const y = pv[1]*px+pv[5]*py+pv[9]*pz+pv[13];
366
+ const z = pv[2]*px+pv[6]*py+pv[10]*pz+pv[14];
367
+ const w = pv[3]*px+pv[7]*py+pv[11]*pz+pv[15];
467
368
  out[0]=x/w; out[1]=y/w; out[2]=z/w;
468
369
  return out;
469
370
  }
470
371
 
471
372
  function _ndcToWorld(out, px, py, pz, ipv) {
472
- const x=ipv[0]*px+ipv[4]*py+ipv[8]*pz+ipv[12];
473
- const y=ipv[1]*px+ipv[5]*py+ipv[9]*pz+ipv[13];
474
- const z=ipv[2]*px+ipv[6]*py+ipv[10]*pz+ipv[14];
475
- const w=ipv[3]*px+ipv[7]*py+ipv[11]*pz+ipv[15];
476
- if (w === 0) { out[0]=px; out[1]=py; out[2]=pz; return out; }
373
+ const x = ipv[0]*px+ipv[4]*py+ipv[8]*pz+ipv[12];
374
+ const y = ipv[1]*px+ipv[5]*py+ipv[9]*pz+ipv[13];
375
+ const z = ipv[2]*px+ipv[6]*py+ipv[10]*pz+ipv[14];
376
+ const w = ipv[3]*px+ipv[7]*py+ipv[11]*pz+ipv[15];
477
377
  out[0]=x/w; out[1]=y/w; out[2]=z/w;
478
378
  return out;
479
379
  }
@@ -494,12 +394,11 @@ function _ndcToScreen(out, px, py, pz, vp, ndcZMin) {
494
394
  return out;
495
395
  }
496
396
 
497
- // ── Inline PV and IPV helpers (stack-local, for paths that need them) ────
397
+ // ── _ensurePV return pvMatrix from bag, computing inline if absent ──────
498
398
 
499
399
  function _ensurePV(m) {
500
- if (m.pv) return m.pv;
501
- // Inline P · V (standard GL: clip = P · V · world_point)
502
- const p = m.proj, v = m.view;
400
+ if (m.pvMatrix) return m.pvMatrix;
401
+ const p = m.pMatrix, v = m.vMatrix;
503
402
  return [
504
403
  p[0]*v[0]+p[4]*v[1]+p[8]*v[2]+p[12]*v[3],
505
404
  p[1]*v[0]+p[5]*v[1]+p[9]*v[2]+p[13]*v[3],
@@ -523,27 +422,27 @@ function _ensurePV(m) {
523
422
  /**
524
423
  * Map a point between coordinate spaces.
525
424
  *
526
- * @param {Vec3} out Result written here.
527
- * @param {number} px,py,pz Input point.
528
- * @param {string} from Source space.
529
- * @param {string} to Target space.
530
- * @param {object} m Matrices bag { proj, view, eye?, pv?, ipv?, model?,
531
- * fromFrame?, toFrameInv? }.
532
- * @param {Vec4} vp Viewport [x, y, width, height].
533
- * @param {number} ndcZMin WEBGL (−1) or WEBGPU (0).
425
+ * @param {Vec3} out Result written here.
426
+ * @param {number} px,py,pz Input point.
427
+ * @param {string} from Source space constant.
428
+ * @param {string} to Target space constant.
429
+ * @param {object} m Matrices bag:
430
+ * { pMatrix, vMatrix, eMatrix?, pvMatrix?, ipvMatrix?, fromFrame?, toFrameInv? }
431
+ * @param {Vec4} vp Viewport [x, y, width, height].
432
+ * @param {number} ndcZMin WEBGL (−1) or WEBGPU (0).
534
433
  */
535
434
  function mapLocation(out, px, py, pz, from, to, m, vp, ndcZMin) {
536
435
  // WORLD ↔ SCREEN
537
436
  if (from === WORLD && to === SCREEN)
538
437
  return _worldToScreen(out, px,py,pz, _ensurePV(m), vp, ndcZMin);
539
438
  if (from === SCREEN && to === WORLD)
540
- return _screenToWorld(out, px,py,pz, m.ipv, vp, ndcZMin);
439
+ return _screenToWorld(out, px,py,pz, m.ipvMatrix, vp, ndcZMin);
541
440
 
542
441
  // WORLD ↔ NDC
543
442
  if (from === WORLD && to === NDC)
544
443
  return _worldToNDC(out, px,py,pz, _ensurePV(m));
545
444
  if (from === NDC && to === WORLD)
546
- return _ndcToWorld(out, px,py,pz, m.ipv);
445
+ return _ndcToWorld(out, px,py,pz, m.ipvMatrix);
547
446
 
548
447
  // SCREEN ↔ NDC
549
448
  if (from === SCREEN && to === NDC)
@@ -553,34 +452,36 @@ function mapLocation(out, px, py, pz, from, to, m, vp, ndcZMin) {
553
452
 
554
453
  // WORLD ↔ EYE
555
454
  if (from === WORLD && to === EYE)
556
- return mat4MulPoint(out, m.view, px,py,pz);
455
+ return mat4MulPoint(out, m.vMatrix, px,py,pz);
557
456
  if (from === EYE && to === WORLD)
558
- return mat4MulPoint(out, m.eye, px,py,pz);
457
+ return mat4MulPoint(out, m.eMatrix, px,py,pz);
559
458
 
560
- // EYE ↔ SCREEN (inline: eye→world→screen / screen→world→eye)
459
+ // EYE ↔ SCREEN
561
460
  if (from === EYE && to === SCREEN) {
562
- const ex=m.eye[0]*px+m.eye[4]*py+m.eye[8]*pz+m.eye[12],
563
- ey=m.eye[1]*px+m.eye[5]*py+m.eye[9]*pz+m.eye[13],
564
- ez=m.eye[2]*px+m.eye[6]*py+m.eye[10]*pz+m.eye[14];
461
+ const e = m.eMatrix;
462
+ const ex=e[0]*px+e[4]*py+e[8]*pz+e[12],
463
+ ey=e[1]*px+e[5]*py+e[9]*pz+e[13],
464
+ ez=e[2]*px+e[6]*py+e[10]*pz+e[14];
565
465
  return _worldToScreen(out, ex,ey,ez, _ensurePV(m), vp, ndcZMin);
566
466
  }
567
467
  if (from === SCREEN && to === EYE) {
568
- _screenToWorld(out, px,py,pz, m.ipv, vp, ndcZMin);
468
+ _screenToWorld(out, px,py,pz, m.ipvMatrix, vp, ndcZMin);
569
469
  const wx=out[0],wy=out[1],wz=out[2];
570
- return mat4MulPoint(out, m.view, wx,wy,wz);
470
+ return mat4MulPoint(out, m.vMatrix, wx,wy,wz);
571
471
  }
572
472
 
573
- // EYE ↔ NDC (inline: eye→world→ndc / ndc→world→eye)
473
+ // EYE ↔ NDC
574
474
  if (from === EYE && to === NDC) {
575
- const ex=m.eye[0]*px+m.eye[4]*py+m.eye[8]*pz+m.eye[12],
576
- ey=m.eye[1]*px+m.eye[5]*py+m.eye[9]*pz+m.eye[13],
577
- ez=m.eye[2]*px+m.eye[6]*py+m.eye[10]*pz+m.eye[14];
475
+ const e = m.eMatrix;
476
+ const ex=e[0]*px+e[4]*py+e[8]*pz+e[12],
477
+ ey=e[1]*px+e[5]*py+e[9]*pz+e[13],
478
+ ez=e[2]*px+e[6]*py+e[10]*pz+e[14];
578
479
  return _worldToNDC(out, ex,ey,ez, _ensurePV(m));
579
480
  }
580
481
  if (from === NDC && to === EYE) {
581
- _ndcToWorld(out, px,py,pz, m.ipv);
482
+ _ndcToWorld(out, px,py,pz, m.ipvMatrix);
582
483
  const wx=out[0],wy=out[1],wz=out[2];
583
- return mat4MulPoint(out, m.view, wx,wy,wz);
484
+ return mat4MulPoint(out, m.vMatrix, wx,wy,wz);
584
485
  }
585
486
 
586
487
  // MATRIX (custom frame) ↔ WORLD
@@ -591,49 +492,54 @@ function mapLocation(out, px, py, pz, from, to, m, vp, ndcZMin) {
591
492
 
592
493
  // MATRIX ↔ EYE
593
494
  if (from === MATRIX && to === EYE) {
594
- const fx=m.fromFrame[0]*px+m.fromFrame[4]*py+m.fromFrame[8]*pz+m.fromFrame[12],
595
- fy=m.fromFrame[1]*px+m.fromFrame[5]*py+m.fromFrame[9]*pz+m.fromFrame[13],
596
- fz=m.fromFrame[2]*px+m.fromFrame[6]*py+m.fromFrame[10]*pz+m.fromFrame[14];
597
- return mat4MulPoint(out, m.view, fx,fy,fz);
495
+ const f = m.fromFrame;
496
+ const fx=f[0]*px+f[4]*py+f[8]*pz+f[12],
497
+ fy=f[1]*px+f[5]*py+f[9]*pz+f[13],
498
+ fz=f[2]*px+f[6]*py+f[10]*pz+f[14];
499
+ return mat4MulPoint(out, m.vMatrix, fx,fy,fz);
598
500
  }
599
501
  if (from === EYE && to === MATRIX) {
600
- const ex=m.eye[0]*px+m.eye[4]*py+m.eye[8]*pz+m.eye[12],
601
- ey=m.eye[1]*px+m.eye[5]*py+m.eye[9]*pz+m.eye[13],
602
- ez=m.eye[2]*px+m.eye[6]*py+m.eye[10]*pz+m.eye[14];
502
+ const e = m.eMatrix;
503
+ const ex=e[0]*px+e[4]*py+e[8]*pz+e[12],
504
+ ey=e[1]*px+e[5]*py+e[9]*pz+e[13],
505
+ ez=e[2]*px+e[6]*py+e[10]*pz+e[14];
603
506
  return mat4MulPoint(out, m.toFrameInv, ex,ey,ez);
604
507
  }
605
508
 
606
509
  // MATRIX ↔ SCREEN
607
510
  if (from === MATRIX && to === SCREEN) {
608
- const fx=m.fromFrame[0]*px+m.fromFrame[4]*py+m.fromFrame[8]*pz+m.fromFrame[12],
609
- fy=m.fromFrame[1]*px+m.fromFrame[5]*py+m.fromFrame[9]*pz+m.fromFrame[13],
610
- fz=m.fromFrame[2]*px+m.fromFrame[6]*py+m.fromFrame[10]*pz+m.fromFrame[14];
511
+ const f = m.fromFrame;
512
+ const fx=f[0]*px+f[4]*py+f[8]*pz+f[12],
513
+ fy=f[1]*px+f[5]*py+f[9]*pz+f[13],
514
+ fz=f[2]*px+f[6]*py+f[10]*pz+f[14];
611
515
  return _worldToScreen(out, fx,fy,fz, _ensurePV(m), vp, ndcZMin);
612
516
  }
613
517
  if (from === SCREEN && to === MATRIX) {
614
- _screenToWorld(out, px,py,pz, m.ipv, vp, ndcZMin);
518
+ _screenToWorld(out, px,py,pz, m.ipvMatrix, vp, ndcZMin);
615
519
  const wx=out[0],wy=out[1],wz=out[2];
616
520
  return mat4MulPoint(out, m.toFrameInv, wx,wy,wz);
617
521
  }
618
522
 
619
523
  // MATRIX ↔ NDC
620
524
  if (from === MATRIX && to === NDC) {
621
- const fx=m.fromFrame[0]*px+m.fromFrame[4]*py+m.fromFrame[8]*pz+m.fromFrame[12],
622
- fy=m.fromFrame[1]*px+m.fromFrame[5]*py+m.fromFrame[9]*pz+m.fromFrame[13],
623
- fz=m.fromFrame[2]*px+m.fromFrame[6]*py+m.fromFrame[10]*pz+m.fromFrame[14];
525
+ const f = m.fromFrame;
526
+ const fx=f[0]*px+f[4]*py+f[8]*pz+f[12],
527
+ fy=f[1]*px+f[5]*py+f[9]*pz+f[13],
528
+ fz=f[2]*px+f[6]*py+f[10]*pz+f[14];
624
529
  return _worldToNDC(out, fx,fy,fz, _ensurePV(m));
625
530
  }
626
531
  if (from === NDC && to === MATRIX) {
627
- _ndcToWorld(out, px,py,pz, m.ipv);
532
+ _ndcToWorld(out, px,py,pz, m.ipvMatrix);
628
533
  const wx=out[0],wy=out[1],wz=out[2];
629
534
  return mat4MulPoint(out, m.toFrameInv, wx,wy,wz);
630
535
  }
631
536
 
632
537
  // MATRIX ↔ MATRIX
633
538
  if (from === MATRIX && to === MATRIX) {
634
- const fx=m.fromFrame[0]*px+m.fromFrame[4]*py+m.fromFrame[8]*pz+m.fromFrame[12],
635
- fy=m.fromFrame[1]*px+m.fromFrame[5]*py+m.fromFrame[9]*pz+m.fromFrame[13],
636
- fz=m.fromFrame[2]*px+m.fromFrame[6]*py+m.fromFrame[10]*pz+m.fromFrame[14];
539
+ const f = m.fromFrame;
540
+ const fx=f[0]*px+f[4]*py+f[8]*pz+f[12],
541
+ fy=f[1]*px+f[5]*py+f[9]*pz+f[13],
542
+ fz=f[2]*px+f[6]*py+f[10]*pz+f[14];
637
543
  return mat4MulPoint(out, m.toFrameInv, fx,fy,fz);
638
544
  }
639
545
 
@@ -644,7 +550,7 @@ function mapLocation(out, px, py, pz, from, to, m, vp, ndcZMin) {
644
550
 
645
551
  // ── Direction helpers ────────────────────────────────────────────────────
646
552
 
647
- /** Apply the 3×3 linear part of a mat4 (rotation/scale, no translation) */
553
+ /** Apply the 3×3 linear part of a mat4 (rotation/scale, no translation). */
648
554
  function _applyDir(out, mat, dx, dy, dz) {
649
555
  out[0]=mat[0]*dx+mat[4]*dy+mat[8]*dz;
650
556
  out[1]=mat[1]*dx+mat[5]*dy+mat[9]*dz;
@@ -652,37 +558,24 @@ function _applyDir(out, mat, dx, dy, dz) {
652
558
  return out;
653
559
  }
654
560
 
655
- /**
656
- * World→Screen direction. Self-contained: reads proj scalars + view mat.
657
- * The existing p5.tree code nested _direction and _location calls here;
658
- * this version inlines all math with stack locals.
659
- */
660
561
  function _worldToScreenDir(out, dx, dy, dz, proj, view, vpW, vpH, ndcZMin) {
661
- // 1. World → Eye direction: R · d (standard column-major mat × vec)
662
562
  const edx = view[0]*dx + view[4]*dy + view[8]*dz;
663
563
  const edy = view[1]*dx + view[5]*dy + view[9]*dz;
664
564
  const edz = view[2]*dx + view[6]*dy + view[10]*dz;
665
-
666
565
  const isPersp = proj[15] === 0;
667
566
  let sdx = edx, sdy = edy;
668
-
669
567
  if (isPersp) {
670
- // Camera-eye Z of world origin (inline WORLD→EYE for [0,0,0]):
671
- // view * [0,0,0,1] = column 3 of view
672
- const zEye = view[8]*0 + view[9]*0 + view[10]*0 + view[14]; // = view[14]
568
+ const zEye = view[14];
673
569
  const halfTan = Math.tan(projFov(proj) / 2);
674
570
  const k = Math.abs(zEye * halfTan);
675
571
  const pixPerUnit = vpH / (2 * k);
676
572
  sdx *= pixPerUnit;
677
573
  sdy *= pixPerUnit;
678
574
  } else {
679
- // Ortho: pixels per world unit along X/Y
680
575
  const orthoW = Math.abs(projRight(proj, ndcZMin) - projLeft(proj, ndcZMin));
681
576
  sdx *= vpW / orthoW;
682
577
  sdy *= vpH / Math.abs(projTop(proj, ndcZMin) - projBottom(proj, ndcZMin));
683
578
  }
684
-
685
- // Z: map eye-space dz to screen-space dz
686
579
  const near = projNear(proj, ndcZMin), far = projFar(proj);
687
580
  const depthRange = near - far;
688
581
  let sdz;
@@ -691,7 +584,6 @@ function _worldToScreenDir(out, dx, dy, dz, proj, view, vpW, vpH, ndcZMin) {
691
584
  } else {
692
585
  sdz = edz / (depthRange / (Math.abs(projRight(proj, ndcZMin) - projLeft(proj, ndcZMin)) / vpW));
693
586
  }
694
-
695
587
  out[0] = sdx; out[1] = sdy; out[2] = sdz;
696
588
  return out;
697
589
  }
@@ -699,7 +591,6 @@ function _worldToScreenDir(out, dx, dy, dz, proj, view, vpW, vpH, ndcZMin) {
699
591
  function _screenToWorldDir(out, dx, dy, dz, proj, view, eye, vpW, vpH, ndcZMin) {
700
592
  const isPersp = proj[15] === 0;
701
593
  let edx = dx, edy = dy;
702
-
703
594
  if (isPersp) {
704
595
  const zEye = view[14];
705
596
  const halfTan = Math.tan(projFov(proj) / 2);
@@ -711,7 +602,6 @@ function _screenToWorldDir(out, dx, dy, dz, proj, view, eye, vpW, vpH, ndcZMin)
711
602
  edx *= orthoW / vpW;
712
603
  edy *= Math.abs(projTop(proj, ndcZMin) - projBottom(proj, ndcZMin)) / vpH;
713
604
  }
714
-
715
605
  const near = projNear(proj, ndcZMin), far = projFar(proj);
716
606
  const depthRange = near - far;
717
607
  let edz;
@@ -720,8 +610,6 @@ function _screenToWorldDir(out, dx, dy, dz, proj, view, eye, vpW, vpH, ndcZMin)
720
610
  } else {
721
611
  edz = dz * (depthRange / (Math.abs(projRight(proj, ndcZMin) - projLeft(proj, ndcZMin)) / vpW));
722
612
  }
723
-
724
- // Eye → World direction (dMatrix = upper-left 3×3 of eye = inv(view))
725
613
  _applyDir(out, eye, edx, edy, edz);
726
614
  return out;
727
615
  }
@@ -743,21 +631,21 @@ function _ndcToScreenDir(out, dx, dy, dz, vpW, vpH, ndcZMin) {
743
631
  }
744
632
 
745
633
  /**
746
- * Map a direction between coordinate spaces.
747
- * Same flat-dispatch as mapLocation.
634
+ * Map a direction vector between coordinate spaces.
635
+ * Same bag contract as mapLocation.
748
636
  */
749
637
  function mapDirection(out, dx, dy, dz, from, to, m, vp, ndcZMin) {
750
638
  const vpW = Math.abs(vp[2]), vpH = Math.abs(vp[3]);
751
639
 
752
- // EYE ↔ WORLD (most common: dMatrix operation)
753
- if (from === EYE && to === WORLD) return _applyDir(out, m.eye, dx, dy, dz);
754
- if (from === WORLD && to === EYE) return _applyDir(out, m.view, dx, dy, dz);
640
+ // EYE ↔ WORLD (most common)
641
+ if (from === EYE && to === WORLD) return _applyDir(out, m.eMatrix, dx, dy, dz);
642
+ if (from === WORLD && to === EYE) return _applyDir(out, m.vMatrix, dx, dy, dz);
755
643
 
756
644
  // WORLD ↔ SCREEN
757
645
  if (from === WORLD && to === SCREEN)
758
- return _worldToScreenDir(out, dx,dy,dz, m.proj, m.view, vpW, vpH, ndcZMin);
646
+ return _worldToScreenDir(out, dx,dy,dz, m.pMatrix, m.vMatrix, vpW, vpH, ndcZMin);
759
647
  if (from === SCREEN && to === WORLD)
760
- return _screenToWorldDir(out, dx,dy,dz, m.proj, m.view, m.eye, vpW, vpH, ndcZMin);
648
+ return _screenToWorldDir(out, dx,dy,dz, m.pMatrix, m.vMatrix, m.eMatrix, vpW, vpH, ndcZMin);
761
649
 
762
650
  // SCREEN ↔ NDC
763
651
  if (from === SCREEN && to === NDC)
@@ -765,45 +653,44 @@ function mapDirection(out, dx, dy, dz, from, to, m, vp, ndcZMin) {
765
653
  if (from === NDC && to === SCREEN)
766
654
  return _ndcToScreenDir(out, dx,dy,dz, vpW, vpH, ndcZMin);
767
655
 
768
- // WORLD ↔ NDC (chain: world→screen→ndc / ndc→screen→world)
656
+ // WORLD ↔ NDC
769
657
  if (from === WORLD && to === NDC) {
770
- _worldToScreenDir(out, dx,dy,dz, m.proj, m.view, vpW, vpH, ndcZMin);
658
+ _worldToScreenDir(out, dx,dy,dz, m.pMatrix, m.vMatrix, vpW, vpH, ndcZMin);
771
659
  const sx=out[0],sy=out[1],sz=out[2];
772
660
  return _screenToNDCDir(out, sx,sy,sz, vpW, vpH, ndcZMin);
773
661
  }
774
662
  if (from === NDC && to === WORLD) {
775
663
  _ndcToScreenDir(out, dx,dy,dz, vpW, vpH, ndcZMin);
776
664
  const sx=out[0],sy=out[1],sz=out[2];
777
- return _screenToWorldDir(out, sx,sy,sz, m.proj, m.view, m.eye, vpW, vpH, ndcZMin);
665
+ return _screenToWorldDir(out, sx,sy,sz, m.pMatrix, m.vMatrix, m.eMatrix, vpW, vpH, ndcZMin);
778
666
  }
779
667
 
780
668
  // EYE ↔ SCREEN
781
669
  if (from === EYE && to === SCREEN) {
782
- // eye→world→screen
783
- _applyDir(out, m.eye, dx,dy,dz);
670
+ _applyDir(out, m.eMatrix, dx,dy,dz);
784
671
  const wx=out[0],wy=out[1],wz=out[2];
785
- return _worldToScreenDir(out, wx,wy,wz, m.proj, m.view, vpW, vpH, ndcZMin);
672
+ return _worldToScreenDir(out, wx,wy,wz, m.pMatrix, m.vMatrix, vpW, vpH, ndcZMin);
786
673
  }
787
674
  if (from === SCREEN && to === EYE) {
788
- _screenToWorldDir(out, dx,dy,dz, m.proj, m.view, m.eye, vpW, vpH, ndcZMin);
675
+ _screenToWorldDir(out, dx,dy,dz, m.pMatrix, m.vMatrix, m.eMatrix, vpW, vpH, ndcZMin);
789
676
  const wx=out[0],wy=out[1],wz=out[2];
790
- return _applyDir(out, m.view, wx,wy,wz);
677
+ return _applyDir(out, m.vMatrix, wx,wy,wz);
791
678
  }
792
679
 
793
680
  // EYE ↔ NDC
794
681
  if (from === EYE && to === NDC) {
795
- _applyDir(out, m.eye, dx,dy,dz);
682
+ _applyDir(out, m.eMatrix, dx,dy,dz);
796
683
  const wx=out[0],wy=out[1],wz=out[2];
797
- _worldToScreenDir(out, wx,wy,wz, m.proj, m.view, vpW, vpH, ndcZMin);
684
+ _worldToScreenDir(out, wx,wy,wz, m.pMatrix, m.vMatrix, vpW, vpH, ndcZMin);
798
685
  const sx=out[0],sy=out[1],sz=out[2];
799
686
  return _screenToNDCDir(out, sx,sy,sz, vpW, vpH, ndcZMin);
800
687
  }
801
688
  if (from === NDC && to === EYE) {
802
689
  _ndcToScreenDir(out, dx,dy,dz, vpW, vpH, ndcZMin);
803
690
  const sx=out[0],sy=out[1],sz=out[2];
804
- _screenToWorldDir(out, sx,sy,sz, m.proj, m.view, m.eye, vpW, vpH, ndcZMin);
691
+ _screenToWorldDir(out, sx,sy,sz, m.pMatrix, m.vMatrix, m.eMatrix, vpW, vpH, ndcZMin);
805
692
  const wx=out[0],wy=out[1],wz=out[2];
806
- return _applyDir(out, m.view, wx,wy,wz);
693
+ return _applyDir(out, m.vMatrix, wx,wy,wz);
807
694
  }
808
695
 
809
696
  // MATRIX ↔ WORLD
@@ -814,10 +701,10 @@ function mapDirection(out, dx, dy, dz, from, to, m, vp, ndcZMin) {
814
701
  if (from === MATRIX && to === EYE) {
815
702
  _applyDir(out, m.fromFrame, dx,dy,dz);
816
703
  const wx=out[0],wy=out[1],wz=out[2];
817
- return _applyDir(out, m.view, wx,wy,wz);
704
+ return _applyDir(out, m.vMatrix, wx,wy,wz);
818
705
  }
819
706
  if (from === EYE && to === MATRIX) {
820
- _applyDir(out, m.eye, dx,dy,dz);
707
+ _applyDir(out, m.eMatrix, dx,dy,dz);
821
708
  const wx=out[0],wy=out[1],wz=out[2];
822
709
  return _applyDir(out, m.toFrameInv, wx,wy,wz);
823
710
  }
@@ -826,10 +713,10 @@ function mapDirection(out, dx, dy, dz, from, to, m, vp, ndcZMin) {
826
713
  if (from === MATRIX && to === SCREEN) {
827
714
  _applyDir(out, m.fromFrame, dx,dy,dz);
828
715
  const wx=out[0],wy=out[1],wz=out[2];
829
- return _worldToScreenDir(out, wx,wy,wz, m.proj, m.view, vpW, vpH, ndcZMin);
716
+ return _worldToScreenDir(out, wx,wy,wz, m.pMatrix, m.vMatrix, vpW, vpH, ndcZMin);
830
717
  }
831
718
  if (from === SCREEN && to === MATRIX) {
832
- _screenToWorldDir(out, dx,dy,dz, m.proj, m.view, m.eye, vpW, vpH, ndcZMin);
719
+ _screenToWorldDir(out, dx,dy,dz, m.pMatrix, m.vMatrix, m.eMatrix, vpW, vpH, ndcZMin);
833
720
  const wx=out[0],wy=out[1],wz=out[2];
834
721
  return _applyDir(out, m.toFrameInv, wx,wy,wz);
835
722
  }
@@ -838,14 +725,14 @@ function mapDirection(out, dx, dy, dz, from, to, m, vp, ndcZMin) {
838
725
  if (from === MATRIX && to === NDC) {
839
726
  _applyDir(out, m.fromFrame, dx,dy,dz);
840
727
  const wx=out[0],wy=out[1],wz=out[2];
841
- _worldToScreenDir(out, wx,wy,wz, m.proj, m.view, vpW, vpH, ndcZMin);
728
+ _worldToScreenDir(out, wx,wy,wz, m.pMatrix, m.vMatrix, vpW, vpH, ndcZMin);
842
729
  const sx=out[0],sy=out[1],sz=out[2];
843
730
  return _screenToNDCDir(out, sx,sy,sz, vpW, vpH, ndcZMin);
844
731
  }
845
732
  if (from === NDC && to === MATRIX) {
846
733
  _ndcToScreenDir(out, dx,dy,dz, vpW, vpH, ndcZMin);
847
734
  const sx=out[0],sy=out[1],sz=out[2];
848
- _screenToWorldDir(out, sx,sy,sz, m.proj, m.view, m.eye, vpW, vpH, ndcZMin);
735
+ _screenToWorldDir(out, sx,sy,sz, m.pMatrix, m.vMatrix, m.eMatrix, vpW, vpH, ndcZMin);
849
736
  const wx=out[0],wy=out[1],wz=out[2];
850
737
  return _applyDir(out, m.toFrameInv, wx,wy,wz);
851
738
  }
@@ -868,9 +755,9 @@ function mapDirection(out, dx, dy, dz, from, to, m, vp, ndcZMin) {
868
755
 
869
756
  /**
870
757
  * World-units-per-pixel at a given eye-space Z depth.
871
- * @param {ArrayLike<number>} proj Projection Mat4.
758
+ * @param {ArrayLike<number>} proj Projection mat4.
872
759
  * @param {number} vpH Viewport height (pixels).
873
- * @param {number} eyeZ Eye-space Z of the point (negative for in-front-of camera).
760
+ * @param {number} eyeZ Eye-space Z (negative for in-front-of camera).
874
761
  * @param {number} ndcZMin WEBGL or WEBGPU.
875
762
  */
876
763
  function pixelRatio(proj, vpH, eyeZ, ndcZMin) {
@@ -1211,46 +1098,165 @@ const _clampS = (x, lo, hi) => x < lo ? lo : (x > hi ? hi : x);
1211
1098
 
1212
1099
  function _parseVec3(v) {
1213
1100
  if (!v) return null;
1214
- if (Array.isArray(v) && v.length >= 3 && v.every(n => typeof n === 'number')) return [v[0],v[1],v[2]];
1215
- if (typeof v === 'object' && 'x' in v) return [v.x||0, v.y||0, v.z||0];
1101
+ if (ArrayBuffer.isView(v) && v.length >= 3) return [v[0], v[1], v[2]];
1102
+ if (Array.isArray(v) && v.length >= 3 && v.every(n => typeof n === 'number')) return [v[0], v[1], v[2]];
1103
+ if (typeof v === 'object' && 'x' in v) return [v.x || 0, v.y || 0, v.z || 0];
1216
1104
  return null;
1217
1105
  }
1218
1106
 
1107
+ // Euler: unit axis vectors and the six valid intrinsic orderings.
1108
+ const _EULER_AXES = { X:[1,0,0], Y:[0,1,0], Z:[0,0,1] };
1109
+ const _EULER_ORDERS = new Set(['XYZ','XZY','YXZ','YZX','ZXY','ZYX']);
1110
+
1111
+ /**
1112
+ * Parse any rotation representation into a unit quaternion [x,y,z,w].
1113
+ *
1114
+ * Accepted forms:
1115
+ *
1116
+ * [x,y,z,w]
1117
+ * Raw quaternion array.
1118
+ *
1119
+ * { axis:[x,y,z], angle }
1120
+ * Axis-angle. Axis need not be unit.
1121
+ *
1122
+ * { dir:[x,y,z], up?:[x,y,z] }
1123
+ * Object orientation — forward direction (−Z) with optional up hint.
1124
+ *
1125
+ * { eMatrix: mat4 }
1126
+ * Extract rotation block from an eye (eye→world) matrix.
1127
+ * Column-major Float32Array(16), plain Array, or { mat4 } wrapper.
1128
+ *
1129
+ * { mat3: mat3 }
1130
+ * Column-major 3×3 rotation matrix — Float32Array(9) or plain Array.
1131
+ *
1132
+ * { euler:[rx,ry,rz], order?:'YXZ' }
1133
+ * Intrinsic Euler angles (radians). Angles are indexed by order position:
1134
+ * e[0] rotates around order[0] axis, e[1] around order[1], e[2] around order[2].
1135
+ * Supported orders: YXZ (default), XYZ, ZYX, ZXY, XZY, YZX.
1136
+ * Note: intrinsic ABC = extrinsic CBA with the same angles — to use
1137
+ * extrinsic order ABC, reverse the string and use intrinsic CBA.
1138
+ *
1139
+ * { from:[x,y,z], to:[x,y,z] }
1140
+ * Shortest-arc rotation from one direction onto another.
1141
+ * Both vectors are normalised internally.
1142
+ * Antiparallel input: 180° rotation around a perpendicular axis.
1143
+ *
1144
+ * @param {*} v
1145
+ * @returns {number[]|null} [x,y,z,w] or null if unparseable.
1146
+ */
1219
1147
  function _parseQuat(v) {
1220
1148
  if (!v) return null;
1221
- if (Array.isArray(v) && v.length === 4 && v.every(n => typeof n === 'number')) return [v[0],v[1],v[2],v[3]];
1149
+
1150
+ // raw [x,y,z,w] — plain array or typed array
1151
+ if ((Array.isArray(v) || ArrayBuffer.isView(v)) && v.length === 4) return [v[0], v[1], v[2], v[3]];
1152
+
1153
+ // { axis, angle }
1222
1154
  if (v.axis && typeof v.angle === 'number') {
1223
1155
  const a = Array.isArray(v.axis) ? v.axis : [v.axis.x||0, v.axis.y||0, v.axis.z||0];
1224
1156
  return qFromAxisAngle([0,0,0,1], a[0],a[1],a[2], v.angle);
1225
1157
  }
1158
+
1159
+ // { dir, up? }
1226
1160
  if (v.dir) {
1227
1161
  const d = Array.isArray(v.dir) ? v.dir : [v.dir.x||0, v.dir.y||0, v.dir.z||0];
1228
1162
  const u = v.up ? (Array.isArray(v.up) ? v.up : [v.up.x||0, v.up.y||0, v.up.z||0]) : null;
1229
1163
  return qFromLookDir([0,0,0,1], d, u);
1230
1164
  }
1231
- // { view } — column-major mat4 or {mat4} wrapper
1232
- if (v.view != null) {
1233
- const m = (ArrayBuffer.isView(v.view) || Array.isArray(v.view)) ? v.view : (v.view.mat4 ?? null);
1234
- if (m && m.length === 16) return qFromMat4([0,0,0,1], m);
1235
- }
1236
- // { eye, center, up? } lookat shorthand matching CameraTrack input
1237
- if (v.eye && v.center) {
1238
- const eye = _parseVec3(v.eye), ctr = _parseVec3(v.center);
1239
- if (eye && ctr) {
1240
- const up = (v.up ? _parseVec3(v.up) : null) || [0,1,0];
1241
- let fx=ctr[0]-eye[0], fy=ctr[1]-eye[1], fz=ctr[2]-eye[2];
1242
- const fl=Math.sqrt(fx*fx+fy*fy+fz*fz)||1; fx/=fl; fy/=fl; fz/=fl;
1243
- let rx=fy*up[2]-fz*up[1], ry=fz*up[0]-fx*up[2], rz=fx*up[1]-fy*up[0];
1244
- const rl=Math.sqrt(rx*rx+ry*ry+rz*rz)||1; rx/=rl; ry/=rl; rz/=rl;
1245
- const ux=ry*fz-rz*fy, uy=rz*fx-rx*fz, uz=rx*fy-ry*fx;
1246
- return qFromRotMat3x3([0,0,0,1], rx,ux,-fx, ry,uy,-fy, rz,uz,-fz);
1165
+
1166
+ // { eMatrix } — rotation block from eye (eye→world) matrix, col-major mat4
1167
+ if (v.eMatrix != null) {
1168
+ const m = (ArrayBuffer.isView(v.eMatrix) || Array.isArray(v.eMatrix))
1169
+ ? v.eMatrix : (v.eMatrix.mat4 ?? null);
1170
+ if (m && m.length >= 16) return qFromMat4([0,0,0,1], m);
1171
+ }
1172
+
1173
+ // { mat3 } — column-major 3×3 rotation matrix
1174
+ // col0=[m0,m1,m2], col1=[m3,m4,m5], col2=[m6,m7,m8]
1175
+ // row-major for qFromRotMat3x3: row0=[m0,m3,m6], row1=[m1,m4,m7], row2=[m2,m5,m8]
1176
+ if (v.mat3 != null) {
1177
+ const m = v.mat3;
1178
+ if ((ArrayBuffer.isView(m) || Array.isArray(m)) && m.length >= 9)
1179
+ return qFromRotMat3x3([0,0,0,1], m[0],m[3],m[6], m[1],m[4],m[7], m[2],m[5],m[8]);
1180
+ }
1181
+
1182
+ // { euler, order? } — intrinsic Euler angles (radians), default order YXZ
1183
+ if (v.euler != null) {
1184
+ const e = v.euler;
1185
+ if (!Array.isArray(e) || e.length < 3) return null;
1186
+ const order = (typeof v.order === 'string' && _EULER_ORDERS.has(v.order))
1187
+ ? v.order : 'YXZ';
1188
+ const q = [0,0,0,1];
1189
+ const s = [0,0,0,1]; // scratch — reused each step
1190
+ for (let i = 0; i < 3; i++) {
1191
+ const ax = _EULER_AXES[order[i]];
1192
+ qMul(q, q, qFromAxisAngle(s, ax[0],ax[1],ax[2], e[i]));
1193
+ }
1194
+ return q;
1195
+ }
1196
+
1197
+ // { from, to } — shortest-arc rotation from one direction onto another
1198
+ if (v.from != null && v.to != null) {
1199
+ const f = Array.isArray(v.from) ? v.from : [v.from.x||0, v.from.y||0, v.from.z||0];
1200
+ const t = Array.isArray(v.to) ? v.to : [v.to.x||0, v.to.y||0, v.to.z||0];
1201
+ const fl = Math.sqrt(f[0]*f[0]+f[1]*f[1]+f[2]*f[2]) || 1;
1202
+ const tl = Math.sqrt(t[0]*t[0]+t[1]*t[1]+t[2]*t[2]) || 1;
1203
+ const fx=f[0]/fl, fy=f[1]/fl, fz=f[2]/fl;
1204
+ const tx=t[0]/tl, ty=t[1]/tl, tz=t[2]/tl;
1205
+ const dot = fx*tx + fy*ty + fz*tz;
1206
+ // parallel — identity
1207
+ if (dot >= 1 - 1e-8) return [0,0,0,1];
1208
+ // antiparallel — 180° around any perpendicular axis
1209
+ if (dot <= -1 + 1e-8) {
1210
+ // cross(from, X=[1,0,0]) = [0, fz, -fy]
1211
+ let px=0, py=fz, pz=-fy;
1212
+ let pl = Math.sqrt(px*px+py*py+pz*pz);
1213
+ if (pl < 1e-8) {
1214
+ // from ≈ ±X; try cross(from, Z=[0,0,1]) = [fy, -fx, 0]
1215
+ px=fy; py=-fx; pz=0;
1216
+ pl = Math.sqrt(px*px+py*py+pz*pz);
1217
+ }
1218
+ if (pl < 1e-8) return [0,0,0,1];
1219
+ return qFromAxisAngle([0,0,0,1], px/pl,py/pl,pz/pl, Math.PI);
1247
1220
  }
1221
+ // general case — axis = normalize(cross(from, to))
1222
+ let ax=fy*tz-fz*ty, ay=fz*tx-fx*tz, az=fx*ty-fy*tx;
1223
+ const al = Math.sqrt(ax*ax+ay*ay+az*az) || 1;
1224
+ return qFromAxisAngle([0,0,0,1], ax/al,ay/al,az/al,
1225
+ Math.acos(Math.max(-1, Math.min(1, dot))));
1248
1226
  }
1227
+
1249
1228
  return null;
1250
1229
  }
1251
1230
 
1231
+ /**
1232
+ * Parse a PoseTrack keyframe spec.
1233
+ *
1234
+ * Accepted forms:
1235
+ *
1236
+ * { mMatrix }
1237
+ * Decompose a column-major mat4 into TRS via mat4ToTransform.
1238
+ * Float32Array(16), plain Array, or { mat4 } wrapper.
1239
+ * pos from col3, scl from column lengths, rot from normalised rotation block.
1240
+ *
1241
+ * { pos, rot, scl }
1242
+ * Explicit TRS. pos and scl are vec3, rot accepts any form from _parseQuat.
1243
+ * All fields are optional — missing pos/scl default to [0,0,0] / [1,1,1],
1244
+ * missing rot defaults to identity.
1245
+ *
1246
+ * @param {Object} spec
1247
+ * @returns {{ pos:number[], rot:number[], scl:number[] }|null}
1248
+ */
1252
1249
  function _parseSpec(spec) {
1253
1250
  if (!spec || typeof spec !== 'object') return null;
1251
+
1252
+ // { mMatrix } — full TRS decomposition from model matrix
1253
+ if (spec.mMatrix != null) {
1254
+ const m = (ArrayBuffer.isView(spec.mMatrix) || Array.isArray(spec.mMatrix))
1255
+ ? spec.mMatrix : (spec.mMatrix.mat4 ?? null);
1256
+ if (!m || m.length < 16) return null;
1257
+ return mat4ToTransform({ pos:[0,0,0], rot:[0,0,0,1], scl:[1,1,1] }, m);
1258
+ }
1259
+
1254
1260
  const pos = _parseVec3(spec.pos) || [0,0,0];
1255
1261
  const rot = _parseQuat(spec.rot) || [0,0,0,1];
1256
1262
  const scl = _parseVec3(spec.scl) || [1,1,1];
@@ -1272,17 +1278,22 @@ function _sameTransform(a, b) {
1272
1278
  *
1273
1279
  * Accepted forms:
1274
1280
  *
1275
- * { eye, center, up? }
1276
- * Explicit lookat. up defaults to [0,1,0] and is normalised on storage.
1281
+ * { eye, center?, up? }
1282
+ * Explicit lookat. center defaults to [0,0,0], up defaults to [0,1,0].
1283
+ * Both are normalised/stored as-is. eye must be a vec3.
1284
+ *
1285
+ * { vMatrix: mat4 }
1286
+ * Column-major view matrix (world→eye).
1287
+ * eye reconstructed via -R^T·t; center = eye + forward·1; up = [0,1,0].
1288
+ * The matrix's up_ortho (col1) is intentionally NOT used as up —
1289
+ * passing it to cam.camera() shifts orbitControl's orbit reference.
1290
+ * Float32Array(16), plain Array, or { mat4 } wrapper.
1277
1291
  *
1278
- * { view: mat4 }
1279
- * Column-major view matrix (Float32Array(16), plain Array, or {mat4} wrapper).
1280
- * eye is extracted from the matrix translation block.
1281
- * center = eye + forward * 1 (unit distance sufficient for interpolation).
1282
- * up defaults to [0,1,0]. The matrix's up_ortho (col1) is intentionally
1283
- * NOT extracted; using up_ortho as the hint causes orbitControl drift.
1284
- * If you need to preserve roll from a rolled camera, pass the live
1285
- * camera's up hint via capturePose() instead of using { view }.
1292
+ * { eMatrix: mat4 }
1293
+ * Column-major eye matrix (eye→world, i.e. inverse view).
1294
+ * eye read directly from col3; center = eye + forward·1; up = [0,1,0].
1295
+ * Simpler extraction than vMatrix; prefer this form when eMatrix is available.
1296
+ * Float32Array(16), plain Array, or { mat4 } wrapper.
1286
1297
  *
1287
1298
  * @param {Object} spec
1288
1299
  * @returns {{ eye:number[], center:number[], up:number[] }|null}
@@ -1290,34 +1301,38 @@ function _sameTransform(a, b) {
1290
1301
  function _parseCameraSpec(spec) {
1291
1302
  if (!spec || typeof spec !== 'object') return null;
1292
1303
 
1293
- // { view } form
1294
- if (spec.view != null) {
1295
- const m = (ArrayBuffer.isView(spec.view) || Array.isArray(spec.view))
1296
- ? spec.view
1297
- : (spec.view.mat4 ?? null);
1304
+ // { vMatrix } — view matrix (world→eye); reconstruct eye via -R^T·t
1305
+ if (spec.vMatrix != null) {
1306
+ const m = (ArrayBuffer.isView(spec.vMatrix) || Array.isArray(spec.vMatrix))
1307
+ ? spec.vMatrix : (spec.vMatrix.mat4 ?? null);
1298
1308
  if (!m || m.length < 16) return null;
1299
- // Extract eye position: eye = -R^T * t
1300
- // Column-major view mat: col0=right, col1=up_ortho, col2=-fwd, col3=translation
1301
1309
  const ex = -(m[0]*m[12] + m[4]*m[13] + m[8]*m[14]);
1302
1310
  const ey = -(m[1]*m[12] + m[5]*m[13] + m[9]*m[14]);
1303
1311
  const ez = -(m[2]*m[12] + m[6]*m[13] + m[10]*m[14]);
1304
- // forward = -col2 = [-m[8], -m[9], -m[10]]
1305
1312
  const fx=-m[8], fy=-m[9], fz=-m[10];
1306
1313
  const fl=Math.sqrt(fx*fx+fy*fy+fz*fz)||1;
1307
- // center = eye + normalized_fwd * 1
1308
- const cx=ex+fx/fl, cy=ey+fy/fl, cz=ez+fz/fl;
1309
- return { eye:[ex,ey,ez], center:[cx,cy,cz], up:[0,1,0] };
1314
+ return { eye:[ex,ey,ez], center:[ex+fx/fl,ey+fy/fl,ez+fz/fl], up:[0,1,0] };
1310
1315
  }
1311
1316
 
1312
- // { eye, center, up? } form
1313
- const eye = _parseVec3(spec.eye);
1314
- const center = _parseVec3(spec.center);
1315
- if (!eye || !center) return null;
1317
+ // { eMatrix } — eye matrix (eye→world); eye = col3, forward = -col2
1318
+ if (spec.eMatrix != null) {
1319
+ const m = (ArrayBuffer.isView(spec.eMatrix) || Array.isArray(spec.eMatrix))
1320
+ ? spec.eMatrix : (spec.eMatrix.mat4 ?? null);
1321
+ if (!m || m.length < 16) return null;
1322
+ const ex=m[12], ey=m[13], ez=m[14];
1323
+ const fx=-m[8], fy=-m[9], fz=-m[10];
1324
+ const fl=Math.sqrt(fx*fx+fy*fy+fz*fz)||1;
1325
+ return { eye:[ex,ey,ez], center:[ex+fx/fl,ey+fy/fl,ez+fz/fl], up:[0,1,0] };
1326
+ }
1316
1327
 
1328
+ // { eye, center?, up? } — explicit lookat (eye is a vec3, not a mat4)
1329
+ const eye = _parseVec3(spec.eye);
1330
+ if (!eye) return null;
1331
+ const center = _parseVec3(spec.center) || [0,0,0];
1317
1332
  const upRaw = spec.up ? _parseVec3(spec.up) : null;
1318
1333
  const up = upRaw || [0,1,0];
1319
1334
  const ul = Math.sqrt(up[0]*up[0]+up[1]*up[1]+up[2]*up[2]) || 1;
1320
- return { eye, center, up: [up[0]/ul, up[1]/ul, up[2]/ul] };
1335
+ return { eye, center, up:[up[0]/ul, up[1]/ul, up[2]/ul] };
1321
1336
  }
1322
1337
 
1323
1338
  function _sameCameraKeyframe(a, b) {
@@ -1584,13 +1599,19 @@ class Track {
1584
1599
  * Keyframe shape: { pos:[x,y,z], rot:[x,y,z,w], scl:[x,y,z] }
1585
1600
  *
1586
1601
  * add() accepts individual specs or a bulk array of specs:
1587
- * { pos, rot, scl } direct TRS
1588
- * { pos, rot: [x,y,z,w] } explicit quaternion
1589
- * { pos, rot: { axis, angle } } axis-angle
1590
- * { pos, rot: { dir, up? } } look direction (object orientation)
1591
- * { pos, rot: { view: mat4 } } from view matrix rotation block
1592
- * { pos, rot: { eye, center, up? } } lookat shorthand
1593
- * [ spec, spec, ... ] bulk
1602
+ *
1603
+ * { mMatrix } — full TRS from model matrix
1604
+ * { pos?, rot?, scl? } — direct TRS; all fields optional
1605
+ * { pos?, rot: [x,y,z,w] } explicit quaternion
1606
+ * { pos?, rot: { axis, angle } } axis-angle
1607
+ * { pos?, rot: { dir, up? } } look direction
1608
+ * { pos?, rot: { eMatrix: mat4 } } — rotation from eye matrix
1609
+ * { pos?, rot: { mat3 } } — column-major 3×3 rotation matrix
1610
+ * { pos?, rot: { euler, order? } } — intrinsic Euler angles (default YXZ)
1611
+ * { pos?, rot: { from, to } } — shortest-arc between two directions
1612
+ * [ spec, spec, ... ] — bulk
1613
+ *
1614
+ * Missing fields default to: pos → [0,0,0], rot → [0,0,0,1], scl → [1,1,1].
1594
1615
  *
1595
1616
  * eval() writes { pos, rot, scl }:
1596
1617
  * pos — Catmull-Rom (posInterp='catmullrom') or lerp
@@ -1599,8 +1620,10 @@ class Track {
1599
1620
  *
1600
1621
  * @example
1601
1622
  * const track = new PoseTrack()
1602
- * track.add({ pos:[0,0,0], rot:[0,0,0,1], scl:[1,1,1] })
1603
- * track.add({ pos:[100,0,0], rot:[0,0,0,1], scl:[1,1,1] })
1623
+ * track.add({ pos:[0,0,0] }) // identity rot, uniform scl
1624
+ * track.add({ pos:[100,0,0], rot: { euler:[0, Math.PI/2, 0] } })
1625
+ * track.add({ rot: { axis:[0,1,0], angle: Math.PI } }) // pos defaults to [0,0,0]
1626
+ * track.add({ mMatrix: someModelMatrix })
1604
1627
  * track.play({ loop: true })
1605
1628
  * // per frame:
1606
1629
  * track.tick()
@@ -1733,18 +1756,21 @@ class PoseTrack extends Track {
1733
1756
  * own paths, up nlerped on the unit sphere. This correctly handles cameras
1734
1757
  * that always look at a fixed target (center stays at origin throughout)
1735
1758
  * as well as free-fly paths where center moves independently.
1759
+ *
1760
+ * Missing fields default to: center → [0,0,0], up → [0,1,0].
1736
1761
  *
1737
1762
  * add() accepts individual specs or a bulk array of specs:
1738
- * { eye, center, up? } explicit lookat; up defaults to [0,1,0]
1739
- * { view: mat4 } view matrix; eye extracted, center = eye+fwd*1,
1740
- * up = [0,1,0] (safe default — see note below)
1741
- * [ spec, spec, ... ] bulk
1742
1763
  *
1743
- * Note on up for { view: mat4 }:
1744
- * The view matrix's col1 (up_ortho) is intentionally not used as up.
1745
- * For upright cameras up_ortho differs from the hint [0,1,0], and
1746
- * passing it to cam.camera() would shift orbitControl's orbit reference.
1747
- * Use capturePose() when you need to preserve the real up hint.
1764
+ * { eye, center?, up? } explicit lookat; center defaults to [0,0,0], up to [0,1,0]
1765
+ * { vMatrix: mat4 } view matrix (world→eye); eye reconstructed via -R^T·t
1766
+ * { eMatrix: mat4 } eye matrix (eye→world); eye read from col3 directly
1767
+ * [ spec, spec, ... ] bulk
1768
+ *
1769
+ * Note on up for matrix forms:
1770
+ * up is always [0,1,0]. The matrix's col1 (up_ortho) is intentionally
1771
+ * not used — it differs from the hint [0,1,0] for upright cameras and
1772
+ * passing it to cam.camera() shifts orbitControl's orbit reference.
1773
+ * Use capturePose() (p5.tree bridge) when the real up hint is needed.
1748
1774
  *
1749
1775
  * eval() writes { eye, center, up }:
1750
1776
  * eye — Catmull-Rom (eyeInterp='catmullrom') or lerp
@@ -1753,8 +1779,10 @@ class PoseTrack extends Track {
1753
1779
  *
1754
1780
  * @example
1755
1781
  * const track = new CameraTrack()
1756
- * track.add({ eye:[0,0,500], center:[0,0,0] })
1782
+ * track.add({ eye:[0,0,500] }) // center defaults to [0,0,0]
1757
1783
  * track.add({ eye:[300,-150,0], center:[0,0,0] })
1784
+ * track.add({ eMatrix: myEyeMatrix })
1785
+ * track.add({ vMatrix: myViewMatrix })
1758
1786
  * track.play({ loop: true })
1759
1787
  * // per frame:
1760
1788
  * track.tick()
@@ -1789,7 +1817,7 @@ class CameraTrack extends Track {
1789
1817
  * Append one or more camera keyframes. Adjacent duplicates are skipped by default.
1790
1818
  *
1791
1819
  * @param {Object|Object[]} spec
1792
- * { eye, center, up? } or { view: mat4 } or an array of either.
1820
+ * { eye, center?, up? } or { vMatrix: mat4 } or { eMatrix: mat4 } or an array of either.
1793
1821
  * @param {{ deduplicate?: boolean }} [opts]
1794
1822
  */
1795
1823
  add(spec, opts) {
@@ -2033,5 +2061,5 @@ function boxVisibility(planes, x0, y0, z0, x1, y1, z1) {
2033
2061
  return allIn ? VISIBLE : SEMIVISIBLE;
2034
2062
  }
2035
2063
 
2036
- export { CameraTrack, EYE, INVISIBLE, MATRIX, MODEL, NDC, ORIGIN, PLANE_BOTTOM, PLANE_FAR, PLANE_LEFT, PLANE_NEAR, PLANE_RIGHT, PLANE_TOP, PoseTrack, SCREEN, SEMIVISIBLE, VISIBLE, WEBGL, WEBGPU, WORLD, _i, _j, _k, boxVisibility, catmullRomVec3, distanceToPlane, frustumPlanes, i, j, k, lerpVec3, mapDirection, mapLocation, mat3Direction, mat3FromMat4T, mat3MulVec3, mat3NormalFromMat4, mat4Identity, mat4Invert, mat4Location, mat4MV, mat4Mul, mat4MulDir, mat4MulPoint, mat4PMV, mat4PV, mat4ToTransform, mat4Transpose, pixelRatio, pointVisibility, projBottom, projFar, projFov, projHfov, projIsOrtho, projLeft, projNear, projRight, projTop, qCopy, qDot, qFromAxisAngle, qFromLookDir, qFromMat4, qFromRotMat3x3, qMul, qNegate, qNlerp, qNormalize, qSet, qSlerp, qToMat4, quatToAxisAngle, sphereVisibility, transformToMat4 };
2064
+ export { CameraTrack, EYE, INVISIBLE, MATRIX, MODEL, NDC, ORIGIN, PLANE_BOTTOM, PLANE_FAR, PLANE_LEFT, PLANE_NEAR, PLANE_RIGHT, PLANE_TOP, PoseTrack, SCREEN, SEMIVISIBLE, VISIBLE, WEBGL, WEBGPU, WORLD, _i, _j, _k, boxVisibility, catmullRomVec3, distanceToPlane, frustumPlanes, i, j, k, lerpVec3, mapDirection, mapLocation, mat3Direction, mat3NormalFromMat4, mat4Invert, mat4Location, mat4MV, mat4Mul, mat4MulPoint, mat4PV, mat4ToTransform, mat4Transpose, pixelRatio, pointVisibility, projBottom, projFar, projFov, projHfov, projIsOrtho, projLeft, projNear, projRight, projTop, qCopy, qDot, qFromAxisAngle, qFromLookDir, qFromMat4, qFromRotMat3x3, qMul, qNegate, qNlerp, qNormalize, qSet, qSlerp, qToMat4, quatToAxisAngle, sphereVisibility, transformToMat4 };
2037
2065
  //# sourceMappingURL=index.js.map