@pluot/core 0.1.0 → 0.1.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.
Files changed (34) hide show
  1. package/README.md +1 -0
  2. package/dist/index.js +1109 -4101
  3. package/dist-tsc/functional-3d-view-controls.d.ts +11 -0
  4. package/dist-tsc/functional-3d-view-controls.d.ts.map +1 -0
  5. package/dist-tsc/functional-3d-view-controls.js +517 -0
  6. package/dist-tsc/functional-dom-2d-camera.d.ts +9 -0
  7. package/dist-tsc/functional-dom-2d-camera.d.ts.map +1 -0
  8. package/dist-tsc/functional-dom-2d-camera.js +178 -0
  9. package/dist-tsc/index.d.ts +2 -2
  10. package/dist-tsc/index.d.ts.map +1 -1
  11. package/dist-tsc/index.js +2 -2
  12. package/dist-tsc/lru-store.d.ts.map +1 -1
  13. package/dist-tsc/lru-store.js +3 -0
  14. package/dist-tsc/unrolled-3d-view-controls.d.ts +2 -0
  15. package/dist-tsc/unrolled-3d-view-controls.d.ts.map +1 -0
  16. package/dist-tsc/unrolled-3d-view-controls.js +637 -0
  17. package/dist-tsc/unrolled-dom-2d-camera.d.ts +2 -0
  18. package/dist-tsc/unrolled-dom-2d-camera.d.ts.map +1 -0
  19. package/dist-tsc/unrolled-dom-2d-camera.js +193 -0
  20. package/dist-tsc/viewport.d.ts +91 -0
  21. package/dist-tsc/viewport.d.ts.map +1 -1
  22. package/dist-tsc/viewport.js +91 -3
  23. package/dist-tsc/viewport.test.d.ts +2 -0
  24. package/dist-tsc/viewport.test.d.ts.map +1 -0
  25. package/dist-tsc/viewport.test.js +184 -0
  26. package/package.json +8 -2
  27. package/src/functional-3d-view-controls.ts +585 -0
  28. package/src/functional-dom-2d-camera.ts +214 -0
  29. package/src/index.ts +2 -2
  30. package/src/lru-store.ts +3 -0
  31. package/src/viewport.test.ts +262 -0
  32. package/src/viewport.ts +91 -3
  33. package/src/3d-view-controls.js +0 -271
  34. package/src/dom-2d-camera.js +0 -441
@@ -0,0 +1,178 @@
1
+ // Functional/stateless adaptation of dom-2d-camera.
2
+ //
3
+ // License copied from https://github.com/flekschas/dom-2d-camera/blob/master/LICENSE.md
4
+ //
5
+ // This software is released under the MIT license:
6
+ //
7
+ // Permission is hereby granted, free of charge, to any person obtaining a copy of
8
+ // this software and associated documentation files (the "Software"), to deal in
9
+ // the Software without restriction, including without limitation the rights to
10
+ // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11
+ // the Software, and to permit persons to whom the Software is furnished to do so,
12
+ // subject to the following conditions:
13
+ //
14
+ // The above copyright notice and this permission notice shall be included in all
15
+ // copies or substantial portions of the Software.
16
+ //
17
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19
+ // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20
+ // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21
+ // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22
+ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+ import { mat4, vec2 } from 'gl-matrix';
24
+ // Camera configuration constants (matching dom-2d-camera defaults)
25
+ const isFixed = false;
26
+ const isNdc = true;
27
+ const isPan = true;
28
+ const isPanInverted = [false, true];
29
+ const panSpeed = 1;
30
+ const isRotate = true;
31
+ const rotateSpeed = 1;
32
+ const defaultMouseDownMoveAction = "pan";
33
+ const mouseDownMoveModKey = "alt";
34
+ const isZoom = true;
35
+ const zoomSpeed = 1;
36
+ // Derived settings
37
+ const isPanX = Array.isArray(isPan) ? Boolean(isPan[0]) : Boolean(isPan);
38
+ const isPanY = Array.isArray(isPan) ? Boolean(isPan[1]) : Boolean(isPan);
39
+ const isPanXInverted = Array.isArray(isPanInverted) ? Boolean(isPanInverted[0]) : Boolean(isPanInverted);
40
+ const isPanYInverted = Array.isArray(isPanInverted) ? Boolean(isPanInverted[1]) : Boolean(isPanInverted);
41
+ const isZoomX = Array.isArray(isZoom) ? Boolean(isZoom[0]) : Boolean(isZoom);
42
+ const isZoomY = Array.isArray(isZoom) ? Boolean(isZoom[1]) : Boolean(isZoom);
43
+ const panOnMouseDownMove = defaultMouseDownMoveAction === "pan";
44
+ const KEY_MAP = {
45
+ alt: "altKey",
46
+ cmd: "metaKey",
47
+ ctrl: "ctrlKey",
48
+ meta: "metaKey",
49
+ shift: "shiftKey",
50
+ };
51
+ // --- Aspect ratio helpers ---
52
+ function computeAspectRatioFactors(vp) {
53
+ const aspectRatio = vp.width / vp.height;
54
+ let xFactor = 1.0;
55
+ let yFactor = 1.0;
56
+ if (vp.aspectRatioMode === "Contain") {
57
+ if (aspectRatio > 1.0)
58
+ xFactor = 1.0 / aspectRatio;
59
+ else if (aspectRatio < 1.0)
60
+ yFactor = aspectRatio;
61
+ }
62
+ else if (vp.aspectRatioMode === "Cover") {
63
+ if (aspectRatio > 1.0)
64
+ yFactor = aspectRatio;
65
+ else if (aspectRatio < 1.0)
66
+ xFactor = 1.0 / aspectRatio;
67
+ }
68
+ let xAlignTranslation = 0.0;
69
+ let yAlignTranslation = 0.0;
70
+ if (vp.aspectRatioAlignmentMode === "Start") {
71
+ xAlignTranslation = xFactor - 1.0;
72
+ yAlignTranslation = yFactor - 1.0;
73
+ }
74
+ else if (vp.aspectRatioAlignmentMode === "End") {
75
+ xAlignTranslation = 1.0 - xFactor;
76
+ yAlignTranslation = 1.0 - yFactor;
77
+ }
78
+ return { xFactor, yFactor, xAlignTranslation, yAlignTranslation };
79
+ }
80
+ // --- Event handlers ---
81
+ export function onWheel(viewportParams, prevCameraMatrix, event) {
82
+ event.preventDefault();
83
+ if ((!isZoomX && !isZoomY) || isFixed)
84
+ return prevCameraMatrix;
85
+ const { width: plotWidth, height: plotHeight, margins } = viewportParams;
86
+ const width = plotWidth - ((margins?.marginLeft ?? 0) + (margins?.marginRight ?? 0));
87
+ const height = plotHeight - ((margins?.marginBottom ?? 0) + (margins?.marginTop ?? 0));
88
+ const { xFactor, yFactor, xAlignTranslation, yAlignTranslation } = computeAspectRatioFactors(viewportParams);
89
+ const deltaModeScale = event.deltaMode === 1 ? 12 : 1;
90
+ const scrollDist = deltaModeScale * (event.deltaY || event.deltaX || 0);
91
+ if (!scrollDist)
92
+ return prevCameraMatrix;
93
+ const dZ = zoomSpeed * Math.exp(scrollDist / height);
94
+ const px = isNdc
95
+ ? ((-1 + (event.offsetX / width) * 2) - xAlignTranslation) * (1.0 / xFactor)
96
+ : event.offsetX;
97
+ const py = isNdc
98
+ ? ((1 - (event.offsetY / height) * 2) - yAlignTranslation) * (1.0 / yFactor)
99
+ : event.offsetY;
100
+ let dx = isZoomX ? 1 / dZ : 1;
101
+ let dy = isZoomY ? 1 / dZ : 1;
102
+ if (dx <= 0 || dy <= 0 || (dx === 1 && dy === 1))
103
+ return prevCameraMatrix;
104
+ const view = mat4.clone(prevCameraMatrix);
105
+ const s = mat4.fromScaling(mat4.create(), new Float32Array([dx, dy, 1]));
106
+ const p = new Float32Array([px, py, 0]);
107
+ const a = mat4.fromTranslation(mat4.create(), p);
108
+ const aInv = mat4.invert(mat4.create(), a);
109
+ // view = a * s * aInv * prevCameraMatrix (scale about mouse pivot)
110
+ mat4.multiply(view, aInv, view);
111
+ mat4.multiply(view, s, view);
112
+ mat4.multiply(view, a, view);
113
+ return view;
114
+ }
115
+ export function onMouseMove(viewportParams, prevCameraMatrix, event) {
116
+ const { width: plotWidth, height: plotHeight, margins } = viewportParams;
117
+ const width = plotWidth - ((margins?.marginLeft ?? 0) + (margins?.marginRight ?? 0));
118
+ const height = plotHeight - ((margins?.marginBottom ?? 0) + (margins?.marginTop ?? 0));
119
+ const { xFactor, yFactor } = computeAspectRatioFactors(viewportParams);
120
+ const isLeftMousePressed = (event.buttons & 1) !== 0;
121
+ const isMouseDownMoveModActive = Boolean(event[KEY_MAP[mouseDownMoveModKey]]);
122
+ const view = mat4.clone(prevCameraMatrix);
123
+ let changed = false;
124
+ // Pan
125
+ if ((isPanX || isPanY) &&
126
+ isLeftMousePressed &&
127
+ ((panOnMouseDownMove && !isMouseDownMoveModActive) ||
128
+ (!panOnMouseDownMove && isMouseDownMoveModActive))) {
129
+ const dX = isPanXInverted ? -event.movementX : event.movementX;
130
+ const dY = isPanYInverted ? -event.movementY : event.movementY;
131
+ const tx = isPanX ? (isNdc ? ((panSpeed * dX) / width) * 2 * (1.0 / xFactor) : panSpeed * dX) : 0;
132
+ const ty = isPanY ? (isNdc ? ((panSpeed * dY) / height) * 2 * (1.0 / yFactor) : -(panSpeed * dY)) : 0;
133
+ if (tx !== 0 || ty !== 0) {
134
+ const t = mat4.fromTranslation(mat4.create(), new Float32Array([tx, ty, 0]));
135
+ mat4.multiply(view, t, view);
136
+ changed = true;
137
+ }
138
+ }
139
+ // Rotate
140
+ if (isRotate &&
141
+ isLeftMousePressed &&
142
+ ((panOnMouseDownMove && isMouseDownMoveModActive) ||
143
+ (!panOnMouseDownMove && !isMouseDownMoveModActive)) &&
144
+ (Math.abs(event.movementX) + Math.abs(event.movementY)) > 0) {
145
+ const wh = width / 2;
146
+ const hh = height / 2;
147
+ const x1 = (event.offsetX - event.movementX) - wh;
148
+ const y1 = hh - (event.offsetY - event.movementY);
149
+ const x2 = event.offsetX - wh;
150
+ const y2 = hh - event.offsetY;
151
+ if (x1 * x1 + y1 * y1 > 0 && x2 * x2 + y2 * y2 > 0) {
152
+ const radians = vec2.angle([x1, y1], [x2, y2]);
153
+ const cross = x1 * y2 - x2 * y1;
154
+ const rad = rotateSpeed * radians * Math.sign(cross);
155
+ if (rad !== 0) {
156
+ const r = mat4.fromRotation(mat4.create(), rad, new Float32Array([0, 0, 1]));
157
+ mat4.multiply(view, r, view);
158
+ changed = true;
159
+ }
160
+ }
161
+ }
162
+ return changed ? view : prevCameraMatrix;
163
+ }
164
+ // These handlers don't modify the camera matrix in the functional approach.
165
+ // Button state is read from event.buttons in onMouseMove; modifier key state
166
+ // is read from event.altKey/etc. in onMouseMove.
167
+ export function onMouseDown(_viewportParams, prevCameraMatrix, _event) {
168
+ return prevCameraMatrix;
169
+ }
170
+ export function onMouseUp(_viewportParams, prevCameraMatrix, _event) {
171
+ return prevCameraMatrix;
172
+ }
173
+ export function onKeyDown(_viewportParams, prevCameraMatrix, _event) {
174
+ return prevCameraMatrix;
175
+ }
176
+ export function onKeyUp(_viewportParams, prevCameraMatrix, _event) {
177
+ return prevCameraMatrix;
178
+ }
@@ -1,6 +1,6 @@
1
1
  export { initialize, getIsWasmReady, render_wasm, pick_wasm, setStore, setStoreByName, getStore, } from './core.js';
2
- export { default as create2dCamera } from "./dom-2d-camera.js";
3
- export { default as create3dCamera } from "./3d-view-controls.js";
4
2
  export { checkWebGpuFeatureDetection } from './feature-detection.js';
5
3
  export { getBounds, getCameraMatrixFromBounds } from './viewport.js';
4
+ export { onMouseMove as onMouseMove2d, onWheel as onWheel2d } from './functional-dom-2d-camera.js';
5
+ export { onMouseMove as onMouseMove3d, onWheel as onWheel3d } from './functional-3d-view-controls.js';
6
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,cAAc,EACd,WAAW,EACX,SAAS,EACT,QAAQ,EACR,cAAc,EACd,QAAQ,GACT,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,EAAE,2BAA2B,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,EAAE,SAAS,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,cAAc,EACd,WAAW,EACX,SAAS,EACT,QAAQ,EACR,cAAc,EACd,QAAQ,GACT,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,2BAA2B,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,EAAE,SAAS,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,EAAE,WAAW,IAAI,aAAa,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,+BAA+B,CAAC;AACnG,OAAO,EAAE,WAAW,IAAI,aAAa,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,kCAAkC,CAAC"}
package/dist-tsc/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export { initialize, getIsWasmReady, render_wasm, pick_wasm, setStore, setStoreByName, getStore, } from './core.js';
2
- export { default as create2dCamera } from "./dom-2d-camera.js";
3
- export { default as create3dCamera } from "./3d-view-controls.js";
4
2
  export { checkWebGpuFeatureDetection } from './feature-detection.js';
5
3
  export { getBounds, getCameraMatrixFromBounds } from './viewport.js';
4
+ export { onMouseMove as onMouseMove2d, onWheel as onWheel2d } from './functional-dom-2d-camera.js';
5
+ export { onMouseMove as onMouseMove3d, onWheel as onWheel3d } from './functional-3d-view-controls.js';
@@ -1 +1 @@
1
- {"version":3,"file":"lru-store.d.ts","sourceRoot":"","sources":["../src/lru-store.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAc,aAAa,EAAgB,MAAM,SAAS,CAAC;AAavE,wBAAgB,cAAc,CAAC,CAAC,SAAS,aAAa,EAAE,KAAK,EAAE,CAAC,IAEhD,GAAG,MAAM,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,KAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAmBhG;AAID,qBAAa,QAAQ,CAAC,CAAC,SAAS,aAAa,CAAE,YAAW,aAAa;;gBASzD,KAAK,EAAE,CAAC,EAAE,OAAO,SAAM;IAW7B,GAAG,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IA8BzE,OAAO,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,GAAG,SAAS;IAQlF,QAAQ,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAmChG,YAAY,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,GAAG,SAAS;IAO/G,UAAU;CAUX;AAED,wBAAgB,GAAG,CAAC,WAAW,EAAE,aAAa,EAAE,OAAO,SAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,CAEtF"}
1
+ {"version":3,"file":"lru-store.d.ts","sourceRoot":"","sources":["../src/lru-store.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAc,aAAa,EAAgB,MAAM,SAAS,CAAC;AAavE,wBAAgB,cAAc,CAAC,CAAC,SAAS,aAAa,EAAE,KAAK,EAAE,CAAC,IAEhD,GAAG,MAAM,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,KAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAmBhG;AAID,qBAAa,QAAQ,CAAC,CAAC,SAAS,aAAa,CAAE,YAAW,aAAa;;gBAYzD,KAAK,EAAE,CAAC,EAAE,OAAO,SAAM;IAW7B,GAAG,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IA8BzE,OAAO,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,GAAG,SAAS;IAQlF,QAAQ,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAmChG,YAAY,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,GAAG,SAAS;IAO/G,UAAU;CAUX;AAED,wBAAgB,GAAG,CAAC,WAAW,EAAE,aAAa,EAAE,OAAO,SAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,CAEtF"}
@@ -40,6 +40,9 @@ export class LruStore {
40
40
  #cache;
41
41
  // We need a way to synchronously peek at the promise state (a-la Bun's peek or Effect's Deferred.poll).
42
42
  // We can probably do something more sophisticated but will try this first.
43
+ // TODO: should this map be stored on the Rust side instead, so that the peeking can be performed without
44
+ // the JS function call? Instead, JS would "push" the promise states by calling a Rust function upon any
45
+ // promise state change, via a new function exposed from Rust such as `wasm.push_promise_state(key, 'fulfilled')`
43
46
  #promise_states;
44
47
  constructor(store, maxSize = 100) {
45
48
  this.#inner_store = store;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=unrolled-3d-view-controls.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unrolled-3d-view-controls.d.ts","sourceRoot":"","sources":["../src/unrolled-3d-view-controls.js"],"names":[],"mappings":""}