@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.
- package/README.md +1 -0
- package/dist/index.js +1109 -4101
- package/dist-tsc/functional-3d-view-controls.d.ts +11 -0
- package/dist-tsc/functional-3d-view-controls.d.ts.map +1 -0
- package/dist-tsc/functional-3d-view-controls.js +517 -0
- package/dist-tsc/functional-dom-2d-camera.d.ts +9 -0
- package/dist-tsc/functional-dom-2d-camera.d.ts.map +1 -0
- package/dist-tsc/functional-dom-2d-camera.js +178 -0
- package/dist-tsc/index.d.ts +2 -2
- package/dist-tsc/index.d.ts.map +1 -1
- package/dist-tsc/index.js +2 -2
- package/dist-tsc/lru-store.d.ts.map +1 -1
- package/dist-tsc/lru-store.js +3 -0
- package/dist-tsc/unrolled-3d-view-controls.d.ts +2 -0
- package/dist-tsc/unrolled-3d-view-controls.d.ts.map +1 -0
- package/dist-tsc/unrolled-3d-view-controls.js +637 -0
- package/dist-tsc/unrolled-dom-2d-camera.d.ts +2 -0
- package/dist-tsc/unrolled-dom-2d-camera.d.ts.map +1 -0
- package/dist-tsc/unrolled-dom-2d-camera.js +193 -0
- package/dist-tsc/viewport.d.ts +91 -0
- package/dist-tsc/viewport.d.ts.map +1 -1
- package/dist-tsc/viewport.js +91 -3
- package/dist-tsc/viewport.test.d.ts +2 -0
- package/dist-tsc/viewport.test.d.ts.map +1 -0
- package/dist-tsc/viewport.test.js +184 -0
- package/package.json +8 -2
- package/src/functional-3d-view-controls.ts +585 -0
- package/src/functional-dom-2d-camera.ts +214 -0
- package/src/index.ts +2 -2
- package/src/lru-store.ts +3 -0
- package/src/viewport.test.ts +262 -0
- package/src/viewport.ts +91 -3
- package/src/3d-view-controls.js +0 -271
- 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"}
|