@nakednous/tree 0.0.21 → 0.0.22
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 +9 -5
- package/dist/index.js +53 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
|
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],
|
|
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
|
-
//
|
|
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
|
|
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
|
|
2139
|
-
*
|
|
2140
|
-
*
|
|
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
|
|
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
|
|
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],
|
|
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
|
}
|