@nakednous/tree 0.0.21 → 0.0.23

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
@@ -116,7 +116,7 @@ rot: { mat4Eye: mat4 } // rotation block of an eye matrix
116
116
 
117
117
  ### CameraTrack — lookat keyframe animation
118
118
 
119
- A renderer-agnostic state machine for `{ eye, center, up, fov?, halfHeight? }` lookat keyframes. Each field is independently interpolated — eye and center along their own paths, up nlerped on the unit sphere.
119
+ A renderer-agnostic state machine for `{ eye, center, up, fov?, halfHeight?, near, far }` lookat keyframes. Each field is independently interpolated — eye and center along their own paths, up nlerped on the unit sphere, `near` / `far` lerped linearly.
120
120
 
121
121
  ```js
122
122
  import { CameraTrack } from '@nakednous/tree'
@@ -127,7 +127,8 @@ track.add({ eye:[300,-150,0], center:[0,0,0] })
127
127
  track.play({ loop: true, duration: 90 })
128
128
 
129
129
  // per-frame — zero allocation
130
- const out = { eye:[0,0,0], center:[0,0,0], up:[0,1,0], fov:null, halfHeight:null }
130
+ const out = { eye:[0,0,0], center:[0,0,0], up:[0,1,0],
131
+ fov:null, halfHeight:null, near:0.1, far:1000 }
131
132
  track.tick()
132
133
  track.eval(out)
133
134
  // apply: cam.camera(out.eye[0],out.eye[1],out.eye[2],
@@ -150,11 +151,12 @@ track.centerInterp = 'step'
150
151
  `add()` accepts explicit lookat specs or a bulk array:
151
152
 
152
153
  ```js
153
- track.add({ eye, center?, up?, fov?, halfHeight?,
154
+ track.add({ eye, center?, up?, fov?, halfHeight?, near?, far?,
154
155
  eyeTanIn?, eyeTanOut?, centerTanIn?, centerTanOut? })
155
156
  // fov — vertical fov (radians) for perspective
156
157
  // halfHeight — world-unit half-height for ortho
157
- // both nullable; omit to leave projection unchanged
158
+ // fov / halfHeight are nullable omit to leave projection unchanged
159
+ // near / far — clip distances; default 0.1 / 1000
158
160
  // eyeTanIn/Out — Hermite tangents for eye path
159
161
  // centerTanIn/Out — Hermite tangents for center path
160
162
  track.add([ spec, spec, ... ]) // bulk
@@ -162,7 +164,9 @@ track.add([ spec, spec, ... ]) // bulk
162
164
 
163
165
  For matrix-based capture use `track.add({ mat4Model: mat4Eye })` for full-fidelity TRS including roll, or `cam.capturePose()` (p5.tree bridge) for lookat-style capture.
164
166
 
165
- `fov` and `halfHeight` are lerped between keyframes only when both adjacent keyframes carry a non-null value for that field. Mixed or null entries pass `null` through — the bridge leaves the projection unchanged.
167
+ `fov` and `halfHeight` are lerped between keyframes only when both adjacent keyframes carry a non-null value for that field. Mixed or null entries pass `null` through — the bridge leaves the projection unchanged. They are nullable because exactly one is meaningful per keyframe (perspective xor orthographic).
168
+
169
+ `near` and `far` carry real defaults on every keyframe (`0.1` / `1000`, matching the three.js / Bevy conventions) and are therefore lerped linearly between every adjacent pair — no null-passthrough. `cam.capturePose()` extracts them from the camera's own projection matrix (not the renderer's live state), so a round-trip through `add(cam.capturePose())` is exact regardless of which camera is currently active on the renderer.
166
170
 
167
171
  ---
168
172
 
package/dist/index.js CHANGED
@@ -1171,7 +1171,8 @@ function mat4ToRotation(out4, m) {
1171
1171
  * transformToMat4 mat4ToTransform
1172
1172
  * Tracks
1173
1173
  * PoseTrack — { pos, rot, scl } TRS keyframes
1174
- * CameraTrack — { eye, center, up, fov?, halfHeight? } lookat keyframes
1174
+ * CameraTrack — { eye, center, up, fov?, halfHeight?, near, far }
1175
+ * lookat keyframes
1175
1176
  *
1176
1177
  * ── Public path access (all zero-alloc, no cursor side effects) ──────────
1177
1178
  * PoseTrack
@@ -1187,7 +1188,8 @@ function mat4ToRotation(out4, m) {
1187
1188
  * eyeTangents (outIn, outOut, index) vec3 × 2 at keyframe
1188
1189
  * centerTangents (outIn, outOut, index) vec3 × 2 at keyframe
1189
1190
  * eval (out?) { eye, center, up,
1190
- * fov, halfHeight }
1191
+ * fov, halfHeight,
1192
+ * near, far }
1191
1193
  *
1192
1194
  * Two arities for the continuous family:
1193
1195
  * (out) cursor form — reads track.seg / track.f. Useful when the
@@ -1593,6 +1595,11 @@ function _sameTransform(a, b) {
1593
1595
  // S4b Spec parser — CameraTrack
1594
1596
  // =========================================================================
1595
1597
 
1598
+ // Lens defaults used when a spec omits near/far. Match the three.js /
1599
+ // Bevy conventions and are safe for typical p5 v2 scene scales.
1600
+ const _DEFAULT_NEAR = 0.1;
1601
+ const _DEFAULT_FAR = 1000;
1602
+
1596
1603
  /**
1597
1604
  * Parse a CameraTrack keyframe spec into internal form.
1598
1605
  *
@@ -1601,9 +1608,18 @@ function _sameTransform(a, b) {
1601
1608
  * up defaults to [0, 1, 0] and is normalised
1602
1609
  * fov vertical fov (radians), perspective only — null if absent
1603
1610
  * halfHeight world-unit half-height of ortho frustum — null if absent
1611
+ * near near clip distance (positive) — defaults to 0.1
1612
+ * far far clip distance (positive) — defaults to 1000
1604
1613
  * eyeTanIn/Out optional Hermite tangents for the eye path
1605
1614
  * centerTanIn/Out optional Hermite tangents for the center path
1606
1615
  *
1616
+ * fov and halfHeight are mutually exclusive (perspective xor ortho) and
1617
+ * therefore left nullable; eval() lerps each only when both adjacent
1618
+ * keyframes carry a non-null value, passing null through otherwise.
1619
+ *
1620
+ * near and far are always meaningful regardless of projection type, so
1621
+ * they receive real defaults and are linearly interpolated unconditionally.
1622
+ *
1607
1623
  * @param {Object} spec
1608
1624
  * @returns {Object|null} Parsed keyframe or null if eye is missing/malformed.
1609
1625
  */
@@ -1620,6 +1636,8 @@ function _parseCameraSpec(spec) {
1620
1636
  up: [up[0]/ul, up[1]/ul, up[2]/ul],
1621
1637
  fov: typeof spec.fov === 'number' ? spec.fov : null,
1622
1638
  halfHeight: typeof spec.halfHeight === 'number' ? spec.halfHeight : null,
1639
+ near: typeof spec.near === 'number' ? spec.near : _DEFAULT_NEAR,
1640
+ far: typeof spec.far === 'number' ? spec.far : _DEFAULT_FAR,
1623
1641
  eyeTanIn: _parseVec3(spec.eyeTanIn) || null,
1624
1642
  eyeTanOut: _parseVec3(spec.eyeTanOut) || null,
1625
1643
  centerTanIn: _parseVec3(spec.centerTanIn) || null,
@@ -1635,6 +1653,8 @@ function _sameCameraKeyframe(a, b) {
1635
1653
  }
1636
1654
  if (a.fov !== b.fov) return false;
1637
1655
  if (a.halfHeight !== b.halfHeight) return false;
1656
+ if (a.near !== b.near) return false;
1657
+ if (a.far !== b.far) return false;
1638
1658
  return true;
1639
1659
  }
1640
1660
 
@@ -2130,20 +2150,27 @@ class PoseTrack extends Track {
2130
2150
  *
2131
2151
  * Keyframe shape: { eye:[x,y,z], center:[x,y,z], up:[x,y,z],
2132
2152
  * fov?:number, halfHeight?:number,
2153
+ * near:number, far:number,
2133
2154
  * eyeTanIn?:[x,y,z], eyeTanOut?:[x,y,z],
2134
2155
  * centerTanIn?:[x,y,z], centerTanOut?:[x,y,z] }
2135
2156
  *
2136
2157
  * fov — vertical fov (radians) for perspective cameras; null for ortho.
2137
2158
  * halfHeight — world-unit half-height of ortho frustum; null for perspective.
2138
- * Both are optional and nullable. eval() lerps each only when both adjacent
2139
- * keyframes carry a non-null value for that field; mixed or missing entries
2140
- * pass `null` through.
2159
+ * Both are optional and nullable because exactly one is meaningful per
2160
+ * keyframe (perspective xor ortho). eval() lerps each only when both
2161
+ * adjacent keyframes carry a non-null value for that field; mixed or
2162
+ * missing entries pass `null` through.
2163
+ *
2164
+ * near, far — clip plane distances (positive, world units). Always real
2165
+ * numbers. Defaults: near = 0.1, far = 1000 (three.js / Bevy convention).
2166
+ * Linearly interpolated between keyframes without null-passthrough.
2141
2167
  *
2142
2168
  * eyeTanIn/Out and centerTanIn/Out are optional vec3 tangents for Hermite
2143
2169
  * interpolation of the eye and center paths respectively. When absent,
2144
2170
  * centripetal Catmull-Rom tangents are auto-computed at sample time.
2145
2171
  *
2146
- * Missing fields default to: center → [0,0,0], up → [0,1,0].
2172
+ * Missing fields default to: center → [0,0,0], up → [0,1,0],
2173
+ * near → 0.1, far → 1000.
2147
2174
  *
2148
2175
  * For matrix-based capture of a camera-like pose use
2149
2176
  * PoseTrack.add({ mat4Model: mat4Eye }) for full TRS fidelity including roll,
@@ -2300,19 +2327,32 @@ class CameraTrack extends Track {
2300
2327
 
2301
2328
  /**
2302
2329
  * Evaluate interpolated camera pose at current cursor.
2330
+ *
2331
+ * `fov` / `halfHeight` are lerped only when both adjacent keyframes carry
2332
+ * a non-null value; mixed entries pass `null` through so the bridge can
2333
+ * leave the projection unchanged.
2334
+ *
2335
+ * `near` / `far` are always real numbers and are linearly interpolated
2336
+ * unconditionally.
2337
+ *
2303
2338
  * @param {{ eye:number[], center:number[], up:number[],
2304
- * fov:number|null, halfHeight:number|null }} [out]
2339
+ * fov:number|null, halfHeight:number|null,
2340
+ * near:number, far:number }} [out]
2305
2341
  * @returns {{ eye:number[], center:number[], up:number[],
2306
- * fov:number|null, halfHeight:number|null }} out
2342
+ * fov:number|null, halfHeight:number|null,
2343
+ * near:number, far:number }} out
2307
2344
  */
2308
2345
  eval(out) {
2309
- out = out || { eye:[0,0,0], center:[0,0,0], up:[0,1,0], fov:null, halfHeight:null };
2346
+ out = out || { eye:[0,0,0], center:[0,0,0], up:[0,1,0],
2347
+ fov:null, halfHeight:null,
2348
+ near:_DEFAULT_NEAR, far:_DEFAULT_FAR };
2310
2349
  const n = this.keyframes.length;
2311
2350
  if (n === 0) return out;
2312
2351
  if (n === 1) {
2313
2352
  const k = this.keyframes[0];
2314
2353
  this._sampleEyePose(out, 0, 0);
2315
2354
  out.fov = k.fov; out.halfHeight = k.halfHeight;
2355
+ out.near = k.near; out.far = k.far;
2316
2356
  return out;
2317
2357
  }
2318
2358
  const [seg, t] = this._cursorSegT();
@@ -2327,6 +2367,10 @@ class CameraTrack extends Track {
2327
2367
  ? k0.halfHeight + t * (k1.halfHeight - k0.halfHeight)
2328
2368
  : (k0.halfHeight ?? k1.halfHeight ?? null);
2329
2369
 
2370
+ // near / far carry real defaults on every keyframe — always lerp.
2371
+ out.near = k0.near + t * (k1.near - k0.near);
2372
+ out.far = k0.far + t * (k1.far - k0.far);
2373
+
2330
2374
  return out;
2331
2375
  }
2332
2376
  }