@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,11 @@
1
+ import { ViewportParams } from './viewport.js';
2
+ import { type CameraMatrix } from './functional-dom-2d-camera.js';
3
+ export declare function onWheel3dTurntable(viewportParams: ViewportParams, prevCameraMatrix: CameraMatrix, event: WheelEvent): CameraMatrix;
4
+ export declare function onMouseMove3dTurntable(viewportParams: ViewportParams, prevCameraMatrix: CameraMatrix, event: MouseEvent): CameraMatrix;
5
+ export declare function onWheel3dOrbit(viewportParams: ViewportParams, prevCameraMatrix: CameraMatrix, event: WheelEvent): CameraMatrix;
6
+ export declare function onMouseMove3dOrbit(viewportParams: ViewportParams, prevCameraMatrix: CameraMatrix, event: MouseEvent): CameraMatrix;
7
+ export declare function onWheel3dMatrix(viewportParams: ViewportParams, prevCameraMatrix: CameraMatrix, event: WheelEvent): CameraMatrix;
8
+ export declare function onMouseMove3dMatrix(viewportParams: ViewportParams, prevCameraMatrix: CameraMatrix, event: MouseEvent): CameraMatrix;
9
+ export declare function onWheel(viewportParams: ViewportParams, prevCameraMatrix: CameraMatrix, event: WheelEvent): CameraMatrix;
10
+ export declare function onMouseMove(viewportParams: ViewportParams, prevCameraMatrix: CameraMatrix, event: MouseEvent): CameraMatrix;
11
+ //# sourceMappingURL=functional-3d-view-controls.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"functional-3d-view-controls.d.ts","sourceRoot":"","sources":["../src/functional-3d-view-controls.ts"],"names":[],"mappings":"AA2BA,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,+BAA+B,CAAC;AA4YlE,wBAAgB,kBAAkB,CAAC,cAAc,EAAE,cAAc,EAAE,gBAAgB,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,GAAG,YAAY,CAgBlI;AAED,wBAAgB,sBAAsB,CAAC,cAAc,EAAE,cAAc,EAAE,gBAAgB,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,GAAG,YAAY,CA8BtI;AAID,wBAAgB,cAAc,CAAC,cAAc,EAAE,cAAc,EAAE,gBAAgB,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,GAAG,YAAY,CAgB9H;AAED,wBAAgB,kBAAkB,CAAC,cAAc,EAAE,cAAc,EAAE,gBAAgB,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,GAAG,YAAY,CA8BlI;AAID,wBAAgB,eAAe,CAAC,cAAc,EAAE,cAAc,EAAE,gBAAgB,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,GAAG,YAAY,CAgB/H;AAED,wBAAgB,mBAAmB,CAAC,cAAc,EAAE,cAAc,EAAE,gBAAgB,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,GAAG,YAAY,CA4BnI;AAID,wBAAgB,OAAO,CAAC,cAAc,EAAE,cAAc,EAAE,gBAAgB,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,GAAG,YAAY,CAEvH;AAED,wBAAgB,WAAW,CAAC,cAAc,EAAE,cAAc,EAAE,gBAAgB,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,GAAG,YAAY,CAE3H"}
@@ -0,0 +1,517 @@
1
+ // Functional/stateless adaptation of 3d-view-controls.
2
+ //
3
+ // License copied from https://github.com/mikolalysenko/3d-view-controls/blob/master/LICENSE
4
+ //
5
+ // The MIT License (MIT)
6
+ //
7
+ // Copyright (c) 2013 Mikola Lysenko
8
+ //
9
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ // of this software and associated documentation files (the "Software"), to deal
11
+ // in the Software without restriction, including without limitation the rights
12
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ // copies of the Software, and to permit persons to whom the Software is
14
+ // furnished to do so, subject to the following conditions:
15
+ //
16
+ // The above copyright notice and this permission notice shall be included in
17
+ // all copies or substantial portions of the Software.
18
+ //
19
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25
+ // THE SOFTWARE.
26
+ import { mat4, vec3, quat } from 'gl-matrix';
27
+ // Camera settings matching 3d-view-controls.js defaults
28
+ const ROTATE_SPEED = 1.0;
29
+ const ZOOM_SPEED = 1.0;
30
+ const TRANSLATE_SPEED = 1.0;
31
+ const FLIP_X = false;
32
+ const FLIP_Y = false;
33
+ // Low-level helpers
34
+ function len3(x, y, z) {
35
+ return Math.sqrt(x * x + y * y + z * z);
36
+ }
37
+ // Extract eye position from a column-major view matrix.
38
+ // eye = -R^T * t (R is upper-left 3x3, t is translation column)
39
+ function eyeFromMat(m) {
40
+ const tx = m[12], ty = m[13], tz = m[14];
41
+ return vec3.fromValues(-(m[0] * tx + m[1] * ty + m[2] * tz), -(m[4] * tx + m[5] * ty + m[6] * tz), -(m[8] * tx + m[9] * ty + m[10] * tz));
42
+ }
43
+ // Rebuild a lookAt view matrix from eye, center, and up.
44
+ function buildLookAt(eye, center, up) {
45
+ const m = mat4.create();
46
+ mat4.lookAt(m, eye, center, up);
47
+ return m;
48
+ }
49
+ // ============================================================
50
+ // Orbit controller
51
+ // State layout: Float32Array of 20 elements
52
+ // [0-15]: view matrix (column-major)
53
+ // [16-18]: center x, y, z
54
+ // [19]: log(radius)
55
+ // ============================================================
56
+ function orbitStateFrom(cam) {
57
+ const m = new Float32Array(cam.subarray ? cam.subarray(0, 16) : cam.slice(0, 16));
58
+ const cx = (cam.length > 16 && isFinite(cam[16])) ? cam[16] : 0;
59
+ const cy = (cam.length > 17 && isFinite(cam[17])) ? cam[17] : 0;
60
+ const cz = (cam.length > 18 && isFinite(cam[18])) ? cam[18] : 0;
61
+ const center = vec3.fromValues(cx, cy, cz);
62
+ let logRadius;
63
+ if (cam.length > 19 && isFinite(cam[19])) {
64
+ logRadius = cam[19];
65
+ }
66
+ else {
67
+ const eye = eyeFromMat(m);
68
+ const radius = vec3.distance(eye, center);
69
+ logRadius = Math.log(Math.max(1e-4, radius));
70
+ }
71
+ return [m, center, logRadius];
72
+ }
73
+ function packOrbit(m, center, logRadius) {
74
+ const result = new Float32Array(20);
75
+ result.set(m, 0);
76
+ result[16] = center[0];
77
+ result[17] = center[1];
78
+ result[18] = center[2];
79
+ result[19] = logRadius;
80
+ return result;
81
+ }
82
+ function orbitRotate(cam, dx, dy, dz) {
83
+ const [m, center, logRadius] = orbitStateFrom(cam);
84
+ // Camera frame from view matrix rows (column-major: row i = mat[i], mat[4+i], mat[8+i])
85
+ const rx = m[0], ry = m[4], rz = m[8]; // right
86
+ const ux = m[1], uy = m[5], uz = m[9]; // up
87
+ const fx = m[2], fy = m[6], fz = m[10]; // forward (center → eye direction)
88
+ // World-space direction from screen deltas
89
+ const qx = dx * rx + dy * ux;
90
+ const qy = dx * ry + dy * uy;
91
+ const qz = dx * rz + dy * uz;
92
+ // Rotation axis b_axis = -(forward × q)
93
+ let bx = -(fy * qz - fz * qy);
94
+ let by = -(fz * qx - fx * qz);
95
+ let bz = -(fx * qy - fy * qx);
96
+ let bw = Math.sqrt(Math.max(0.0, 1.0 - bx * bx - by * by - bz * bz));
97
+ const bl = Math.sqrt(bx * bx + by * by + bz * bz + bw * bw);
98
+ if (bl > 1e-6) {
99
+ bx /= bl;
100
+ by /= bl;
101
+ bz /= bl;
102
+ bw /= bl;
103
+ }
104
+ else {
105
+ bx = by = bz = 0;
106
+ bw = 1;
107
+ }
108
+ const bq = quat.fromValues(bx, by, bz, bw);
109
+ // Rotate eye around center
110
+ const eye = eyeFromMat(m);
111
+ const relEye = vec3.fromValues(eye[0] - center[0], eye[1] - center[1], eye[2] - center[2]);
112
+ vec3.transformQuat(relEye, relEye, bq);
113
+ const newEye = vec3.fromValues(center[0] + relEye[0], center[1] + relEye[1], center[2] + relEye[2]);
114
+ // Rotate up
115
+ const upVec = vec3.fromValues(ux, uy, uz);
116
+ vec3.transformQuat(upVec, upVec, bq);
117
+ if (dz) {
118
+ const fwd = vec3.fromValues(fx, fy, fz);
119
+ const rollQ = quat.setAxisAngle(quat.create(), fwd, dz);
120
+ vec3.transformQuat(upVec, upVec, rollQ);
121
+ }
122
+ const newMat = buildLookAt(newEye, center, upVec);
123
+ return packOrbit(newMat, center, logRadius);
124
+ }
125
+ function orbitPan(cam, dx, dy, dz) {
126
+ const [m, center, logRadius] = orbitStateFrom(cam);
127
+ // Normalized up
128
+ let ux = m[1], uy = m[5], uz = m[9];
129
+ const ul = len3(ux, uy, uz);
130
+ ux /= ul;
131
+ uy /= ul;
132
+ uz /= ul;
133
+ // Right orthogonalized to up
134
+ let rx = m[0], ry = m[4], rz = m[8];
135
+ const ru = rx * ux + ry * uy + rz * uz;
136
+ rx -= ux * ru;
137
+ ry -= uy * ru;
138
+ rz -= uz * ru;
139
+ const rl = len3(rx, ry, rz);
140
+ rx /= rl;
141
+ ry /= rl;
142
+ rz /= rl;
143
+ const newCenter = vec3.fromValues(center[0] + rx * dx + ux * dy, center[1] + ry * dx + uy * dy, center[2] + rz * dx + uz * dy);
144
+ const radius = Math.exp(logRadius);
145
+ const newLogRadius = Math.log(Math.max(1e-4, radius + dz));
146
+ const newRadius = Math.exp(newLogRadius);
147
+ // Maintain eye direction (forward = row 2 of view matrix)
148
+ const fx = m[2], fy = m[6], fz = m[10];
149
+ const newEye = vec3.fromValues(newCenter[0] + fx * newRadius, newCenter[1] + fy * newRadius, newCenter[2] + fz * newRadius);
150
+ const upVec = vec3.fromValues(ux, uy, uz);
151
+ const newMat = buildLookAt(newEye, newCenter, upVec);
152
+ return packOrbit(newMat, newCenter, newLogRadius);
153
+ }
154
+ // ============================================================
155
+ // Turntable controller
156
+ // State layout: Float32Array of 28 elements
157
+ // [0-15]: view matrix
158
+ // [16-18]: center
159
+ // [19]: log(radius)
160
+ // [20]: theta (azimuth)
161
+ // [21]: phi (elevation)
162
+ // [22-24]: up_base
163
+ // [25-27]: right_base
164
+ // ============================================================
165
+ // Default turntable base frame: Y-up, X-right, -Z-toward
166
+ const TT_UP_DEFAULT = [0, 1, 0];
167
+ const TT_RIGHT_DEFAULT = [1, 0, 0];
168
+ // Derive initial (theta, phi) from a view matrix assuming standard base frame.
169
+ function anglesFromMat(m) {
170
+ // forward row (row 2): mat[2], mat[6], mat[10] = outward direction (center→eye)
171
+ // With right_base=(1,0,0), toward_base=(0,0,-1), up_base=(0,1,0):
172
+ // wx = dot(fw, right_base) = fw.x
173
+ // wy = dot(fw, toward_base) = -fw.z
174
+ // wz = dot(fw, up_base) = fw.y
175
+ // wz = sin(phi) → phi = asin(fw.y)
176
+ // wx = cos(phi)*cos(theta), wy = cos(phi)*sin(theta) → theta = atan2(wy, wx) = atan2(-fw.z, fw.x)
177
+ const fy = m[6];
178
+ const phi = Math.asin(Math.max(-1, Math.min(1, fy)));
179
+ const theta = Math.atan2(-m[10], m[2]);
180
+ return { theta, phi };
181
+ }
182
+ function turntableStateFrom(cam) {
183
+ const m = new Float32Array(cam.subarray ? cam.subarray(0, 16) : cam.slice(0, 16));
184
+ const cx = (cam.length > 16 && isFinite(cam[16])) ? cam[16] : 0;
185
+ const cy = (cam.length > 17 && isFinite(cam[17])) ? cam[17] : 0;
186
+ const cz = (cam.length > 18 && isFinite(cam[18])) ? cam[18] : 0;
187
+ const center = vec3.fromValues(cx, cy, cz);
188
+ let logRadius;
189
+ if (cam.length > 19 && isFinite(cam[19])) {
190
+ logRadius = cam[19];
191
+ }
192
+ else {
193
+ const eye = eyeFromMat(m);
194
+ const radius = vec3.distance(eye, center);
195
+ logRadius = Math.log(Math.max(1e-4, radius));
196
+ }
197
+ let theta, phi;
198
+ if (cam.length > 21 && isFinite(cam[20]) && isFinite(cam[21])) {
199
+ theta = cam[20];
200
+ phi = cam[21];
201
+ }
202
+ else {
203
+ ({ theta, phi } = anglesFromMat(m));
204
+ }
205
+ const upBase = (cam.length > 24 && isFinite(cam[22]))
206
+ ? vec3.fromValues(cam[22], cam[23], cam[24])
207
+ : vec3.fromValues(...TT_UP_DEFAULT);
208
+ const rightBase = (cam.length > 27 && isFinite(cam[25]))
209
+ ? vec3.fromValues(cam[25], cam[26], cam[27])
210
+ : vec3.fromValues(...TT_RIGHT_DEFAULT);
211
+ return { m, center, logRadius, theta, phi, upBase, rightBase };
212
+ }
213
+ function turntableRecalc(center, logRadius, theta, phi, upBase, rightBase) {
214
+ // Gram-Schmidt orthonormalize upBase and rightBase
215
+ const up = vec3.clone(upBase);
216
+ const right = vec3.clone(rightBase);
217
+ const uu = vec3.dot(up, up);
218
+ const ur = vec3.dot(up, right);
219
+ vec3.normalize(up, up);
220
+ // right = right - up * (dot(right, up) / dot(up, up))
221
+ vec3.scaleAndAdd(right, right, up, -ur / uu);
222
+ vec3.normalize(right, right);
223
+ // toward = up × right
224
+ const toward = vec3.create();
225
+ vec3.cross(toward, up, right);
226
+ vec3.normalize(toward, toward);
227
+ const radius = Math.exp(logRadius);
228
+ const ctheta = Math.cos(theta), stheta = Math.sin(theta);
229
+ const cphi = Math.cos(phi), sphi = Math.sin(phi);
230
+ // Outward direction (center → eye) in (right, toward, up) frame
231
+ const wx = ctheta * cphi, wy = stheta * cphi, wz = sphi;
232
+ // Screen-up direction in same frame
233
+ const sx = -ctheta * sphi, sy = -stheta * sphi, sz = cphi;
234
+ // Fill rows 1 (screen-up) and 2 (forward/outward) of the view matrix.
235
+ // Column-major: mat[4*col + row].
236
+ const mat = new Float32Array(16);
237
+ for (let i = 0; i < 3; ++i) {
238
+ mat[4 * i + 1] = sx * right[i] + sy * toward[i] + sz * up[i]; // row 1
239
+ mat[4 * i + 2] = wx * right[i] + wy * toward[i] + wz * up[i]; // row 2
240
+ mat[4 * i + 3] = 0.0;
241
+ }
242
+ // Row 0 (right) = cross(row1, row2) normalized
243
+ const a0 = mat[1], a1 = mat[5], a2 = mat[9]; // row 1
244
+ const b0 = mat[2], b1 = mat[6], b2 = mat[10]; // row 2
245
+ let c0 = a1 * b2 - a2 * b1;
246
+ let c1 = a2 * b0 - a0 * b2;
247
+ let c2 = a0 * b1 - a1 * b0;
248
+ const cl = len3(c0, c1, c2);
249
+ c0 /= cl;
250
+ c1 /= cl;
251
+ c2 /= cl;
252
+ mat[0] = c0;
253
+ mat[4] = c1;
254
+ mat[8] = c2;
255
+ // Eye = center + forward * radius (forward = row 2 = mat[2], mat[6], mat[10])
256
+ const eyeArr = [
257
+ center[0] + mat[2] * radius,
258
+ center[1] + mat[6] * radius,
259
+ center[2] + mat[10] * radius,
260
+ ];
261
+ // Translation column: mat[12+i] = -dot(row_i, eye)
262
+ for (let i = 0; i < 3; ++i) {
263
+ let r = 0;
264
+ for (let j = 0; j < 3; ++j)
265
+ r += mat[i + 4 * j] * eyeArr[j];
266
+ mat[12 + i] = -r;
267
+ }
268
+ mat[15] = 1.0;
269
+ return { m: mat, upBase: up, rightBase: right };
270
+ }
271
+ function packTurntable(m, center, logRadius, theta, phi, upBase, rightBase) {
272
+ const result = new Float32Array(28);
273
+ result.set(m, 0);
274
+ result[16] = center[0];
275
+ result[17] = center[1];
276
+ result[18] = center[2];
277
+ result[19] = logRadius;
278
+ result[20] = theta;
279
+ result[21] = phi;
280
+ result[22] = upBase[0];
281
+ result[23] = upBase[1];
282
+ result[24] = upBase[2];
283
+ result[25] = rightBase[0];
284
+ result[26] = rightBase[1];
285
+ result[27] = rightBase[2];
286
+ return result;
287
+ }
288
+ function turntableRotate(cam, dtheta, dphi, droll) {
289
+ const { center, logRadius, theta: t0, phi: p0, upBase, rightBase } = turntableStateFrom(cam);
290
+ const theta = t0 + dtheta;
291
+ const phi = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, p0 + dphi));
292
+ let { m, upBase: newUp, rightBase: newRight } = turntableRecalc(center, logRadius, theta, phi, upBase, rightBase);
293
+ if (droll) {
294
+ const fwd = vec3.fromValues(m[2], m[6], m[10]);
295
+ const rollQ = quat.setAxisAngle(quat.create(), fwd, droll);
296
+ const rollUp = vec3.clone(newUp);
297
+ const rollRight = vec3.clone(newRight);
298
+ vec3.transformQuat(rollUp, rollUp, rollQ);
299
+ vec3.transformQuat(rollRight, rollRight, rollQ);
300
+ const recalc = turntableRecalc(center, logRadius, theta, phi, rollUp, rollRight);
301
+ m = recalc.m;
302
+ newUp = recalc.upBase;
303
+ newRight = recalc.rightBase;
304
+ }
305
+ return packTurntable(m, center, logRadius, theta, phi, newUp, newRight);
306
+ }
307
+ function turntablePan(cam, dx, dy, dz) {
308
+ const { m, center, logRadius, theta, phi, upBase, rightBase } = turntableStateFrom(cam);
309
+ let ux = m[1], uy = m[5], uz = m[9];
310
+ const ul = len3(ux, uy, uz);
311
+ ux /= ul;
312
+ uy /= ul;
313
+ uz /= ul;
314
+ let rx = m[0], ry = m[4], rz = m[8];
315
+ const ru = rx * ux + ry * uy + rz * uz;
316
+ rx -= ux * ru;
317
+ ry -= uy * ru;
318
+ rz -= uz * ru;
319
+ const rl = len3(rx, ry, rz);
320
+ rx /= rl;
321
+ ry /= rl;
322
+ rz /= rl;
323
+ const newCenter = vec3.fromValues(center[0] + rx * dx + ux * dy, center[1] + ry * dx + uy * dy, center[2] + rz * dx + uz * dy);
324
+ const radius = Math.exp(logRadius);
325
+ const newLogRadius = Math.log(Math.max(1e-4, radius + dz));
326
+ const { m: newMat, upBase: newUp, rightBase: newRight } = turntableRecalc(newCenter, newLogRadius, theta, phi, upBase, rightBase);
327
+ return packTurntable(newMat, newCenter, newLogRadius, theta, phi, newUp, newRight);
328
+ }
329
+ // Matrix controller
330
+ // State: just the 16-element view matrix
331
+ function matrixRotate(cam, yaw, pitch, roll) {
332
+ const imat = mat4.create();
333
+ mat4.invert(imat, cam.subarray ? cam.subarray(0, 16) : new Float32Array(cam.slice(0, 16)));
334
+ if (yaw)
335
+ mat4.rotateY(imat, imat, yaw);
336
+ if (pitch)
337
+ mat4.rotateX(imat, imat, pitch);
338
+ if (roll)
339
+ mat4.rotateZ(imat, imat, roll);
340
+ const result = mat4.create();
341
+ mat4.invert(result, imat);
342
+ return result;
343
+ }
344
+ function matrixPan(cam, dx, dy, dz) {
345
+ const imat = mat4.create();
346
+ mat4.invert(imat, cam.subarray ? cam.subarray(0, 16) : new Float32Array(cam.slice(0, 16)));
347
+ mat4.translate(imat, imat, [-dx, -dy, -dz]);
348
+ const result = mat4.create();
349
+ mat4.invert(result, imat);
350
+ return result;
351
+ }
352
+ // Event handler helpers
353
+ function getWheelDeltas(event, viewportParams) {
354
+ let dx = event.deltaX || 0;
355
+ let dy = event.deltaY || 0;
356
+ let wheelScale = 1;
357
+ if (event.deltaMode === 1)
358
+ wheelScale = 20;
359
+ else if (event.deltaMode === 2)
360
+ wheelScale = viewportParams.height;
361
+ return { dx: dx * wheelScale, dy: dy * wheelScale };
362
+ }
363
+ // Turntable event handlers
364
+ export function onWheel3dTurntable(viewportParams, prevCameraMatrix, event) {
365
+ event.preventDefault();
366
+ const { dx, dy } = getWheelDeltas(event, viewportParams);
367
+ if (!dx && !dy)
368
+ return prevCameraMatrix;
369
+ const flipX = FLIP_X ? 1 : -1;
370
+ const flipY = FLIP_Y ? 1 : -1;
371
+ const { logRadius } = turntableStateFrom(prevCameraMatrix);
372
+ const distance = Math.exp(logRadius);
373
+ if (Math.abs(dx) > Math.abs(dy)) {
374
+ return turntableRotate(prevCameraMatrix, 0, 0, -dx * flipX * Math.PI * ROTATE_SPEED / viewportParams.width);
375
+ }
376
+ else {
377
+ const kzoom = ZOOM_SPEED * dy / viewportParams.height * 0.16;
378
+ return turntablePan(prevCameraMatrix, 0, 0, distance * (Math.exp(kzoom) - 1));
379
+ }
380
+ }
381
+ export function onMouseMove3dTurntable(viewportParams, prevCameraMatrix, event) {
382
+ const { height } = viewportParams;
383
+ const scale = 1.0 / height;
384
+ const dx = scale * event.movementX;
385
+ const dy = scale * event.movementY;
386
+ if (!dx && !dy)
387
+ return prevCameraMatrix;
388
+ const flipX = FLIP_X ? 1 : -1;
389
+ const flipY = FLIP_Y ? 1 : -1;
390
+ const drot = Math.PI * ROTATE_SPEED;
391
+ const buttons = event.buttons;
392
+ if (buttons & 1) {
393
+ if (event.shiftKey) {
394
+ return turntableRotate(prevCameraMatrix, 0, 0, -dx * drot);
395
+ }
396
+ else {
397
+ return turntableRotate(prevCameraMatrix, -flipX * drot * dx, flipY * drot * dy, 0);
398
+ }
399
+ }
400
+ else if (buttons & 2) {
401
+ const { logRadius } = turntableStateFrom(prevCameraMatrix);
402
+ const distance = Math.exp(logRadius);
403
+ return turntablePan(prevCameraMatrix, TRANSLATE_SPEED * dx * distance, -TRANSLATE_SPEED * dy * distance, 0);
404
+ }
405
+ else if (buttons & 4) {
406
+ const { logRadius } = turntableStateFrom(prevCameraMatrix);
407
+ const distance = Math.exp(logRadius);
408
+ const kzoom = ZOOM_SPEED * dy / height * 0.32;
409
+ return turntablePan(prevCameraMatrix, 0, 0, distance * (Math.exp(kzoom) - 1));
410
+ }
411
+ return prevCameraMatrix;
412
+ }
413
+ // Orbit event handlers
414
+ export function onWheel3dOrbit(viewportParams, prevCameraMatrix, event) {
415
+ event.preventDefault();
416
+ const { dx, dy } = getWheelDeltas(event, viewportParams);
417
+ if (!dx && !dy)
418
+ return prevCameraMatrix;
419
+ const flipX = FLIP_X ? 1 : -1;
420
+ const flipY = FLIP_Y ? 1 : -1;
421
+ const [, , logRadius] = orbitStateFrom(prevCameraMatrix);
422
+ const distance = Math.exp(logRadius);
423
+ if (Math.abs(dx) > Math.abs(dy)) {
424
+ return orbitRotate(prevCameraMatrix, 0, 0, -dx * flipX * Math.PI * ROTATE_SPEED / viewportParams.width);
425
+ }
426
+ else {
427
+ const kzoom = ZOOM_SPEED * dy / viewportParams.height * 0.16;
428
+ return orbitPan(prevCameraMatrix, 0, 0, distance * (Math.exp(kzoom) - 1));
429
+ }
430
+ }
431
+ export function onMouseMove3dOrbit(viewportParams, prevCameraMatrix, event) {
432
+ const { height } = viewportParams;
433
+ const scale = 1.0 / height;
434
+ const dx = scale * event.movementX;
435
+ const dy = scale * event.movementY;
436
+ if (!dx && !dy)
437
+ return prevCameraMatrix;
438
+ const flipX = FLIP_X ? 1 : -1;
439
+ const flipY = FLIP_Y ? 1 : -1;
440
+ const drot = Math.PI * ROTATE_SPEED;
441
+ const buttons = event.buttons;
442
+ if (buttons & 1) {
443
+ if (event.shiftKey) {
444
+ return orbitRotate(prevCameraMatrix, 0, 0, -dx * drot);
445
+ }
446
+ else {
447
+ return orbitRotate(prevCameraMatrix, -flipX * drot * dx, flipY * drot * dy, 0);
448
+ }
449
+ }
450
+ else if (buttons & 2) {
451
+ const [, , logRadius] = orbitStateFrom(prevCameraMatrix);
452
+ const distance = Math.exp(logRadius);
453
+ return orbitPan(prevCameraMatrix, TRANSLATE_SPEED * dx * distance, -TRANSLATE_SPEED * dy * distance, 0);
454
+ }
455
+ else if (buttons & 4) {
456
+ const [, , logRadius] = orbitStateFrom(prevCameraMatrix);
457
+ const distance = Math.exp(logRadius);
458
+ const kzoom = ZOOM_SPEED * dy / height * 0.32;
459
+ return orbitPan(prevCameraMatrix, 0, 0, distance * (Math.exp(kzoom) - 1));
460
+ }
461
+ return prevCameraMatrix;
462
+ }
463
+ // Matrix event handlers
464
+ export function onWheel3dMatrix(viewportParams, prevCameraMatrix, event) {
465
+ event.preventDefault();
466
+ const { dx, dy } = getWheelDeltas(event, viewportParams);
467
+ if (!dx && !dy)
468
+ return prevCameraMatrix;
469
+ const flipX = FLIP_X ? 1 : -1;
470
+ const flipY = FLIP_Y ? 1 : -1;
471
+ const eye = eyeFromMat(prevCameraMatrix);
472
+ const distance = vec3.length(eye);
473
+ if (Math.abs(dx) > Math.abs(dy)) {
474
+ return matrixRotate(prevCameraMatrix, 0, 0, -dx * flipX * Math.PI * ROTATE_SPEED / viewportParams.width);
475
+ }
476
+ else {
477
+ const kzoom = ZOOM_SPEED * dy / viewportParams.height * 0.16;
478
+ return matrixPan(prevCameraMatrix, 0, 0, distance * (Math.exp(kzoom) - 1));
479
+ }
480
+ }
481
+ export function onMouseMove3dMatrix(viewportParams, prevCameraMatrix, event) {
482
+ const { height } = viewportParams;
483
+ const scale = 1.0 / height;
484
+ const dx = scale * event.movementX;
485
+ const dy = scale * event.movementY;
486
+ if (!dx && !dy)
487
+ return prevCameraMatrix;
488
+ const flipX = FLIP_X ? 1 : -1;
489
+ const flipY = FLIP_Y ? 1 : -1;
490
+ const drot = Math.PI * ROTATE_SPEED;
491
+ const buttons = event.buttons;
492
+ const eye = eyeFromMat(prevCameraMatrix);
493
+ const distance = vec3.length(eye);
494
+ if (buttons & 1) {
495
+ if (event.shiftKey) {
496
+ return matrixRotate(prevCameraMatrix, 0, 0, -dx * drot);
497
+ }
498
+ else {
499
+ return matrixRotate(prevCameraMatrix, -flipX * drot * dx, flipY * drot * dy, 0);
500
+ }
501
+ }
502
+ else if (buttons & 2) {
503
+ return matrixPan(prevCameraMatrix, TRANSLATE_SPEED * dx * distance, -TRANSLATE_SPEED * dy * distance, 0);
504
+ }
505
+ else if (buttons & 4) {
506
+ const kzoom = ZOOM_SPEED * dy / height * 0.32;
507
+ return matrixPan(prevCameraMatrix, 0, 0, distance * (Math.exp(kzoom) - 1));
508
+ }
509
+ return prevCameraMatrix;
510
+ }
511
+ // Default 3D handlers (orbit controller)
512
+ export function onWheel(viewportParams, prevCameraMatrix, event) {
513
+ return onWheel3dOrbit(viewportParams, prevCameraMatrix, event);
514
+ }
515
+ export function onMouseMove(viewportParams, prevCameraMatrix, event) {
516
+ return onMouseMove3dOrbit(viewportParams, prevCameraMatrix, event);
517
+ }
@@ -0,0 +1,9 @@
1
+ import { ViewportParams } from './viewport.js';
2
+ export type CameraMatrix = Float32Array;
3
+ export declare function onWheel(viewportParams: ViewportParams, prevCameraMatrix: CameraMatrix, event: WheelEvent): CameraMatrix;
4
+ export declare function onMouseMove(viewportParams: ViewportParams, prevCameraMatrix: CameraMatrix, event: MouseEvent): CameraMatrix;
5
+ export declare function onMouseDown(_viewportParams: ViewportParams, prevCameraMatrix: CameraMatrix, _event: MouseEvent): CameraMatrix;
6
+ export declare function onMouseUp(_viewportParams: ViewportParams, prevCameraMatrix: CameraMatrix, _event: MouseEvent): CameraMatrix;
7
+ export declare function onKeyDown(_viewportParams: ViewportParams, prevCameraMatrix: CameraMatrix, _event: KeyboardEvent): CameraMatrix;
8
+ export declare function onKeyUp(_viewportParams: ViewportParams, prevCameraMatrix: CameraMatrix, _event: KeyboardEvent): CameraMatrix;
9
+ //# sourceMappingURL=functional-dom-2d-camera.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"functional-dom-2d-camera.d.ts","sourceRoot":"","sources":["../src/functional-dom-2d-camera.ts"],"names":[],"mappings":"AAyBA,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAG/C,MAAM,MAAM,YAAY,GAAG,YAAY,CAAC;AAiExC,wBAAgB,OAAO,CAAC,cAAc,EAAE,cAAc,EAAE,gBAAgB,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,GAAG,YAAY,CAuCvH;AAED,wBAAgB,WAAW,CAAC,cAAc,EAAE,cAAc,EAAE,gBAAgB,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,GAAG,YAAY,CA2D3H;AAMD,wBAAgB,WAAW,CAAC,eAAe,EAAE,cAAc,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,GAAG,YAAY,CAE7H;AAED,wBAAgB,SAAS,CAAC,eAAe,EAAE,cAAc,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,GAAG,YAAY,CAE3H;AAED,wBAAgB,SAAS,CAAC,eAAe,EAAE,cAAc,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,GAAG,YAAY,CAE9H;AAED,wBAAgB,OAAO,CAAC,eAAe,EAAE,cAAc,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,GAAG,YAAY,CAE5H"}