@nakednous/tree 0.0.7 → 0.0.9

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
@@ -58,11 +58,14 @@ track.eval(out) // writes interpolated TRS into out
58
58
  Interpolation modes:
59
59
 
60
60
  ```js
61
- track.posInterp = 'catmullrom' // default — centripetal Catmull-Rom
61
+ track.posInterp = 'hermite' // default — cubic Hermite; auto-computes centripetal
62
+ // Catmull-Rom tangents when none are stored
62
63
  track.posInterp = 'linear'
64
+ track.posInterp = 'step' // snap to k0; useful for discrete state changes
63
65
 
64
- track.rotInterp = 'slerp' // default — constant angular velocity
65
- track.rotInterp = 'nlerp' // normalised lerp; cheaper, slightly non-constant
66
+ track.rotInterp = 'slerp' // default — constant angular velocity
67
+ track.rotInterp = 'nlerp' // normalised lerp; cheaper, slightly non-constant speed
68
+ track.rotInterp = 'step' // snap to k0 quaternion
66
69
  ```
67
70
 
68
71
  Playback features: signed `rate` (negative reverses), `loop`, `pingPong`, `seek(t)` scrubbing, and lifecycle hooks (`onPlay`, `onEnd`, `onStop`). `_onActivate` / `_onDeactivate` are lib-space hooks for the host layer's draw-loop registry — not for user code.
@@ -70,9 +73,19 @@ Playback features: signed `rate` (negative reverses), `loop`, `pingPong`, `seek(
70
73
  `add()` accepts flexible specs. Top-level forms:
71
74
 
72
75
  ```js
73
- track.add({ pos, rot, scl }) // explicit TRS — rot accepts any form below
74
- track.add({ mMatrix: mat4 }) // decompose a column-major model matrix into TRS
75
- track.add([ spec, spec, ... ]) // bulk
76
+ track.add({ pos, rot, scl }) // explicit TRS — rot accepts any form below
77
+ track.add({ pos, rot, scl, tanIn, tanOut }) // with Hermite tangents (vec3, optional)
78
+ track.add({ mMatrix: mat4 }) // decompose a column-major model matrix into TRS
79
+ track.add([ spec, spec, ... ]) // bulk
80
+ ```
81
+
82
+ `tanIn` is the incoming position tangent at this keyframe; `tanOut` is the outgoing tangent. When only one is given, the other mirrors it. When neither is given, centripetal Catmull-Rom tangents are auto-computed from neighboring keyframes — identical to prior default behavior.
83
+
84
+ ```js
85
+ track.add({ pos:[0,0,0] }) // auto tangents
86
+ track.add({ pos:[100,0,0], tanOut:[0,50,0] }) // leave heading +Y
87
+ track.add({ pos:[200,0,0], tanIn:[0,50,0], tanOut:[-30,0,0] }) // arrive from +Y, leave heading -X
88
+ track.add({ pos:[300,0,0] }) // auto tangents
76
89
  ```
77
90
 
78
91
  `rot` sub-forms — all normalised internally:
@@ -94,7 +107,7 @@ rot: { eMatrix: mat4 } // rotation block of an eye ma
94
107
 
95
108
  ### CameraTrack — lookat keyframe animation
96
109
 
97
- A renderer-agnostic state machine for `{ eye, center, up }` lookat keyframes. Each field is independently interpolated — eye and center along their own paths, up nlerped on the unit sphere.
110
+ 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.
98
111
 
99
112
  ```js
100
113
  import { CameraTrack } from '@nakednous/tree'
@@ -105,7 +118,7 @@ track.add({ eye:[300,-150,0], center:[0,0,0] })
105
118
  track.play({ loop: true, duration: 90 })
106
119
 
107
120
  // per-frame — zero allocation
108
- const out = { eye:[0,0,0], center:[0,0,0], up:[0,1,0] }
121
+ const out = { eye:[0,0,0], center:[0,0,0], up:[0,1,0], fov:null, halfHeight:null }
109
122
  track.tick()
110
123
  track.eval(out)
111
124
  // apply: cam.camera(out.eye[0],out.eye[1],out.eye[2],
@@ -116,17 +129,25 @@ track.eval(out)
116
129
  Interpolation modes:
117
130
 
118
131
  ```js
119
- track.eyeInterp = 'catmullrom' // default
132
+ track.eyeInterp = 'hermite' // default — auto-CR tangents when none stored
120
133
  track.eyeInterp = 'linear'
134
+ track.eyeInterp = 'step'
121
135
 
122
- track.centerInterp = 'linear' // default — suits fixed lookat targets
123
- track.centerInterp = 'catmullrom' // smoother when center is also moving freely
136
+ track.centerInterp = 'linear' // default — suits fixed lookat targets
137
+ track.centerInterp = 'hermite' // smoother when center is also moving freely
138
+ track.centerInterp = 'step'
124
139
  ```
125
140
 
126
141
  `add()` accepts:
127
142
 
128
143
  ```js
129
- track.add({ eye, center?, up? }) // explicit lookat; center defaults to [0,0,0], up to [0,1,0]
144
+ track.add({ eye, center?, up?, fov?, halfHeight?,
145
+ eyeTanIn?, eyeTanOut?, centerTanIn?, centerTanOut? })
146
+ // fov — vertical fov (radians) for perspective
147
+ // halfHeight — world-unit half-height for ortho
148
+ // both nullable; omit to leave projection unchanged
149
+ // eyeTanIn/Out — Hermite tangents for eye path
150
+ // centerTanIn/Out — Hermite tangents for center path
130
151
  track.add({ vMatrix: mat4 }) // view matrix (world→eye); eye reconstructed
131
152
  track.add({ eMatrix: mat4 }) // eye matrix (eye→world); eye read from col3
132
153
  track.add([ spec, spec, ... ]) // bulk
@@ -134,6 +155,8 @@ track.add([ spec, spec, ... ]) // bulk
134
155
 
135
156
  Note: both matrix forms default `up` to `[0,1,0]`. The matrix col1 (up_ortho) is intentionally not used — it differs from the hint for upright cameras and would shift orbitControl's orbit reference. Use `capturePose()` (p5.tree bridge) when the real up hint is needed.
136
157
 
158
+ `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.
159
+
137
160
  ---
138
161
 
139
162
  ### Shared Track transport
@@ -234,7 +257,7 @@ qFromAxisAngle qFromLookDir qFromRotMat3x3 qFromMat4 qToMat4
234
257
  quatToAxisAngle
235
258
  ```
236
259
 
237
- **Spline / vector:** `catmullRomVec3`, `lerpVec3`
260
+ **Spline / vector:** `hermiteVec3`, `lerpVec3`
238
261
 
239
262
  **Mat4:**
240
263
  ```
@@ -301,7 +324,7 @@ mapLocation(out, px, py, pz, WORLD, SCREEN,
301
324
 
302
325
  ## Relationship to `p5.tree`
303
326
 
304
- [p5.tree](https://github.com/VisualComputing/p5.tree) is the bridge layer. It reads live renderer state (camera matrices, viewport dimensions, NDC convention) and passes it to `@nakednous/tree` functions. It wires `PoseTrack` and `CameraTrack` to the p5 draw loop, exposes `createTrack` / `getCamera`, and provides `createPanel` for transport and parameter UIs.
327
+ [p5.tree](https://github.com/VisualComputing/p5.tree) is the bridge layer. It reads live renderer state (camera matrices, viewport dimensions, NDC convention) and passes it to `@nakednous/tree` functions. It wires `PoseTrack` and `CameraTrack` to the p5 draw loop, exposes `createPoseTrack` / `createCameraTrack` / `getCamera`, and provides `createPanel` for transport and parameter UIs.
305
328
 
306
329
  `@nakednous/tree` provides the algorithms. The bridge provides the wiring.
307
330