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/LICENSE +661 -0
- package/README.md +307 -0
- package/dist/p5.tree.esm.js +2751 -0
- package/dist/p5.tree.esm.js.map +1 -0
- package/dist/p5.tree.js +2754 -0
- package/dist/p5.tree.js.map +1 -0
- package/package.json +46 -0
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
|
+

|
|
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)
|