three-cad-viewer 4.3.5 → 4.3.7
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/dist/three-cad-viewer.esm.js +8 -5
- package/dist/three-cad-viewer.esm.js.map +1 -1
- package/dist/three-cad-viewer.esm.min.js +1 -1
- package/dist/three-cad-viewer.js +8 -5
- package/dist/three-cad-viewer.min.js +1 -1
- package/package.json +2 -3
- package/src/_version.ts +0 -1
- package/src/camera/camera.ts +0 -445
- package/src/camera/controls/CADOrbitControls.ts +0 -241
- package/src/camera/controls/CADTrackballControls.ts +0 -598
- package/src/camera/controls.ts +0 -380
- package/src/core/patches.ts +0 -16
- package/src/core/studio-manager.ts +0 -652
- package/src/core/types.ts +0 -892
- package/src/core/viewer-state.ts +0 -784
- package/src/core/viewer.ts +0 -4821
- package/src/index.ts +0 -151
- package/src/rendering/environment.ts +0 -840
- package/src/rendering/light-detection.ts +0 -327
- package/src/rendering/material-factory.ts +0 -735
- package/src/rendering/material-presets.ts +0 -289
- package/src/rendering/raycast.ts +0 -291
- package/src/rendering/room-environment.ts +0 -192
- package/src/rendering/studio-composer.ts +0 -577
- package/src/rendering/studio-floor.ts +0 -108
- package/src/rendering/texture-cache.ts +0 -324
- package/src/rendering/tree-model.ts +0 -542
- package/src/rendering/triplanar.ts +0 -329
- package/src/scene/animation.ts +0 -343
- package/src/scene/axes.ts +0 -108
- package/src/scene/bbox.ts +0 -223
- package/src/scene/clipping.ts +0 -650
- package/src/scene/grid.ts +0 -864
- package/src/scene/nestedgroup.ts +0 -1448
- package/src/scene/objectgroup.ts +0 -866
- package/src/scene/orientation.ts +0 -259
- package/src/scene/render-shape.ts +0 -634
- package/src/tools/cad_tools/measure.ts +0 -811
- package/src/tools/cad_tools/select.ts +0 -100
- package/src/tools/cad_tools/tools.ts +0 -231
- package/src/tools/cad_tools/ui.ts +0 -454
- package/src/tools/cad_tools/zebra.ts +0 -369
- package/src/types/html.d.ts +0 -5
- package/src/types/n8ao.d.ts +0 -28
- package/src/types/three-augmentation.d.ts +0 -60
- package/src/ui/display.ts +0 -3295
- package/src/ui/index.html +0 -505
- package/src/ui/info.ts +0 -177
- package/src/ui/slider.ts +0 -206
- package/src/ui/toolbar.ts +0 -347
- package/src/ui/treeview.ts +0 -945
- package/src/utils/decode-instances.ts +0 -233
- package/src/utils/font.ts +0 -60
- package/src/utils/gpu-tracker.ts +0 -265
- package/src/utils/logger.ts +0 -92
- package/src/utils/sizeof.ts +0 -116
- package/src/utils/timer.ts +0 -69
- package/src/utils/utils.ts +0 -446
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "three-cad-viewer",
|
|
3
|
-
"version": "4.3.
|
|
3
|
+
"version": "4.3.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "WebGL-based CAD viewer built on Three.js with clipping planes, measurement tools, and tree navigation",
|
|
6
6
|
"repository": {
|
|
@@ -15,8 +15,7 @@
|
|
|
15
15
|
"dist/three-cad-viewer.esm.js.map",
|
|
16
16
|
"dist/three-cad-viewer.esm.min.js",
|
|
17
17
|
"dist/three-cad-viewer.js",
|
|
18
|
-
"dist/three-cad-viewer.min.js"
|
|
19
|
-
"src"
|
|
18
|
+
"dist/three-cad-viewer.min.js"
|
|
20
19
|
],
|
|
21
20
|
"main": "dist/three-cad-viewer.js",
|
|
22
21
|
"module": "dist/three-cad-viewer.esm.js",
|
package/src/_version.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const version: string = "4.3.5";
|
package/src/camera/camera.ts
DELETED
|
@@ -1,445 +0,0 @@
|
|
|
1
|
-
import * as THREE from "three";
|
|
2
|
-
import type { Vector3Tuple, QuaternionTuple } from "three";
|
|
3
|
-
import type { UpDirection } from "../core/types";
|
|
4
|
-
import { logger } from "../utils/logger.js";
|
|
5
|
-
|
|
6
|
-
type CameraDirection = "iso" | "front" | "rear" | "left" | "right" | "top" | "bottom";
|
|
7
|
-
|
|
8
|
-
interface DirectionConfig {
|
|
9
|
-
pos: THREE.Vector3;
|
|
10
|
-
quat: THREE.Quaternion | null;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
type DirectionMap = Record<CameraDirection, DirectionConfig>;
|
|
14
|
-
|
|
15
|
-
type UpMode = "y_up" | "z_up" | "legacy";
|
|
16
|
-
|
|
17
|
-
const defaultDirections: Record<UpMode, DirectionMap> = {
|
|
18
|
-
y_up: {
|
|
19
|
-
// compatible to fusion 360
|
|
20
|
-
iso: { pos: new THREE.Vector3(1, 1, 1), quat: null },
|
|
21
|
-
front: { pos: new THREE.Vector3(0, 0, 1), quat: null },
|
|
22
|
-
rear: { pos: new THREE.Vector3(0, 0, -1), quat: null },
|
|
23
|
-
left: { pos: new THREE.Vector3(-1, 0, 0), quat: null },
|
|
24
|
-
right: { pos: new THREE.Vector3(1, 0, 0), quat: null },
|
|
25
|
-
top: { pos: new THREE.Vector3(0, 1, 0), quat: null },
|
|
26
|
-
bottom: { pos: new THREE.Vector3(0, -1, 0), quat: null },
|
|
27
|
-
},
|
|
28
|
-
z_up: {
|
|
29
|
-
// compatible to FreeCAD, OnShape
|
|
30
|
-
iso: { pos: new THREE.Vector3(1, -1, 1), quat: null },
|
|
31
|
-
front: { pos: new THREE.Vector3(0, -1, 0), quat: null },
|
|
32
|
-
rear: { pos: new THREE.Vector3(0, 1, 0), quat: null },
|
|
33
|
-
left: { pos: new THREE.Vector3(-1, 0, 0), quat: null },
|
|
34
|
-
right: { pos: new THREE.Vector3(1, 0, 0), quat: null },
|
|
35
|
-
top: { pos: new THREE.Vector3(0, 0, 1), quat: new THREE.Quaternion(0, 0, 0, 1) },
|
|
36
|
-
bottom: { pos: new THREE.Vector3(0, 0, -1), quat: new THREE.Quaternion(1, 0, 0, 0) },
|
|
37
|
-
},
|
|
38
|
-
legacy: {
|
|
39
|
-
// legacy Z up
|
|
40
|
-
iso: { pos: new THREE.Vector3(1, 1, 1), quat: null },
|
|
41
|
-
front: { pos: new THREE.Vector3(1, 0, 0), quat: null },
|
|
42
|
-
rear: { pos: new THREE.Vector3(-1, 0, 0), quat: null },
|
|
43
|
-
left: { pos: new THREE.Vector3(0, 1, 0), quat: null },
|
|
44
|
-
right: { pos: new THREE.Vector3(0, -1, 0), quat: null },
|
|
45
|
-
top: { pos: new THREE.Vector3(0, 0, 1), quat: null },
|
|
46
|
-
bottom: { pos: new THREE.Vector3(0, 0, -1), quat: null },
|
|
47
|
-
},
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const cameraUp: Record<UpMode, [number, number, number]> = {
|
|
51
|
-
y_up: [0, 1, 0],
|
|
52
|
-
z_up: [0, 0, 1],
|
|
53
|
-
legacy: [0, 0, 1],
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Manages orthographic and perspective cameras for the viewer.
|
|
58
|
-
*
|
|
59
|
-
* Camera wraps both camera types and provides:
|
|
60
|
-
* - Seamless switching between orthographic and perspective
|
|
61
|
-
* - Preset positions (iso, front, top, etc.)
|
|
62
|
-
* - Support for Y-up and Z-up coordinate systems
|
|
63
|
-
* - Synchronized position/zoom across camera types
|
|
64
|
-
*
|
|
65
|
-
* ## Coordinate Systems
|
|
66
|
-
* Supports three modes via `up` parameter:
|
|
67
|
-
* - `"Y"`: Y-up (Fusion 360 compatible)
|
|
68
|
-
* - `"Z"`: Z-up (FreeCAD, OnShape compatible)
|
|
69
|
-
* - Legacy Z-up mode
|
|
70
|
-
*
|
|
71
|
-
* @internal - This is an internal class used by Viewer
|
|
72
|
-
*/
|
|
73
|
-
class Camera {
|
|
74
|
-
private static readonly DISTANCE_FACTOR = 5;
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Near plane factor: near = max(0.1, NEAR_FACTOR * distance).
|
|
78
|
-
* With far = 100 * distance, this gives a far/near ratio of 10,000:1,
|
|
79
|
-
* which is comfortable for 24-bit depth buffers and avoids z-fighting
|
|
80
|
-
* on large models (e.g. toycar at ~1100 unit bounding radius).
|
|
81
|
-
*/
|
|
82
|
-
private static readonly NEAR_FACTOR = 0.01;
|
|
83
|
-
|
|
84
|
-
target: THREE.Vector3;
|
|
85
|
-
ortho: boolean;
|
|
86
|
-
up: UpMode;
|
|
87
|
-
yaxis: THREE.Vector3;
|
|
88
|
-
zaxis: THREE.Vector3;
|
|
89
|
-
camera_distance: number;
|
|
90
|
-
pCamera!: THREE.PerspectiveCamera; // Initialized in constructor
|
|
91
|
-
oCamera!: THREE.OrthographicCamera; // Initialized in constructor
|
|
92
|
-
camera!: THREE.PerspectiveCamera | THREE.OrthographicCamera; // Set in constructor
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Compute the near clipping plane from the bounding radius.
|
|
96
|
-
* Keeps the far/near ratio bounded for depth buffer precision.
|
|
97
|
-
*/
|
|
98
|
-
private static _computeNear(distance: number): number {
|
|
99
|
-
return Math.max(0.1, Camera.NEAR_FACTOR * distance);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Create a combined camera (orthographic and perspective).
|
|
104
|
-
* @param width - canvas width.
|
|
105
|
-
* @param height - canvas height.
|
|
106
|
-
* @param distance - distance from the lookAt point.
|
|
107
|
-
* @param target - target (Vector3) to look at.
|
|
108
|
-
* @param ortho - flag whether the initial camera should be orthographic.
|
|
109
|
-
* @param up - Z or Y to define whether Z or Y direction is camera up.
|
|
110
|
-
*/
|
|
111
|
-
constructor(
|
|
112
|
-
width: number,
|
|
113
|
-
height: number,
|
|
114
|
-
distance: number,
|
|
115
|
-
target: Vector3Tuple,
|
|
116
|
-
ortho: boolean,
|
|
117
|
-
up: UpDirection
|
|
118
|
-
) {
|
|
119
|
-
const mapping: Record<string, UpMode> = {
|
|
120
|
-
Y: "y_up",
|
|
121
|
-
Z: "z_up",
|
|
122
|
-
L: "legacy",
|
|
123
|
-
};
|
|
124
|
-
this.target = new THREE.Vector3(...target);
|
|
125
|
-
this.ortho = ortho;
|
|
126
|
-
this.up = mapping[up] || "z_up";
|
|
127
|
-
this.yaxis = new THREE.Vector3(0, 1, 0);
|
|
128
|
-
this.zaxis = new THREE.Vector3(0, 0, 1);
|
|
129
|
-
|
|
130
|
-
// define the perspective camera
|
|
131
|
-
|
|
132
|
-
const aspect = width / height;
|
|
133
|
-
|
|
134
|
-
// 22 is a good compromise
|
|
135
|
-
const fov = 22;
|
|
136
|
-
|
|
137
|
-
this.camera_distance = Camera.DISTANCE_FACTOR * distance;
|
|
138
|
-
|
|
139
|
-
const near = Camera._computeNear(distance);
|
|
140
|
-
const far = 100 * distance;
|
|
141
|
-
|
|
142
|
-
this.pCamera = new THREE.PerspectiveCamera(fov, aspect, near, far);
|
|
143
|
-
this.pCamera.up.set(...cameraUp[this.up]);
|
|
144
|
-
this.pCamera.lookAt(this.target);
|
|
145
|
-
|
|
146
|
-
// define the orthographic camera
|
|
147
|
-
const pSize = this.projectSize(distance, aspect);
|
|
148
|
-
|
|
149
|
-
this.oCamera = new THREE.OrthographicCamera(
|
|
150
|
-
-pSize[0],
|
|
151
|
-
pSize[0],
|
|
152
|
-
pSize[1],
|
|
153
|
-
-pSize[1],
|
|
154
|
-
near,
|
|
155
|
-
far,
|
|
156
|
-
);
|
|
157
|
-
this.oCamera.up.set(...cameraUp[this.up]);
|
|
158
|
-
this.oCamera.lookAt(this.target);
|
|
159
|
-
|
|
160
|
-
this.camera = ortho ? this.oCamera! : this.pCamera!;
|
|
161
|
-
this.camera.up.set(...cameraUp[this.up]);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Update the near/far clipping planes for both cameras.
|
|
166
|
-
* @param distance - The new bounding radius to base the clipping planes on.
|
|
167
|
-
*/
|
|
168
|
-
updateFarPlane(distance: number): void {
|
|
169
|
-
const near = Camera._computeNear(distance);
|
|
170
|
-
const far = 100 * distance;
|
|
171
|
-
this.pCamera.near = near;
|
|
172
|
-
this.pCamera.far = far;
|
|
173
|
-
this.oCamera.near = near;
|
|
174
|
-
this.oCamera.far = far;
|
|
175
|
-
this.camera.updateProjectionMatrix();
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Recalculate camera_distance from a new bounding radius.
|
|
180
|
-
* Uses the same factor as the constructor so that zoom 1.0 frames the scene.
|
|
181
|
-
* @param distance - The new bounding radius (bb_radius).
|
|
182
|
-
*/
|
|
183
|
-
updateCameraDistance(distance: number): void {
|
|
184
|
-
this.camera_distance = Camera.DISTANCE_FACTOR * distance;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Remove assets.
|
|
189
|
-
*/
|
|
190
|
-
dispose(): void {
|
|
191
|
-
// Cameras are simple objects; no explicit cleanup needed.
|
|
192
|
-
// The Camera instance itself will be garbage collected.
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Get the current camera.
|
|
197
|
-
* @returns Camera object.
|
|
198
|
-
*/
|
|
199
|
-
getCamera(): THREE.PerspectiveCamera | THREE.OrthographicCamera {
|
|
200
|
-
return this.camera;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Set the lookAt point for the camera to the provided target.
|
|
205
|
-
*/
|
|
206
|
-
lookAtTarget(): void {
|
|
207
|
-
this.camera.lookAt(this.target);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Update current camera's projection matrix.
|
|
212
|
-
*/
|
|
213
|
-
updateProjectionMatrix(): void {
|
|
214
|
-
this.camera.updateProjectionMatrix();
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Switch between orthographic and perspective camera.
|
|
219
|
-
* @param ortho_flag - true for orthographic camera, else perspective camera.
|
|
220
|
-
*/
|
|
221
|
-
switchCamera(ortho_flag: boolean): void {
|
|
222
|
-
const p0 = this.getPosition().clone();
|
|
223
|
-
const z0 = this.getZoom();
|
|
224
|
-
const q0 = this.getQuaternion().clone();
|
|
225
|
-
|
|
226
|
-
if (ortho_flag) {
|
|
227
|
-
this.camera = this.oCamera;
|
|
228
|
-
this.ortho = true;
|
|
229
|
-
} else {
|
|
230
|
-
this.camera = this.pCamera;
|
|
231
|
-
this.ortho = false;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
this.setPosition(p0, false);
|
|
235
|
-
this.setZoom(z0);
|
|
236
|
-
this.setQuaternion(q0);
|
|
237
|
-
|
|
238
|
-
this.updateProjectionMatrix();
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Calculate projected size for orthographic camera.
|
|
243
|
-
* @param frustum - View frustum size.
|
|
244
|
-
* @param aspect - Viewer aspect ratio (width / height).
|
|
245
|
-
* @returns Width and height [w, h] for the orthographic camera.
|
|
246
|
-
*/
|
|
247
|
-
projectSize(frustum: number, aspect: number): [number, number] {
|
|
248
|
-
let w: number, h: number;
|
|
249
|
-
if (aspect < 1) {
|
|
250
|
-
w = frustum;
|
|
251
|
-
h = w / aspect;
|
|
252
|
-
} else {
|
|
253
|
-
h = frustum;
|
|
254
|
-
w = h * aspect;
|
|
255
|
-
}
|
|
256
|
-
return [w, h];
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Setup the current camera.
|
|
261
|
-
* @param relative - flag whether the position is a relative (e.g. [1,1,1] for iso) or absolute point.
|
|
262
|
-
* @param position - the camera position (relative or absolute).
|
|
263
|
-
* @param quaternion - the camera rotation expressed by a quaternion.
|
|
264
|
-
* @param zoom - zoom value.
|
|
265
|
-
*/
|
|
266
|
-
setupCamera(
|
|
267
|
-
relative: boolean,
|
|
268
|
-
position: THREE.Vector3 | null = null,
|
|
269
|
-
quaternion: THREE.Quaternion | null = null,
|
|
270
|
-
zoom: number | null = null
|
|
271
|
-
): void {
|
|
272
|
-
if (position != null) {
|
|
273
|
-
const cameraPosition = relative
|
|
274
|
-
? position
|
|
275
|
-
.clone()
|
|
276
|
-
.normalize()
|
|
277
|
-
.multiplyScalar(this.camera_distance)
|
|
278
|
-
.add(this.target)
|
|
279
|
-
: position;
|
|
280
|
-
|
|
281
|
-
this.camera.position.set(...cameraPosition.toArray());
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
if (quaternion != null) {
|
|
285
|
-
this.camera.quaternion.set(...quaternion.toArray());
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
if (zoom != null) {
|
|
289
|
-
this.setZoom(zoom);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
this.updateProjectionMatrix();
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* Move the camera to a given preset.
|
|
297
|
-
* @param dir - can be "iso", "top", "bottom", "front", "rear", "left", "right"
|
|
298
|
-
*/
|
|
299
|
-
presetCamera(dir: CameraDirection, zoom: number | null = null): void {
|
|
300
|
-
if (zoom == null) {
|
|
301
|
-
zoom = this.camera.zoom;
|
|
302
|
-
}
|
|
303
|
-
// For the default directions quaternion can be ignored, it will be reset automatically
|
|
304
|
-
this.setupCamera(true, defaultDirections[this.up][dir].pos, null, zoom);
|
|
305
|
-
this.lookAtTarget();
|
|
306
|
-
|
|
307
|
-
const quat = defaultDirections[this.up][dir].quat;
|
|
308
|
-
if (quat != null) {
|
|
309
|
-
this.setQuaternion(quat);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
/**
|
|
314
|
-
* Return current zoom value.
|
|
315
|
-
* @returns zoom value.
|
|
316
|
-
*/
|
|
317
|
-
getZoom(): number {
|
|
318
|
-
if (this.ortho) {
|
|
319
|
-
return this.camera.zoom;
|
|
320
|
-
} else {
|
|
321
|
-
const p = this.camera.position.clone().sub(this.target);
|
|
322
|
-
return this.camera_distance / p.length();
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
* Set zoom value.
|
|
328
|
-
* @param val - float zoom value.
|
|
329
|
-
*/
|
|
330
|
-
setZoom(val: number): void {
|
|
331
|
-
if (this.ortho) {
|
|
332
|
-
this.camera.zoom = val;
|
|
333
|
-
} else {
|
|
334
|
-
this.camera.position
|
|
335
|
-
.sub(this.target)
|
|
336
|
-
.setLength(this.camera_distance / val)
|
|
337
|
-
.add(this.target);
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
this.updateProjectionMatrix();
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
/**
|
|
344
|
-
* Get the current camera position.
|
|
345
|
-
* @returns camera position.
|
|
346
|
-
*/
|
|
347
|
-
getPosition(): THREE.Vector3 {
|
|
348
|
-
return this.camera.position;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
/**
|
|
352
|
-
* Set camera position.
|
|
353
|
-
* @param position - position as 3 dim Array [x,y,z] or as Vector3.
|
|
354
|
-
* @param relative - flag whether the position is a relative (e.g. [1,1,1] for iso) or absolute point.
|
|
355
|
-
*/
|
|
356
|
-
setPosition(position: Vector3Tuple | THREE.Vector3, relative: boolean): void {
|
|
357
|
-
if (Array.isArray(position) && position.length === 3) {
|
|
358
|
-
this.setupCamera(relative, new THREE.Vector3(...position));
|
|
359
|
-
} else if (position instanceof THREE.Vector3) {
|
|
360
|
-
this.setupCamera(relative, position);
|
|
361
|
-
} else {
|
|
362
|
-
logger.error("wrong type for position", position);
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
/**
|
|
367
|
-
* Get the current camera quaternion.
|
|
368
|
-
* @returns camera quaternion.
|
|
369
|
-
*/
|
|
370
|
-
getQuaternion(): THREE.Quaternion {
|
|
371
|
-
return this.camera.quaternion;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
/**
|
|
375
|
-
* Set camera quaternion.
|
|
376
|
-
* @param quaternion - quaternion as 4 dim Array or as Quaternion.
|
|
377
|
-
*/
|
|
378
|
-
setQuaternion(quaternion: QuaternionTuple | THREE.Quaternion): void {
|
|
379
|
-
if (Array.isArray(quaternion) && quaternion.length === 4) {
|
|
380
|
-
this.setupCamera(false, null, new THREE.Quaternion(...quaternion));
|
|
381
|
-
} else if (quaternion instanceof THREE.Quaternion) {
|
|
382
|
-
this.setupCamera(false, null, quaternion);
|
|
383
|
-
} else {
|
|
384
|
-
logger.error("wrong type for quaternion", quaternion);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
this.updateProjectionMatrix();
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
/**
|
|
391
|
-
* Get the current camera rotation.
|
|
392
|
-
* @returns camera rotation.
|
|
393
|
-
*/
|
|
394
|
-
getRotation(): THREE.Euler {
|
|
395
|
-
return this.camera.rotation;
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
/**
|
|
399
|
-
* Get the visible area dimensions at the target plane.
|
|
400
|
-
* @returns The visible width and height.
|
|
401
|
-
*/
|
|
402
|
-
getVisibleArea(): { width: number; height: number } {
|
|
403
|
-
if (this.ortho && this.oCamera) {
|
|
404
|
-
const height = (this.oCamera.top - this.oCamera.bottom) / this.oCamera.zoom;
|
|
405
|
-
const width = (this.oCamera.right - this.oCamera.left) / this.oCamera.zoom;
|
|
406
|
-
return { width, height };
|
|
407
|
-
} else if (this.pCamera) {
|
|
408
|
-
const distance = this.pCamera.position.distanceTo(this.target);
|
|
409
|
-
const vFOV = (this.pCamera.fov * Math.PI) / 180;
|
|
410
|
-
const height = 2 * Math.tan(vFOV / 2) * distance;
|
|
411
|
-
const width = height * this.pCamera.aspect;
|
|
412
|
-
return { width, height };
|
|
413
|
-
}
|
|
414
|
-
return { width: 0, height: 0 };
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
/**
|
|
418
|
-
* Update camera dimensions when viewport size changes.
|
|
419
|
-
* @param distance - Distance used for orthographic frustum calculation.
|
|
420
|
-
* @param width - New viewport width in pixels.
|
|
421
|
-
* @param height - New viewport height in pixels.
|
|
422
|
-
*/
|
|
423
|
-
changeDimensions(distance: number, width: number, height: number): void {
|
|
424
|
-
const aspect = width / height;
|
|
425
|
-
const pSize = this.projectSize(distance, aspect);
|
|
426
|
-
|
|
427
|
-
if (this.oCamera) {
|
|
428
|
-
this.oCamera.left = -pSize[0];
|
|
429
|
-
this.oCamera.right = pSize[0];
|
|
430
|
-
this.oCamera.top = pSize[1];
|
|
431
|
-
this.oCamera.bottom = -pSize[1];
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
if (this.pCamera) {
|
|
435
|
-
this.pCamera.aspect = aspect;
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
if (this.camera) {
|
|
439
|
-
this.camera.updateProjectionMatrix();
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
export { Camera };
|
|
445
|
-
export type { CameraDirection, UpMode };
|
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CADOrbitControls - Extended OrbitControls for CAD applications
|
|
3
|
-
*
|
|
4
|
-
* Adds:
|
|
5
|
-
* - Public rotateLeft/rotateUp methods for programmatic rotation
|
|
6
|
-
* - Quaternion-based saveState/reset
|
|
7
|
-
* - Modifier key rotation restrictions (ctrl: vertical only, meta: horizontal only)
|
|
8
|
-
*
|
|
9
|
-
* Internal OrbitControls methods/properties used (see three-augmentation.d.ts):
|
|
10
|
-
* - _onMouseDown: Replaced to customize modifier key behavior (shift=pan, ctrl/meta=rotate with axis lock)
|
|
11
|
-
* - _handleMouseDownRotate: Called to initialize rotation state
|
|
12
|
-
* - _handleMouseDownDolly: Called to initialize dolly/zoom state
|
|
13
|
-
* - _handleMouseDownPan: Called to initialize pan state
|
|
14
|
-
* - _sphericalDelta: Modified in _rotateLeft/_rotateUp overrides to implement axis locking
|
|
15
|
-
* - state: Set to track current interaction mode (ROTATE/DOLLY/PAN/NONE)
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
|
|
19
|
-
import { MOUSE, Quaternion, Vector3, Camera } from "three";
|
|
20
|
-
import { KeyMapper, isOrthographicCamera, isPerspectiveCamera } from "../../utils/utils.js";
|
|
21
|
-
|
|
22
|
-
// State constants matching OrbitControls internal state
|
|
23
|
-
const STATE = {
|
|
24
|
-
NONE: -1,
|
|
25
|
-
ROTATE: 0,
|
|
26
|
-
DOLLY: 1,
|
|
27
|
-
PAN: 2,
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
class CADOrbitControls extends OrbitControls {
|
|
31
|
-
quaternion0: Quaternion;
|
|
32
|
-
private _horizontalRotate: boolean;
|
|
33
|
-
private _verticalRotate: boolean;
|
|
34
|
-
private _onCADPointerDown?: (event: PointerEvent) => void;
|
|
35
|
-
private _onCADPointerUp?: () => void;
|
|
36
|
-
|
|
37
|
-
// Expose internal properties for type safety
|
|
38
|
-
declare state: number;
|
|
39
|
-
declare _sphericalDelta: { theta: number; phi: number };
|
|
40
|
-
declare zoom0: number;
|
|
41
|
-
declare target0: Vector3;
|
|
42
|
-
declare position0: Vector3;
|
|
43
|
-
declare _onMouseDown: (event: MouseEvent) => void;
|
|
44
|
-
declare _handleMouseDownRotate: (event: MouseEvent) => void;
|
|
45
|
-
declare _handleMouseDownDolly: (event: MouseEvent) => void;
|
|
46
|
-
declare _handleMouseDownPan: (event: MouseEvent) => void;
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Constructs CAD-enhanced orbit controls.
|
|
50
|
-
*
|
|
51
|
-
* @param object - The camera to control.
|
|
52
|
-
* @param domElement - The HTML element for event listeners.
|
|
53
|
-
*/
|
|
54
|
-
constructor(object: Camera, domElement: HTMLElement | null = null) {
|
|
55
|
-
super(object, domElement!);
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Saved quaternion for reset (in addition to position/target/zoom).
|
|
59
|
-
*/
|
|
60
|
-
this.quaternion0 = this.object.quaternion.clone();
|
|
61
|
-
|
|
62
|
-
// Rotation axis restriction flags (set via modifier keys)
|
|
63
|
-
this._horizontalRotate = true; // meta key restricts to horizontal only
|
|
64
|
-
this._verticalRotate = true; // ctrl key restricts to vertical only
|
|
65
|
-
|
|
66
|
-
// Add pointer event listeners for modifier key detection
|
|
67
|
-
if (domElement) {
|
|
68
|
-
this._onCADPointerDown = this._handleCADPointerDown.bind(this);
|
|
69
|
-
this._onCADPointerUp = this._handleCADPointerUp.bind(this);
|
|
70
|
-
domElement.addEventListener("pointerdown", this._onCADPointerDown);
|
|
71
|
-
domElement.addEventListener("pointerup", this._onCADPointerUp);
|
|
72
|
-
domElement.addEventListener("pointercancel", this._onCADPointerUp);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Override the parent's _onMouseDown which was bound in parent constructor
|
|
76
|
-
this._onMouseDown = this._handleMouseDown.bind(this);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Handle pointer down to check modifier keys for rotation restriction.
|
|
81
|
-
*/
|
|
82
|
-
private _handleCADPointerDown(event: PointerEvent): void {
|
|
83
|
-
// Check modifier keys for rotation restriction
|
|
84
|
-
// ctrl: restrict to vertical rotation only (horizontalRotate = false)
|
|
85
|
-
// meta: restrict to horizontal rotation only (verticalRotate = false)
|
|
86
|
-
this._horizontalRotate = !KeyMapper.get(event, "ctrl");
|
|
87
|
-
this._verticalRotate = !KeyMapper.get(event, "meta");
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Handle pointer up to reset rotation restrictions.
|
|
92
|
-
*/
|
|
93
|
-
private _handleCADPointerUp(): void {
|
|
94
|
-
this._horizontalRotate = true;
|
|
95
|
-
this._verticalRotate = true;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Override dispose to clean up our event listeners.
|
|
100
|
-
*/
|
|
101
|
-
dispose(): void {
|
|
102
|
-
if (this.domElement && this._onCADPointerDown && this._onCADPointerUp) {
|
|
103
|
-
this.domElement.removeEventListener("pointerdown", this._onCADPointerDown);
|
|
104
|
-
this.domElement.removeEventListener("pointerup", this._onCADPointerUp);
|
|
105
|
-
this.domElement.removeEventListener("pointercancel", this._onCADPointerUp);
|
|
106
|
-
}
|
|
107
|
-
super.dispose();
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Custom mouse down handler for rotation restriction via modifier keys.
|
|
112
|
-
*
|
|
113
|
-
* Original OrbitControls: ctrl/meta/shift + left mouse = pan
|
|
114
|
-
* CADOrbitControls: ctrl = vertical rotate only, meta = horizontal rotate only,
|
|
115
|
-
* shift = pan (via KeyMapper)
|
|
116
|
-
*/
|
|
117
|
-
private _handleMouseDown(event: MouseEvent): void {
|
|
118
|
-
let mouseAction: number;
|
|
119
|
-
|
|
120
|
-
switch (event.button) {
|
|
121
|
-
case 0:
|
|
122
|
-
mouseAction = this.mouseButtons.LEFT!;
|
|
123
|
-
break;
|
|
124
|
-
case 1:
|
|
125
|
-
mouseAction = this.mouseButtons.MIDDLE!;
|
|
126
|
-
break;
|
|
127
|
-
case 2:
|
|
128
|
-
mouseAction = this.mouseButtons.RIGHT!;
|
|
129
|
-
break;
|
|
130
|
-
default:
|
|
131
|
-
mouseAction = -1;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
switch (mouseAction) {
|
|
135
|
-
case MOUSE.DOLLY:
|
|
136
|
-
if (this.enableZoom === false) return;
|
|
137
|
-
this._handleMouseDownDolly(event);
|
|
138
|
-
this.state = STATE.DOLLY;
|
|
139
|
-
break;
|
|
140
|
-
|
|
141
|
-
case MOUSE.ROTATE:
|
|
142
|
-
// Check if shift key (via KeyMapper) is pressed for pan
|
|
143
|
-
if (KeyMapper.get(event, "shift")) {
|
|
144
|
-
if (this.enablePan === false) return;
|
|
145
|
-
this._handleMouseDownPan(event);
|
|
146
|
-
this.state = STATE.PAN;
|
|
147
|
-
} else {
|
|
148
|
-
// ctrl and meta are handled for rotation restriction in _handleCADPointerDown
|
|
149
|
-
if (this.enableRotate === false) return;
|
|
150
|
-
this._handleMouseDownRotate(event);
|
|
151
|
-
this.state = STATE.ROTATE;
|
|
152
|
-
}
|
|
153
|
-
break;
|
|
154
|
-
|
|
155
|
-
case MOUSE.PAN:
|
|
156
|
-
// For right mouse button, check if any modifier for rotate
|
|
157
|
-
if (
|
|
158
|
-
KeyMapper.get(event, "ctrl") ||
|
|
159
|
-
KeyMapper.get(event, "meta") ||
|
|
160
|
-
KeyMapper.get(event, "shift")
|
|
161
|
-
) {
|
|
162
|
-
if (this.enableRotate === false) return;
|
|
163
|
-
this._handleMouseDownRotate(event);
|
|
164
|
-
this.state = STATE.ROTATE;
|
|
165
|
-
} else {
|
|
166
|
-
if (this.enablePan === false) return;
|
|
167
|
-
this._handleMouseDownPan(event);
|
|
168
|
-
this.state = STATE.PAN;
|
|
169
|
-
}
|
|
170
|
-
break;
|
|
171
|
-
|
|
172
|
-
default:
|
|
173
|
-
this.state = STATE.NONE;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Override _rotateLeft to respect horizontal rotation restriction.
|
|
179
|
-
*/
|
|
180
|
-
_rotateLeft(angle: number): void {
|
|
181
|
-
if (this._horizontalRotate) {
|
|
182
|
-
this._sphericalDelta.theta -= angle;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Override _rotateUp to respect vertical rotation restriction.
|
|
188
|
-
*/
|
|
189
|
-
_rotateUp(angle: number): void {
|
|
190
|
-
if (this._verticalRotate) {
|
|
191
|
-
this._sphericalDelta.phi -= angle;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Save the current state including quaternion.
|
|
197
|
-
*/
|
|
198
|
-
saveState(): void {
|
|
199
|
-
super.saveState();
|
|
200
|
-
this.quaternion0.copy(this.object.quaternion);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Reset to saved state including quaternion.
|
|
205
|
-
*/
|
|
206
|
-
reset(): void {
|
|
207
|
-
this.target.copy(this.target0);
|
|
208
|
-
this.object.position.copy(this.position0);
|
|
209
|
-
this.object.quaternion.copy(this.quaternion0);
|
|
210
|
-
|
|
211
|
-
if (isPerspectiveCamera(this.object) || isOrthographicCamera(this.object)) {
|
|
212
|
-
this.object.zoom = this.zoom0;
|
|
213
|
-
this.object.updateProjectionMatrix();
|
|
214
|
-
}
|
|
215
|
-
this.dispatchEvent({ type: "change" });
|
|
216
|
-
|
|
217
|
-
this.update();
|
|
218
|
-
|
|
219
|
-
this.state = -1; // STATE.NONE
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* Rotate camera left (around the up axis).
|
|
224
|
-
* Programmatic rotation bypasses modifier key restrictions.
|
|
225
|
-
*/
|
|
226
|
-
rotateLeft(angle: number): void {
|
|
227
|
-
// Bypass restriction for programmatic rotation
|
|
228
|
-
this._sphericalDelta.theta -= angle;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* Rotate camera up (around the right axis).
|
|
233
|
-
* Programmatic rotation bypasses modifier key restrictions.
|
|
234
|
-
*/
|
|
235
|
-
rotateUp(angle: number): void {
|
|
236
|
-
// Bypass restriction for programmatic rotation
|
|
237
|
-
this._sphericalDelta.phi -= angle;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
export { CADOrbitControls };
|