Skip to content

Commit 3e63de6

Browse files
committed
bridge: move out into opts on mapLocation / mapDirection
mapLocation(point?, opts?) and mapDirection(dir?, opts?) no longer take out as a positional first argument. opts.out accepts the same types as before (Float32Array | ArrayLike | p5.Vector). When opts.out is absent a fresh p5.Vector is allocated and returned, giving an ergonomic non-hot-path call style with no ambiguity. Internal callers in gizmos.js and picking.js updated: _sl and _wl scratch buffers are now passed as opts.out, preserving zero-alloc. _resolveOut(opts) extracted as a shared one-liner inside installMatrix.
1 parent 341ceec commit 3e63de6

File tree

5 files changed

+293
-398
lines changed

5 files changed

+293
-398
lines changed

README.md

Lines changed: 64 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -244,31 +244,31 @@ Accepted types for `out` and override params: `Float32Array` | `ArrayLike` | `p5
244244
**Simple queries** — read from live renderer state:
245245
246246
```js
247-
eMatrix(out) // eye matrix (inverse view) — eye→world
248-
pMatrix(out) // projection matrix
249-
vMatrix(out) // view matrix — world→eye
250-
mMatrix(out) // model matrix — local→world
247+
mat4Eye(out) // eye matrix (inverse view) — eye→world
248+
mat4Proj(out) // projection matrix
249+
mat4View(out) // view matrix — world→eye
250+
mat4Model(out) // model matrix — local→world
251251
```
252252
253253
**Composite queries** — `out` first, optional overrides in an opts object:
254254
255255
```js
256-
pvMatrix(out, [{ pMatrix, vMatrix }])
257-
ipvMatrix(out, [{ pMatrix, vMatrix, pvMatrix }])
258-
mvMatrix(out, [{ mMatrix, vMatrix }])
259-
pmvMatrix(out, [{ pMatrix, mMatrix, vMatrix }])
260-
nMatrix(out, [{ mMatrix, vMatrix, mvMatrix }]) // 9-element out
256+
mat4PV(out, [{ mat4Proj, mat4View }])
257+
mat4PVInv(out, [{ mat4Proj, mat4View, mat4PV }])
258+
mat4MV(out, [{ mat4Model, mat4View }])
259+
mat4PMV(out, [{ mat4Proj, mat4Model, mat4View }])
260+
mat3Normal(out,[{ mat4Model, mat4View, mat4MV }]) // 9-element out
261261
mat4Location(out, from, to) // location transform: inv(to) · from
262262
mat3Direction(out, from, to) // direction transform: to₃ · inv(from₃), 9-element out
263263
```
264264
265265
**Raw matrix math** — forwarded from `@nakednous/tree`, same out-first contract:
266266
267267
```js
268-
mat4Mul(out, A, B) // out = A · B (column-major)
269-
mat4Invert(out, src) // out = inv(src), null if singular
270-
mat4MulPoint(out, m, point) // out = m · [x,y,z,1] perspective-divided
271-
// point: Float32Array | ArrayLike | p5.Vector
268+
mat4Mul(out, A, B) // out = A · B (column-major)
269+
mat4Invert(out, src) // out = inv(src), null if singular
270+
mat4MulPoint(out, m, point) // out = m · [x,y,z,1] perspective-divided
271+
// point: Float32Array | ArrayLike | p5.Vector
272272
mat4MulDir(out, m, dx,dy,dz) // out = 3×3 block of m applied to direction
273273
// no translation, no perspective divide
274274
```
@@ -284,62 +284,71 @@ const wlm = new Float32Array(16) // e.g. bias · lightPV for shadow mapping
284284
const pt = new Float32Array(3)
285285

286286
// draw — zero allocations
287-
eMatrix(e)
288-
pMatrix(pm)
289-
pvMatrix(pv)
287+
mat4Eye(e)
288+
mat4Proj(pm)
289+
mat4PV(pv)
290290
mat4Mul(wlm, biasMatrix, pv)
291291
mat4MulPoint(pt, wlm, lightPosition)
292-
viewFrustum({ eMatrix: e, pMatrix: pm })
293-
mouseHit({ pvMatrix: pv, eMatrix: e })
292+
viewFrustum({ mat4Eye: e, mat4Proj: pm })
293+
mouseHit({ mat4PV: pv, mat4Eye: e })
294294
```
295295
296296
## Frustum queries
297297
298298
Scalars read directly from the projection matrix — no buffer needed:
299299
300300
```js
301-
lPlane() rPlane() bPlane() tPlane() // side planes
302-
nPlane() fPlane() // near / far
303-
fov() hfov() // field of view (radians)
304-
isOrtho() // true for orthographic
301+
projLeft() projRight() projBottom() projTop() // side planes
302+
projNear() projFar() // near / far
303+
projFov() projHfov() // field of view (radians)
304+
projIsOrtho() // true for orthographic
305305

306-
pixelRatio([worldPos], [{ pMatrix, vMatrix }])
306+
pixelRatio([worldPos], [{ mat4Proj, mat4View }])
307307
// world-units-per-pixel at worldPos (defaults to camera position)
308308
```
309309
310310
## Coordinate space conversions
311311
312+
`out` is opt-in. When provided via `opts.out` the result is written into it (zero-alloc hot path). When omitted a fresh `p5.Vector` is allocated and returned. Return type matches `opts.out`.
313+
312314
```js
313-
mapLocation(out, point, [opts]) // map a point between spaces
314-
mapLocation(out, [opts]) // input defaults to p5.Tree.ORIGIN
315-
mapLocation(out) // defaults to ORIGIN, EYE → WORLD
315+
mapLocation([point], [opts]) // map a point between spaces
316+
mapLocation([opts]) // input defaults to p5.Tree.ORIGIN
317+
mapLocation() // ORIGIN, EYE → WORLD → p5.Vector
316318

317-
mapDirection(out, vector, [opts]) // map a direction between spaces
318-
mapDirection(out, [opts]) // input defaults to p5.Tree._k
319-
mapDirection(out) // defaults to _k, EYE → WORLD
319+
mapDirection([dir], [opts]) // map a direction between spaces
320+
mapDirection([opts]) // input defaults to p5.Tree._k
321+
mapDirection() // _k, EYE → WORLD → p5.Vector
320322
```
321323
322-
`out` is a 3-element `Float32Array`, `ArrayLike`, or `p5.Vector`.
324+
`point` / `dir` accept `Float32Array` | `ArrayLike` | `p5.Vector`.
323325
324-
| Option | Default | Description |
325-
|-------------|-------------------|-----------------------------------------|
326-
| `from` | `p5.Tree.EYE` | Source space (constant or matrix). |
327-
| `to` | `p5.Tree.WORLD` | Target space (constant or matrix). |
328-
| `eMatrix` | current eye | Pre-computed eye matrix. |
329-
| `pMatrix` | current proj | Override projection matrix. |
330-
| `vMatrix` | current view | Override view matrix. |
331-
| `pvMatrix` | P·V | Pre-computed PV — skips multiply. |
332-
| `ipvMatrix` | inv(PV) | Pre-computed IPV — skips inversion. |
326+
| Option | Default | Description |
327+
|--------------|-------------------|-------------------------------------------------|
328+
| `out` | new p5.Vector() | Destination buffer — omit to allocate p5.Vector.|
329+
| `from` | `p5.Tree.EYE` | Source space (constant or matrix). |
330+
| `to` | `p5.Tree.WORLD` | Target space (constant or matrix). |
331+
| `mat4Eye` | current eye | Pre-computed eye matrix. |
332+
| `mat4Proj` | current proj | Override projection matrix. |
333+
| `mat4View` | current view | Override view matrix. |
334+
| `mat4PV` | P·V | Pre-computed PV — skips multiply. |
335+
| `mat4PVInv` | inv(PV) | Pre-computed IPV — skips inversion. |
333336
334337
`from` / `to` accept: `p5.Tree.WORLD`, `EYE`, `SCREEN`, `NDC`, `MODEL`, or a mat4 for a custom local frame.
335338
336339
```js
337-
const loc = new Float32Array(3)
338-
const dir = new Float32Array(3)
340+
// ergonomic — allocates p5.Vector
341+
const eye = mapLocation() // camera world position
342+
const fwd = mapDirection() // camera look direction
343+
const scr = mapLocation([100,0,0], { from: p5.Tree.WORLD,
344+
to: p5.Tree.SCREEN })
339345

340-
mapLocation(loc) // camera world position
341-
mapDirection(dir) // camera view direction
342-
mapLocation(loc, [100,0,0], { from: p5.Tree.WORLD, to: p5.Tree.SCREEN })
346+
// hot path — zero allocation
347+
const loc = new Float32Array(3)
348+
const pv = new Float32Array(16)
349+
mat4PV(pv)
350+
mapLocation([100,0,0], { from: p5.Tree.WORLD, to: p5.Tree.SCREEN,
351+
out: loc, mat4PV: pv })
343352
```
344353
345354
Constants: `p5.Tree.ORIGIN`, `p5.Tree.i`, `p5.Tree.j`, `p5.Tree.k`, `p5.Tree._i`, `p5.Tree._j`, `p5.Tree._k`.
@@ -566,15 +575,15 @@ pop()
566575
567576
Both accept the same options object:
568577
569-
| Option | Default | Description |
570-
|------------|------------------|------------------------------------------------|
571-
| `mMatrix` | current model | Override model matrix. |
572-
| `size` | `50` | Hit radius (world units, auto-scaled by depth).|
573-
| `shape` | `p5.Tree.CIRCLE` | `CIRCLE` or `SQUARE`. |
574-
| `eMatrix` | current eye | Pre-computed eye matrix. |
575-
| `pMatrix` | current proj | Override projection. |
576-
| `vMatrix` | current view | Override view. |
577-
| `pvMatrix` | P·V | Pre-computed PV. |
578+
| Option | Default | Description |
579+
|-------------|------------------|------------------------------------------------|
580+
| `mat4Model` | current model | Override model matrix. |
581+
| `size` | `50` | Hit radius (world units, auto-scaled by depth).|
582+
| `shape` | `p5.Tree.CIRCLE` | `CIRCLE` or `SQUARE`. |
583+
| `mat4Eye` | current eye | Pre-computed eye matrix. |
584+
| `mat4Proj` | current proj | Override projection. |
585+
| `mat4View` | current view | Override view. |
586+
| `mat4PV` | P·V | Pre-computed PV. |
578587
579588
---
580589
@@ -630,11 +639,11 @@ p5.Tree.INVISIBLE
630639
Scene-space diagnostic helpers — drawn to understand the scene, not to build it.
631640
632641
```js
633-
axes([{ size, bits, mMatrix, eMatrix, pMatrix, vMatrix, pvMatrix }])
642+
axes([{ size, bits, mat4Model, mat4Eye, mat4Proj, mat4View, mat4PV }])
634643
grid([{ size, subdivisions }])
635644
bullsEye([{ size, shape }])
636645
cross([{ size }])
637-
viewFrustum({ pg, eMatrix, pMatrix, vMatrix, bits, viewer })
646+
viewFrustum({ pg, mat4Eye, mat4Proj, mat4View, bits, viewer })
638647
```
639648
640649
`axes` bits: `p5.Tree.X`, `p5.Tree._X`, `p5.Tree.Y`, `p5.Tree._Y`, `p5.Tree.Z`, `p5.Tree._Z`, `p5.Tree.LABELS`.

deps/tree/README.md

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -219,24 +219,48 @@ One-keyframe behaviour: `play()` with exactly one keyframe snaps `eval()` to tha
219219
220220
**Spaces:** `WORLD`, `EYE`, `SCREEN`, `NDC`, `MODEL`, `MATRIX` (custom frame).
221221
222-
**NDC convention:** `WEBGL = -1` (z ∈ [−1,1]), `WEBGPU = 0` (z ∈ [0,1]).
222+
#### Conventions
223+
224+
Three independent conventions are controlled by caller-supplied parameters:
225+
226+
**NDC Z** — passed as `ndcZMin`:
227+
```
228+
WEBGL =1 z ∈ [−1, 1]
229+
WEBGPU = 0 z ∈ [ 0, 1]
230+
```
231+
232+
**Viewport** — `vp = [x, y, w, h]` with signed `h`:
233+
```
234+
h < 0 screen y-down (DOM / p5 mouseX·mouseY) → [0, canvasH, canvasW, −canvasH]
235+
h > 0 screen y-up (OpenGL gl_FragCoord) → [0, 0, canvasW, canvasH]
236+
```
237+
The sign of `h` is the only thing that differs — no branching, no flags.
238+
239+
**NDC Y** — controlled by `ndcYSign` in the projection constructors (`form.js`):
240+
```
241+
+1 NDC y-up (default) — OpenGL / WebGL / WebGPU / Three.js / p5v2
242+
1 NDC y-down — native Vulkan clip space
243+
```
244+
245+
#### Usage
223246
224247
```js
225248
import { mapLocation, mapDirection, WORLD, SCREEN, WEBGL } from '@nakednous/tree'
226249

227250
const out = new Float32Array(3)
228251
const m = {
229-
pMatrix: /* Float32Array(16) — projection */,
230-
vMatrix: /* Float32Array(16) — view (worldeye) */,
231-
pvMatrix: /* pMatrix × vMatrix — optional, computed if absent */,
232-
ipvMatrix: /* inv(pvMatrix) — optional, computed if absent */,
252+
mat4Proj: /* Float32Array(16) — projection (eye → clip) */,
253+
mat4View: /* Float32Array(16) — view (worldeye) */,
254+
mat4PV?: /* mat4Proj × mat4View — optional, computed if absent */,
255+
mat4PVInv?: /* inv(mat4PV) — optional, computed if absent */,
233256
}
234-
const vp = [0, height, width, -height]
257+
const vp = [0, height, width, -height] // signed h = screen y-down
235258

236259
mapLocation(out, worldX, worldY, worldZ, WORLD, SCREEN, m, vp, WEBGL)
237260
```
238261
239-
The matrices bag `m` is assembled by the host (p5.tree reads live renderer state into it). All pairs are supported: WORLD↔EYE, WORLD↔SCREEN, WORLD↔NDC, EYE↔SCREEN, SCREEN↔NDC, WORLD↔MATRIX, and their reverses.
262+
The matrices bag `m` is assembled by the host. All pairs are supported:
263+
WORLD↔EYE, WORLD↔SCREEN, WORLD↔NDC, EYE↔SCREEN, SCREEN↔NDC, WORLD↔MATRIX, and their reverses.
240264
241265
---
242266
@@ -289,14 +313,14 @@ mat4PV mat4MV
289313
**Matrix construction from specs** (`form.js`):
290314
```
291315
mat4FromBasis — rigid frame from orthonormal basis + translation
292-
mat4View — view matrix (world→eye) from lookat params
293-
mat4Eye — eye matrix (eye→world) from lookat params
316+
mat4View — view matrix (world→eye) from lookat params
317+
mat4Eye — eye matrix (eye→world) from lookat params
294318
mat4FromTRS — column-major mat4 from flat TRS scalars
295319
mat4FromTranslation — translation-only mat4
296320
mat4FromScale — scale-only mat4
297-
mat4Perspective — perspective projection
298-
mat4Ortho — orthographic projection
299-
mat4Frustum — off-centre perspective projection
321+
mat4Perspective — perspective projection (ndcZMin, ndcYSign)
322+
mat4Ortho — orthographic projection (ndcZMin, ndcYSign)
323+
mat4Frustum — off-centre perspective (ndcZMin, ndcYSign)
300324
mat4Bias — NDC→texture/UV remap [0,1] for shadow mapping
301325
mat4Reflect — reflection across a plane
302326
mat4ToTranslation — extract translation (col 3)
@@ -312,7 +336,7 @@ projLeft projRight projTop projBottom
312336
313337
**Pixel ratio:** `pixelRatio(proj, vpH, eyeZ, ndcZMin)` — world-units-per-pixel at a given depth, handles both perspective and orthographic.
314338
315-
**Pick matrix:** `mat4Pick(proj, px, py, W, H)` — mutates a projection mat4 in-place so that pixel `(px, py)` maps to the full NDC square. Used by the p5.tree GPU color-ID picking implementation. Convention-independent (perspective and orthographic).
339+
**Pick matrix:** `mat4Pick(proj, px, py, vp)` — mutates a projection matrix in-place so that the pixel at `(px, py)` maps to the full NDC square, making a 1×1 FBO render contain exactly that pixel. Takes the same signed viewport `vp` as `mapLocation` — the y-convention is preserved automatically.
316340
317341
---
318342
@@ -337,7 +361,7 @@ ORIGIN, i, j, k, _i, _j, _k
337361
338362
## Performance contract
339363
340-
All hot-path functions follow an **out-first, zero-allocation** contract:
364+
All functions in this package follow an **out-first, zero-allocation** contract:
341365
342366
- `out` is the first parameter — the caller owns the buffer
343367
- the function writes into `out` and returns it
@@ -346,15 +370,15 @@ All hot-path functions follow an **out-first, zero-allocation** contract:
346370
347371
```js
348372
// allocate once
349-
const out = new Float32Array(3)
350-
const pvMatrix = new Float32Array(16)
351-
const ipvMatrix= new Float32Array(16)
373+
const out = new Float32Array(3)
374+
const mat4PV = new Float32Array(16)
375+
const mat4PVInv = new Float32Array(16)
352376

353377
// per frame — zero allocation
354-
mat4Mul(pvMatrix, proj, view)
355-
mat4Invert(ipvMatrix, pvMatrix)
378+
mat4Mul(mat4PV, proj, view)
379+
mat4Invert(mat4PVInv, mat4PV)
356380
mapLocation(out, px, py, pz, WORLD, SCREEN,
357-
{ pMatrix: proj, vMatrix: view, pvMatrix, ipvMatrix }, vp, WEBGL)
381+
{ mat4Proj: proj, mat4View: view, mat4PV, mat4PVInv }, vp, WEBGL)
358382
```
359383
360384
---

0 commit comments

Comments
 (0)