p5.tree 0.0.1

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 ADDED
@@ -0,0 +1,307 @@
1
+ # p5.tree
2
+
3
+ Shader development, camera keyframes interpolation and space transformations for [WEBGL](https://p5js.org/reference/#/p5/WEBGL) / WebGPU-ready [p5.js v2](https://beta.p5js.org/).
4
+
5
+ ![A non-Euclidean geometry cube with faces showcasing teapot, bunny, and Buddha models.](p5.tree.png)
6
+
7
+ - [Keyframes interpolation](#keyframes-interpolation)
8
+ - [Recording keyframes](#recording-keyframes)
9
+ - [Playback](#playback)
10
+ - [Seek, stop, reset](#seek-stop-reset)
11
+ - [Space transformations](#space-transformations)
12
+ - [Matrix operations](#matrix-operations)
13
+ - [Matrix queries](#matrix-queries)
14
+ - [Frustum queries](#frustum-queries)
15
+ - [Coordinate space conversions](#coordinate-space-conversions)
16
+ - [Heads Up Display](#heads-up-display)
17
+ - [Utilities](#utilities)
18
+ - [Drawing stuff](#drawing-stuff)
19
+ - [Installation](#installation)
20
+ - [vs-code \& vs-codium \& gitpod hacking instructions](#vs-code--vs-codium--gitpod-hacking-instructions)
21
+
22
+ In `p5.tree`, matrix queries are **immutable** and cache-friendly: they never modify their parameters and always return new `p5.Matrix` instances. For example, `iMatrix` (inverse) does not modify its matrix argument:
23
+
24
+ ```js
25
+ let matrix = createMatrix(4)
26
+ // iMatrix doesn't modify its matrix param, it gives a new value
27
+ let i = iMatrix(matrix)
28
+ // i !== matrix
29
+ ````
30
+
31
+ Most functions are available both as **p5 helpers** (global-style) and as **renderer methods** (`p5.RendererGL`). Camera path methods are available on `p5.Camera`, and also as `p5` helpers that forward to the current active camera.
32
+
33
+ Parameters for `p5.tree` functions can be provided in any order, unless specified otherwise. When a function takes an options/config object, it is always the **last** parameter.
34
+
35
+ # Keyframes interpolation
36
+
37
+ `p5.tree` provides a small camera-path API built on top of `p5.Camera.copy()` snapshots and `p5.Camera.slerp()` interpolation.
38
+
39
+ The path lives in user-space as `camera.path` (an array of `p5.Camera` snapshots). You add keyframes, then play the path at a chosen speed and duration.
40
+
41
+ ## Recording keyframes
42
+
43
+ `camera.addPath(...)` appends a keyframe (camera snapshot) to `camera.path`.
44
+
45
+ **Overloads**
46
+
47
+ 1. `camera.addPath(eye, center, up, [opts])`
48
+ 2. `camera.addPath(view, [opts])`
49
+ 3. `camera.addPath([camera0, camera1, ...], [opts])`
50
+ 4. `camera.addPath([view0, view1, ...], [opts])`
51
+ 5. `camera.addPath([opts])`
52
+
53
+ **Notes**
54
+
55
+ - In **(1)**, `up` is **mandatory** (no default is assumed).
56
+ - In **(2)**, `view` is a `p5.Matrix(4)` or a raw `mat4[16]` representing a world→camera transform.
57
+ - **(3)** appends copies of existing camera snapshots.
58
+ - **(4)** appends copies of existing camera view matrices.
59
+ - **(5)** records a snapshot of the current camera at call time.
60
+
61
+ Where:
62
+
63
+ * `eye`, `center`, `up` are `p5.Vector` or `[x, y, z]`.
64
+ * `view` is a `p5.Matrix` (4x4) or a raw `mat4[16]` representing a **world → camera** transform (like `camera.cameraMatrix`).
65
+ * `opts.reset` (boolean, default `false`) clears the path before appending.
66
+
67
+ **Example: record 3 keyframes**
68
+
69
+ ```js
70
+ let cam
71
+
72
+ function setup() {
73
+ createCanvas(600, 400, WEBGL)
74
+ cam = createCamera()
75
+
76
+ cam.addPath([400, 0, 0], [0, 0, 0], [0, 1, 0])
77
+ cam.addPath(other_cam)
78
+ cam.addPath(viewMatrix)
79
+ }
80
+ ```
81
+
82
+ **p5 wrappers**
83
+
84
+ `addPath(...)` is also available as a `p5` helper; it forwards to the current active camera (the one used for rendering).
85
+
86
+ ```js
87
+ function setup() {
88
+ createCanvas(600, 400, WEBGL)
89
+
90
+ addPath([400, 0, 0], [0, 0, 0], [0, 1, 0])
91
+ addPath(cam)
92
+ addPath(viewMatrix)
93
+ }
94
+ ```
95
+
96
+ ## Playback
97
+
98
+ `camera.playPath(rateOrOpts)` starts (or updates) playback.
99
+
100
+ * `camera.playPath(rate)` where `rate` is a speed multiplier:
101
+
102
+ * `rate > 0` plays forward
103
+ * `rate < 0` plays reverse
104
+ * `rate === 0` stops ticking (equivalent to stopping)
105
+
106
+ * `camera.playPath({ duration, loop, pingPong, onEnd, rate })`
107
+
108
+ Options:
109
+
110
+ * `duration`: **frames per segment** (default `30`)
111
+ * `loop`: wraps at ends (default `false`)
112
+ * `pingPong`: bounces at ends (default `false`)
113
+ * `onEnd`: callback when playback naturally ends (non-looping, non-pingPong)
114
+ * `rate`: speed multiplier (default `1`)
115
+
116
+ If both `pingPong` and `loop` are `true`, `pingPong` takes precedence.
117
+
118
+ **Example: loop the path**
119
+
120
+ ```js
121
+ function setup() {
122
+ createCanvas(600, 400, WEBGL)
123
+ addPath([400, 0, 0], [0, 0, 0], [0, 1, 0], { reset: true })
124
+ addPath(cam)
125
+ addPath(viewMatrix)
126
+
127
+ // 45 frames per segment, loop forever
128
+ playPath({ duration: 45, loop: true })
129
+ }
130
+
131
+ function draw() {
132
+ background(220)
133
+ box(80)
134
+ }
135
+ ```
136
+
137
+ > Projection safety: `p5.Camera.slerp()` requires all keyframes to use the same projection.
138
+ > `p5.tree` enforces this by checking projection matrix compatibility while recording.
139
+
140
+ ## Seek, stop, reset
141
+
142
+ * `camera.seekPath(t)` jumps to a normalized time `t` in `[0, 1]` along the whole path.
143
+ * `camera.stopPath()` stops playback (keeps current camera pose).
144
+ * `camera.resetPath()` clears playback state (keeps keyframes unless you clear them).
145
+
146
+ The same methods exist as `p5` helpers (`seekPath`, `stopPath`, `resetPath`) forwarding to the active camera.
147
+
148
+ ---
149
+
150
+ # Space transformations
151
+
152
+ This section covers matrix operations, matrix/frustum queries, and coordinate space conversions for 3D rendering.
153
+
154
+ ## Matrix operations
155
+
156
+ 1. `createMatrix(...args)`: Explicit wrapper around `new p5.Matrix(...args)` (identity creation).
157
+ 2. `tMatrix(matrix)`: Returns the transpose of `matrix`.
158
+ 3. `iMatrix(matrix)`: Returns the inverse of `matrix`.
159
+ 4. `axbMatrix(a, b)`: Returns the product of the `a` and `b` matrices.
160
+
161
+ **Observation:** all returned matrices are `p5.Matrix` instances.
162
+
163
+ ## Matrix queries
164
+
165
+ 1. `pMatrix()`: Returns the current projection matrix.
166
+ 2. `mvMatrix([{ [vMatrix], [mMatrix] }])`: Returns the modelview matrix.
167
+ 3. `mMatrix()`: Returns the model matrix (local → world), defined by `translate/rotate/scale` and the current `push/pop` stack.
168
+ 4. `eMatrix()`: Returns the current eye matrix (inverse of `vMatrix()`). Also available on `p5.Camera`.
169
+ 5. `vMatrix()`: Returns the view matrix (inverse of `eMatrix()`). Also available on `p5.Camera`.
170
+ 6. `pvMatrix([{ [pMatrix], [vMatrix] }])`: Returns projection × view.
171
+ 7. `ipvMatrix([{ [pMatrix], [vMatrix], [pvMatrix] }])`: Returns `(pvMatrix)⁻¹`.
172
+ 8. `lMatrix([{ [from = createMatrix(4)], [to = this.eMatrix()], [matrix] }])`: Returns the 4×4 matrix that transforms **locations** (points) from `from` to `to`.
173
+ 9. `dMatrix([{ [from = createMatrix(4)], [to = this.eMatrix()], [matrix] }])`: Returns the 3×3 matrix that transforms **directions** (vectors) from `from` to `to` (rotational part only).
174
+ 10. `nMatrix([{ [vMatrix], [mMatrix], [mvMatrix] }])`: Returns the normal matrix.
175
+
176
+ **Observations**
177
+
178
+ 1. All returned matrices are `p5.Matrix` instances.
179
+ 2. Default values (`pMatrix`, `vMatrix`, `pvMatrix`, `eMatrix`, `mMatrix`, `mvMatrix`) are those defined by the renderer at the moment the query is issued.
180
+
181
+ ## Frustum queries
182
+
183
+ 1. `lPlane()`, `rPlane()`, `bPlane()`, `tPlane()`
184
+ 2. `nPlane()`, `fPlane()`
185
+ 3. `fov()`: vertical field-of-view (radians).
186
+ 4. `hfov()`: horizontal field-of-view (radians).
187
+ 5. `isOrtho()`: `true` for orthographic, `false` for perspective.
188
+
189
+ ## Coordinate space conversions
190
+
191
+ 1. `transformPosition(point = p5.Tree.ORIGIN, [{ [from = p5.Tree.EYE], [to = p5.Tree.WORLD], [pMatrix], [vMatrix], [eMatrix], [pvMatrix], [ipvMatrix] }])`
192
+ 2. `transformDirection(vector = p5.Tree._k, [{ [from = p5.Tree.EYE], [to = p5.Tree.WORLD], [vMatrix], [eMatrix], [pMatrix] }])`
193
+
194
+ Pass matrix parameters when you have **cached** those matrices (see [Matrix queries](#matrix-queries)) to speed up repeated conversions:
195
+
196
+ ```js
197
+ let cachedPVI
198
+
199
+ function draw() {
200
+ cachedPVI = ipvMatrix() // compute once per frame
201
+
202
+ // many fast conversions using the cached matrix
203
+ const a = transformPosition([0, 0, 0], { from: p5.Tree.WORLD, to: p5.Tree.SCREEN, ipvMatrix: cachedPVI })
204
+ const b = transformPosition([100, 0, 0], { from: p5.Tree.WORLD, to: p5.Tree.SCREEN, ipvMatrix: cachedPVI })
205
+ // ...
206
+ }
207
+ ```
208
+
209
+ You can also convert between **local spaces** by passing a `p5.Matrix` as `from` / `to`:
210
+
211
+ ```js
212
+ let model
213
+
214
+ function draw() {
215
+ background(0)
216
+
217
+ push()
218
+ translate(80, 0, 0)
219
+ rotateY(frameCount * 0.01)
220
+ model = mMatrix()
221
+ box(40)
222
+ pop()
223
+
224
+ // screen projection of the model origin
225
+ const s = transformPosition(p5.Tree.ORIGIN, { from: model, to: p5.Tree.SCREEN })
226
+ beginHUD()
227
+ bullsEye({ x: s.x, y: s.y, size: 30 })
228
+ endHUD()
229
+ }
230
+ ```
231
+
232
+ **Observations**
233
+
234
+ 1. Returned vectors are `p5.Vector` instances.
235
+ 2. `from` and `to` may be matrices or any of: `p5.Tree.WORLD`, `p5.Tree.EYE`, `p5.Tree.SCREEN`, `p5.Tree.NDC`, `p5.Tree.MODEL`.
236
+ 3. When no matrix params are passed, current renderer values are used.
237
+ 4. The default `transformPosition()` call (i.e. eye → world at origin) returns the camera world position.
238
+ 5. The default `transformDirection()` call returns the normalized camera viewing direction.
239
+ 6. Useful vector constants: `p5.Tree.ORIGIN`, `p5.Tree._k`, `p5.Tree.i`, `p5.Tree.j`, `p5.Tree.k`, `p5.Tree._i`, `p5.Tree._j`.
240
+
241
+ ## Heads Up Display
242
+
243
+ 1. `beginHUD()`: begins HUD mode, so geometry specified between `beginHUD()` and `endHUD()` is in window space.
244
+ 2. `endHUD()`: ends HUD mode.
245
+
246
+ In HUD space: `(x, y) ∈ [0, width] × [0, height]` (origin at the top-left, y down), matching `image()` / 2D drawing coordinates.
247
+
248
+ ---
249
+
250
+ # Utilities
251
+
252
+ A small collection of helpers commonly needed in interactive 3D sketches:
253
+
254
+ 1. `texOffset(image)`: `[1 / image.width, 1 / image.height]`
255
+ 2. `mousePosition([flip = true])`: pixel-density-aware mouse position (optionally flips Y).
256
+ 3. `pointerPosition(pointerX, pointerY, [flip = true])`: pixel-density-aware pointer position (optionally flips Y).
257
+ 4. `resolution()`: pixel-density-aware canvas resolution `[pd * width, pd * height]`
258
+ 5. `pixelRatio(location)`: world-to-pixel ratio at a world location.
259
+ 6. `mousePicking([{ ... }])` and `pointerPicking(pointerX, pointerY, [{ ... }])`: hit-test a screen-space circle/square tied to a model matrix.
260
+ 7. `bounds([{ [eMatrix], [vMatrix] }])`: frustum planes in general form `ax + by + cz + d = 0`.
261
+ 8. `visibility({ ... })`: returns `p5.Tree.VISIBLE`, `p5.Tree.INVISIBLE`, or `p5.Tree.SEMIVISIBLE`.
262
+
263
+ # Drawing stuff
264
+
265
+ Debug / teaching primitives for visualizing common 3D concepts:
266
+
267
+ 1. `axes({ size, colors, bits })`
268
+ 2. `grid({ size, subdivisions, style })`
269
+ 3. `cross({ mMatrix, x, y, size, ... })`
270
+ 4. `bullsEye({ mMatrix, x, y, size, shape, ... })`
271
+ 5. `viewFrustum({ pg, bits, viewer, eMatrix, pMatrix, vMatrix })`
272
+
273
+ ---
274
+
275
+ # Installation
276
+
277
+ Link `p5.tree.js` after you have linked in `p5.js`. For example:
278
+
279
+ ```html
280
+ <!doctype html>
281
+ <html>
282
+ <head>
283
+ <script src="p5.js"></script>
284
+ <script src="p5.sound.js"></script>
285
+ <script src=https://cdn.jsdelivr.net/gh/VisualComputing/p5.tree/p5.tree.js></script>
286
+ <script src="sketch.js"></script>
287
+ </head>
288
+ <body>
289
+ </body>
290
+ </html>
291
+ ```
292
+
293
+ To include the minified build (when available):
294
+
295
+ ```html
296
+ <script src=https://cdn.jsdelivr.net/gh/VisualComputing/p5.tree/p5.tree.min.js></script>
297
+ ```
298
+
299
+ # [vs-code](https://code.visualstudio.com/) & [vs-codium](https://vscodium.com/) & [gitpod](https://www.gitpod.io/) hacking instructions
300
+
301
+ Clone the repo (`git clone https://github.com/VisualComputing/p5.tree`) and open it with your favorite editor.
302
+
303
+ If you are developing against p5 v2, keep an eye on:
304
+
305
+ 1. [Creating libraries](https://beta.p5js.org/contribute/creating_libraries/)
306
+ 2. [p5.js source architecture](https://github.com/processing/p5.js/blob/main/src/core/README.md)
307
+ 3. [WEBGL mode architecture](https://github.com/processing/p5.js/blob/main/contributor_docs/webgl_mode_architecture.md)