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
|
@@ -1,598 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CADTrackballControls - Extended TrackballControls for CAD applications
|
|
3
|
-
*
|
|
4
|
-
* Adds:
|
|
5
|
-
* - Holroyd (non-tumbling) trackball rotation mode
|
|
6
|
-
* - rotateX/Y/Z methods for programmatic world-axis rotation
|
|
7
|
-
* - Quaternion-based saveState/reset
|
|
8
|
-
*
|
|
9
|
-
* Internal TrackballControls methods/properties used (see three-augmentation.d.ts):
|
|
10
|
-
* - _onMouseDown: Replaced to customize modifier key behavior (shift=pan)
|
|
11
|
-
* - _getMouseOnCircle: Called to convert page coordinates for rotation
|
|
12
|
-
* - _getMouseOnScreen: Called to convert page coordinates for pan/zoom
|
|
13
|
-
* - _rotateCamera: Overridden to implement holroyd sphere projection
|
|
14
|
-
* - _panCamera: Overridden to use quaternion-based camera orientation in holroyd mode
|
|
15
|
-
* - _zoomCamera: Called for zoom handling
|
|
16
|
-
* - _moveCurr/_movePrev: Rotation tracking vectors
|
|
17
|
-
* - _zoomStart/_zoomEnd: Zoom tracking vectors
|
|
18
|
-
* - _panStart/_panEnd: Pan tracking vectors
|
|
19
|
-
* - _eye: Camera-to-target vector
|
|
20
|
-
* - _lastPosition: Change detection
|
|
21
|
-
* - _target0/_position0/_up0/_zoom0: Saved state for reset
|
|
22
|
-
* - state/keyState: Current interaction mode tracking
|
|
23
|
-
* - noRotate/noZoom/noPan: Feature disable flags
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
import { TrackballControls } from "three/examples/jsm/controls/TrackballControls.js";
|
|
27
|
-
import { MOUSE, Quaternion, Vector2, Vector3, Camera } from "three";
|
|
28
|
-
import {
|
|
29
|
-
KeyMapper,
|
|
30
|
-
AXIS_VECTORS,
|
|
31
|
-
isOrthographicCamera,
|
|
32
|
-
isPerspectiveCamera,
|
|
33
|
-
} from "../../utils/utils.js";
|
|
34
|
-
import type { Axis } from "../../core/types.js";
|
|
35
|
-
|
|
36
|
-
// State constants matching TrackballControls internal state
|
|
37
|
-
const STATE = {
|
|
38
|
-
NONE: -1,
|
|
39
|
-
ROTATE: 0,
|
|
40
|
-
ZOOM: 1,
|
|
41
|
-
PAN: 2,
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
// Used for change detection in holroyd mode
|
|
45
|
-
const _lastQuaternion = new Quaternion();
|
|
46
|
-
let _lastZoom = 1;
|
|
47
|
-
|
|
48
|
-
// Reusable objects for rotation calculations
|
|
49
|
-
const _quaternion = new Quaternion();
|
|
50
|
-
const _axis = new Vector3();
|
|
51
|
-
const _rotateStart3 = new Vector3();
|
|
52
|
-
const _rotateEnd3 = new Vector3();
|
|
53
|
-
|
|
54
|
-
// Reusable objects for pan calculations
|
|
55
|
-
const _panDirection = new Vector3();
|
|
56
|
-
const _cameraUp = new Vector3();
|
|
57
|
-
const _cameraRight = new Vector3();
|
|
58
|
-
|
|
59
|
-
class CADTrackballControls extends TrackballControls {
|
|
60
|
-
holroyd: boolean;
|
|
61
|
-
radius: number;
|
|
62
|
-
quaternion0: Quaternion;
|
|
63
|
-
private _holroydStart: Vector2;
|
|
64
|
-
private _holroydEnd: Vector2;
|
|
65
|
-
private _holroydActive: boolean;
|
|
66
|
-
private _horizontalRotate: boolean;
|
|
67
|
-
private _verticalRotate: boolean;
|
|
68
|
-
private _holroydPointerDown?: (event: PointerEvent) => void;
|
|
69
|
-
private _holroydPointerMove?: (event: PointerEvent) => void;
|
|
70
|
-
private _holroydPointerUp?: () => void;
|
|
71
|
-
private _holroydWheel?: (event: WheelEvent) => void;
|
|
72
|
-
private _parentOnMouseDown!: (event: MouseEvent) => void;
|
|
73
|
-
|
|
74
|
-
// Expose internal properties for type safety
|
|
75
|
-
declare state: number;
|
|
76
|
-
declare keyState: number;
|
|
77
|
-
declare _moveCurr: Vector2;
|
|
78
|
-
declare _movePrev: Vector2;
|
|
79
|
-
declare _zoomStart: Vector2;
|
|
80
|
-
declare _zoomEnd: Vector2;
|
|
81
|
-
declare _panStart: Vector2;
|
|
82
|
-
declare _panEnd: Vector2;
|
|
83
|
-
declare _eye: Vector3;
|
|
84
|
-
declare _lastPosition: Vector3;
|
|
85
|
-
declare _target0: Vector3;
|
|
86
|
-
declare _position0: Vector3;
|
|
87
|
-
declare _up0: Vector3;
|
|
88
|
-
declare _zoom0: number;
|
|
89
|
-
declare noRotate: boolean;
|
|
90
|
-
declare noZoom: boolean;
|
|
91
|
-
declare noPan: boolean;
|
|
92
|
-
declare _onMouseDown: (event: MouseEvent) => void;
|
|
93
|
-
declare _getMouseOnCircle: (pageX: number, pageY: number) => Vector2;
|
|
94
|
-
declare _getMouseOnScreen: (pageX: number, pageY: number) => Vector2;
|
|
95
|
-
declare _zoomCamera: () => void;
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Constructs CAD-enhanced trackball controls.
|
|
99
|
-
*
|
|
100
|
-
* @param object - The camera to control.
|
|
101
|
-
* @param domElement - The HTML element for event listeners.
|
|
102
|
-
*/
|
|
103
|
-
constructor(object: Camera, domElement: HTMLElement | null = null) {
|
|
104
|
-
super(object, domElement!);
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Enable holroyd (non-tumbling) trackball mode.
|
|
108
|
-
* When true, uses a projection that prevents disorientation.
|
|
109
|
-
*/
|
|
110
|
-
this.holroyd = true;
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* NDC trackball radius for holroyd projection.
|
|
114
|
-
*/
|
|
115
|
-
this.radius = 0.9;
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Saved quaternion for reset (in addition to position/up/zoom).
|
|
119
|
-
*/
|
|
120
|
-
this.quaternion0 = this.object.quaternion.clone();
|
|
121
|
-
|
|
122
|
-
// Holroyd-specific: track absolute page coordinates for sphere projection
|
|
123
|
-
// These store the raw page coordinates, not processed values
|
|
124
|
-
this._holroydStart = new Vector2();
|
|
125
|
-
this._holroydEnd = new Vector2();
|
|
126
|
-
this._holroydActive = false;
|
|
127
|
-
|
|
128
|
-
// Rotation axis restriction flags (set via modifier keys)
|
|
129
|
-
// When false, that axis is locked (coordinate set to 0 in sphere projection)
|
|
130
|
-
this._horizontalRotate = true; // meta key restricts to horizontal only
|
|
131
|
-
this._verticalRotate = true; // ctrl key restricts to vertical only
|
|
132
|
-
|
|
133
|
-
// Add our own pointer event listeners to capture raw coordinates
|
|
134
|
-
// This runs alongside the parent's handlers
|
|
135
|
-
if (domElement) {
|
|
136
|
-
this._holroydPointerDown = this._onHolroydPointerDown.bind(this);
|
|
137
|
-
this._holroydPointerMove = this._onHolroydPointerMove.bind(this);
|
|
138
|
-
this._holroydPointerUp = this._onHolroydPointerUp.bind(this);
|
|
139
|
-
this._holroydWheel = this._onHolroydWheel.bind(this);
|
|
140
|
-
domElement.addEventListener("pointerdown", this._holroydPointerDown);
|
|
141
|
-
domElement.addEventListener("pointermove", this._holroydPointerMove);
|
|
142
|
-
domElement.addEventListener("pointerup", this._holroydPointerUp);
|
|
143
|
-
domElement.addEventListener("pointercancel", this._holroydPointerUp);
|
|
144
|
-
domElement.addEventListener("wheel", this._holroydWheel, {
|
|
145
|
-
passive: false,
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Save parent's _onMouseDown before overriding (for holroyd=false fallback)
|
|
150
|
-
this._parentOnMouseDown = this._onMouseDown;
|
|
151
|
-
this._onMouseDown = this._handleMouseDown.bind(this);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Custom mouse down handler to support shift+drag for pan.
|
|
156
|
-
* When holroyd=false, delegates to parent for pure Three.js behavior.
|
|
157
|
-
*/
|
|
158
|
-
private _handleMouseDown(event: MouseEvent): void {
|
|
159
|
-
// When holroyd is disabled, use pure Three.js TrackballControls behavior
|
|
160
|
-
if (!this.holroyd) {
|
|
161
|
-
this._parentOnMouseDown(event);
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
let mouseAction: number;
|
|
166
|
-
|
|
167
|
-
switch (event.button) {
|
|
168
|
-
case 0:
|
|
169
|
-
mouseAction = this.mouseButtons.LEFT!;
|
|
170
|
-
break;
|
|
171
|
-
case 1:
|
|
172
|
-
mouseAction = this.mouseButtons.MIDDLE!;
|
|
173
|
-
break;
|
|
174
|
-
case 2:
|
|
175
|
-
mouseAction = this.mouseButtons.RIGHT!;
|
|
176
|
-
break;
|
|
177
|
-
default:
|
|
178
|
-
mouseAction = -1;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// Shift + left click = pan (via KeyMapper)
|
|
182
|
-
if (mouseAction === MOUSE.ROTATE && KeyMapper.get(event, "shift")) {
|
|
183
|
-
mouseAction = MOUSE.PAN;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
switch (mouseAction) {
|
|
187
|
-
case MOUSE.DOLLY:
|
|
188
|
-
this.state = STATE.ZOOM;
|
|
189
|
-
break;
|
|
190
|
-
case MOUSE.ROTATE:
|
|
191
|
-
this.state = STATE.ROTATE;
|
|
192
|
-
break;
|
|
193
|
-
case MOUSE.PAN:
|
|
194
|
-
this.state = STATE.PAN;
|
|
195
|
-
break;
|
|
196
|
-
default:
|
|
197
|
-
this.state = STATE.NONE;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
const state = this.keyState !== STATE.NONE ? this.keyState : this.state;
|
|
201
|
-
|
|
202
|
-
if (state === STATE.ROTATE && !this.noRotate) {
|
|
203
|
-
this._moveCurr.copy(this._getMouseOnCircle(event.pageX, event.pageY));
|
|
204
|
-
this._movePrev.copy(this._moveCurr);
|
|
205
|
-
} else if (state === STATE.ZOOM && !this.noZoom) {
|
|
206
|
-
this._zoomStart.copy(this._getMouseOnScreen(event.pageX, event.pageY));
|
|
207
|
-
this._zoomEnd.copy(this._zoomStart);
|
|
208
|
-
} else if (state === STATE.PAN && !this.noPan) {
|
|
209
|
-
this._panStart.copy(this._getMouseOnScreen(event.pageX, event.pageY));
|
|
210
|
-
this._panEnd.copy(this._panStart);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
this.dispatchEvent({ type: "start" });
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Capture raw pointer coordinates on pointer down for holroyd.
|
|
218
|
-
* Works for all pointer types (mouse, touch, pen, trackpad).
|
|
219
|
-
* Also checks modifier keys for rotation axis restriction.
|
|
220
|
-
*/
|
|
221
|
-
private _onHolroydPointerDown(event: PointerEvent): void {
|
|
222
|
-
if (!this.holroyd) return;
|
|
223
|
-
|
|
224
|
-
// Only activate holroyd for rotation (left mouse button or touch)
|
|
225
|
-
// Right mouse (button 2) is for pan, middle (button 1) for zoom
|
|
226
|
-
// For touch, button is 0
|
|
227
|
-
if (event.button !== 0) return;
|
|
228
|
-
|
|
229
|
-
// Shift key triggers pan instead of rotation (via KeyMapper)
|
|
230
|
-
if (KeyMapper.get(event, "shift")) return;
|
|
231
|
-
|
|
232
|
-
this._holroydStart.set(event.pageX, event.pageY);
|
|
233
|
-
this._holroydEnd.set(event.pageX, event.pageY);
|
|
234
|
-
this._holroydActive = true;
|
|
235
|
-
|
|
236
|
-
// Check modifier keys for rotation restriction
|
|
237
|
-
// Works for all pointer types (e.g., touchscreen + keyboard on laptops)
|
|
238
|
-
// ctrl: restrict to vertical rotation only (horizontalRotate = false)
|
|
239
|
-
// meta: restrict to horizontal rotation only (verticalRotate = false)
|
|
240
|
-
this._horizontalRotate = !KeyMapper.get(event, "ctrl");
|
|
241
|
-
this._verticalRotate = !KeyMapper.get(event, "meta");
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Capture raw pointer coordinates on pointer move for holroyd.
|
|
246
|
-
* Only captures when actively dragging.
|
|
247
|
-
* Works for all pointer types (mouse, touch, pen, trackpad).
|
|
248
|
-
*/
|
|
249
|
-
private _onHolroydPointerMove(event: PointerEvent): void {
|
|
250
|
-
if (this.holroyd && this._holroydActive) {
|
|
251
|
-
this._holroydEnd.set(event.pageX, event.pageY);
|
|
252
|
-
}
|
|
253
|
-
// Call update to process the pointer movement and dispatch "change" event
|
|
254
|
-
// This enables change-listener mode (non-animation loop) to work
|
|
255
|
-
// Note: this runs for all pointer moves while dragging (rotate, pan, zoom)
|
|
256
|
-
if (this.state !== -1) {
|
|
257
|
-
// STATE.NONE = -1
|
|
258
|
-
this.update();
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Reset holroyd active state and rotation restrictions on pointer up.
|
|
264
|
-
*/
|
|
265
|
-
private _onHolroydPointerUp(): void {
|
|
266
|
-
this._holroydActive = false;
|
|
267
|
-
this._horizontalRotate = true;
|
|
268
|
-
this._verticalRotate = true;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* Handle wheel events for zoom - call update after parent processes wheel.
|
|
273
|
-
* This enables change-listener mode (non-animation loop) to work for zoom.
|
|
274
|
-
*/
|
|
275
|
-
private _onHolroydWheel(): void {
|
|
276
|
-
// Parent's wheel handler already processed the event, just call update
|
|
277
|
-
this.update();
|
|
278
|
-
|
|
279
|
-
// TrackballControls uses dynamic damping (staticMoving=false) which only
|
|
280
|
-
// consumes ~20% of the zoom delta per update() call. In animation-loop mode
|
|
281
|
-
// this creates smooth deceleration, but in change-listener mode (no continuous
|
|
282
|
-
// updates) the residual delta persists and gets applied on the next interaction
|
|
283
|
-
// (e.g., clicking to rotate), causing a phantom zoom. Clear it.
|
|
284
|
-
this._zoomStart.copy(this._zoomEnd);
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* Override dispose to clean up our event listeners.
|
|
289
|
-
*/
|
|
290
|
-
dispose(): void {
|
|
291
|
-
if (
|
|
292
|
-
this.domElement &&
|
|
293
|
-
this._holroydPointerDown &&
|
|
294
|
-
this._holroydPointerMove &&
|
|
295
|
-
this._holroydPointerUp &&
|
|
296
|
-
this._holroydWheel
|
|
297
|
-
) {
|
|
298
|
-
this.domElement.removeEventListener(
|
|
299
|
-
"pointerdown",
|
|
300
|
-
this._holroydPointerDown,
|
|
301
|
-
);
|
|
302
|
-
this.domElement.removeEventListener(
|
|
303
|
-
"pointermove",
|
|
304
|
-
this._holroydPointerMove,
|
|
305
|
-
);
|
|
306
|
-
this.domElement.removeEventListener("pointerup", this._holroydPointerUp);
|
|
307
|
-
this.domElement.removeEventListener(
|
|
308
|
-
"pointercancel",
|
|
309
|
-
this._holroydPointerUp,
|
|
310
|
-
);
|
|
311
|
-
this.domElement.removeEventListener("wheel", this._holroydWheel);
|
|
312
|
-
}
|
|
313
|
-
super.dispose();
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* Save the current state including quaternion.
|
|
318
|
-
*/
|
|
319
|
-
saveState(): void {
|
|
320
|
-
this._target0.copy(this.target);
|
|
321
|
-
this._position0.copy(this.object.position);
|
|
322
|
-
this._up0.copy(this.object.up);
|
|
323
|
-
if (isPerspectiveCamera(this.object) || isOrthographicCamera(this.object)) {
|
|
324
|
-
this._zoom0 = this.object.zoom;
|
|
325
|
-
}
|
|
326
|
-
this.quaternion0.copy(this.object.quaternion);
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
/**
|
|
330
|
-
* Reset to saved state including quaternion.
|
|
331
|
-
*/
|
|
332
|
-
reset(): void {
|
|
333
|
-
super.reset();
|
|
334
|
-
this.object.quaternion.copy(this.quaternion0);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
// Expose saved state properties for compatibility with controls.js
|
|
338
|
-
get target0(): Vector3 {
|
|
339
|
-
return this._target0;
|
|
340
|
-
}
|
|
341
|
-
get position0(): Vector3 {
|
|
342
|
-
return this._position0;
|
|
343
|
-
}
|
|
344
|
-
get zoom0(): number {
|
|
345
|
-
return this._zoom0;
|
|
346
|
-
}
|
|
347
|
-
set zoom0(value: number) {
|
|
348
|
-
this._zoom0 = value;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
/**
|
|
352
|
-
* Project page coordinates onto the holroyd trackball sphere.
|
|
353
|
-
* Uses the original CameraControls coordinate system:
|
|
354
|
-
* - NDC x: -1 (left) to +1 (right)
|
|
355
|
-
* - NDC y: -1 (bottom) to +1 (top)
|
|
356
|
-
*/
|
|
357
|
-
private _getMouseOnSphere(
|
|
358
|
-
pageX: number,
|
|
359
|
-
pageY: number,
|
|
360
|
-
target: Vector3,
|
|
361
|
-
): Vector3 {
|
|
362
|
-
const rect = this.domElement!.getBoundingClientRect();
|
|
363
|
-
|
|
364
|
-
// Convert to NDC space (-1 to 1)
|
|
365
|
-
// Note: Do NOT apply rotateSpeed here - it would break the sphere geometry
|
|
366
|
-
// rotateSpeed is applied to the final angle instead
|
|
367
|
-
// Apply rotation axis restrictions: set coordinate to 0 if that axis is locked
|
|
368
|
-
const x = this._horizontalRotate
|
|
369
|
-
? (pageX - rect.left) / (rect.width / 2) - 1.0
|
|
370
|
-
: 0;
|
|
371
|
-
const y = this._verticalRotate
|
|
372
|
-
? 1.0 - (pageY - rect.top) / (rect.height / 2)
|
|
373
|
-
: 0;
|
|
374
|
-
|
|
375
|
-
// Holroyd sphere projection
|
|
376
|
-
const r2 = this.radius * this.radius;
|
|
377
|
-
const d2 = x * x + y * y;
|
|
378
|
-
|
|
379
|
-
if (d2 <= r2 / 2) {
|
|
380
|
-
// Inside sphere - project onto sphere surface
|
|
381
|
-
target.set(x, y, Math.sqrt(r2 - d2));
|
|
382
|
-
} else {
|
|
383
|
-
// Outside sphere - use hyperbolic sheet for smooth falloff
|
|
384
|
-
target.set(x, y, r2 / (2 * Math.sqrt(d2)));
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
return target;
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
/**
|
|
391
|
-
* Override update to skip lookAt in holroyd mode.
|
|
392
|
-
*
|
|
393
|
-
* Standard TrackballControls calls lookAt() which recomputes the quaternion
|
|
394
|
-
* from position and up. In holroyd mode, we set the quaternion directly,
|
|
395
|
-
* so lookAt() would destroy the tilted rotation axis effect.
|
|
396
|
-
*
|
|
397
|
-
* When holroyd=false, delegates to parent for pure Three.js behavior.
|
|
398
|
-
*/
|
|
399
|
-
update(): void {
|
|
400
|
-
// When holroyd is disabled, use pure Three.js TrackballControls behavior
|
|
401
|
-
if (!this.holroyd) {
|
|
402
|
-
super.update();
|
|
403
|
-
return;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
this._eye.subVectors(this.object.position, this.target);
|
|
407
|
-
|
|
408
|
-
if (!this.noRotate) {
|
|
409
|
-
this._rotateCamera();
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
if (!this.noZoom) {
|
|
413
|
-
this._zoomCamera();
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
if (!this.noPan) {
|
|
417
|
-
this._panCamera();
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
this.object.position.addVectors(this.target, this._eye);
|
|
421
|
-
|
|
422
|
-
// In holroyd mode, we set quaternion directly - skip lookAt
|
|
423
|
-
// Just check for changes and dispatch event
|
|
424
|
-
const currentZoom =
|
|
425
|
-
isPerspectiveCamera(this.object) || isOrthographicCamera(this.object)
|
|
426
|
-
? this.object.zoom
|
|
427
|
-
: 1;
|
|
428
|
-
const zoomChanged = Math.abs(currentZoom - _lastZoom) > 0.000001;
|
|
429
|
-
if (
|
|
430
|
-
this._lastPosition.distanceToSquared(this.object.position) > 0.000001 ||
|
|
431
|
-
_lastQuaternion.dot(this.object.quaternion) < 0.999999 ||
|
|
432
|
-
zoomChanged
|
|
433
|
-
) {
|
|
434
|
-
this.dispatchEvent({ type: "change" });
|
|
435
|
-
this._lastPosition.copy(this.object.position);
|
|
436
|
-
_lastQuaternion.copy(this.object.quaternion);
|
|
437
|
-
_lastZoom = currentZoom;
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
/**
|
|
442
|
-
* Override rotation to support holroyd mode.
|
|
443
|
-
*
|
|
444
|
-
* The key difference from standard TrackballControls:
|
|
445
|
-
* - Standard: uses delta-based rotation from _moveCurr - _movePrev
|
|
446
|
-
* - Holroyd: projects absolute positions onto a virtual sphere
|
|
447
|
-
*
|
|
448
|
-
* This gives the "grab and rotate" feel where the rotation axis
|
|
449
|
-
* depends on WHERE you grab, not just HOW you move.
|
|
450
|
-
*/
|
|
451
|
-
_rotateCamera(): void {
|
|
452
|
-
if (!this.holroyd) {
|
|
453
|
-
// Use default TrackballControls rotation
|
|
454
|
-
super._rotateCamera();
|
|
455
|
-
return;
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
// Only process if start and end are different (actual movement)
|
|
459
|
-
if (
|
|
460
|
-
this._holroydStart.x === this._holroydEnd.x &&
|
|
461
|
-
this._holroydStart.y === this._holroydEnd.y
|
|
462
|
-
) {
|
|
463
|
-
this._movePrev.copy(this._moveCurr);
|
|
464
|
-
return;
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
// Project both start and end positions onto the holroyd sphere
|
|
468
|
-
this._getMouseOnSphere(
|
|
469
|
-
this._holroydStart.x,
|
|
470
|
-
this._holroydStart.y,
|
|
471
|
-
_rotateStart3,
|
|
472
|
-
);
|
|
473
|
-
this._getMouseOnSphere(this._holroydEnd.x, this._holroydEnd.y, _rotateEnd3);
|
|
474
|
-
|
|
475
|
-
// Calculate rotation axis as cross product of the two sphere points
|
|
476
|
-
_axis.crossVectors(_rotateStart3, _rotateEnd3);
|
|
477
|
-
const angle = Math.atan(_axis.length() / _rotateStart3.dot(_rotateEnd3));
|
|
478
|
-
|
|
479
|
-
if (angle) {
|
|
480
|
-
_axis.normalize();
|
|
481
|
-
|
|
482
|
-
// Transform axis from screen space to world space via camera orientation
|
|
483
|
-
_axis.applyQuaternion(this.object.quaternion);
|
|
484
|
-
|
|
485
|
-
// Apply rotation - use full rotation (no damping) to preserve non-tumbling property
|
|
486
|
-
// The original CameraControls had enableDamping=false by default
|
|
487
|
-
// Damping would break the geodesic rotation property that prevents tumbling
|
|
488
|
-
const finalAngle = -2 * angle * this.rotateSpeed;
|
|
489
|
-
|
|
490
|
-
_quaternion.setFromAxisAngle(_axis, finalAngle);
|
|
491
|
-
|
|
492
|
-
// Apply rotation via premultiplication (world-space rotation)
|
|
493
|
-
this.object.quaternion.premultiply(_quaternion);
|
|
494
|
-
this._eye.applyQuaternion(_quaternion);
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
// Update start to end for next frame
|
|
498
|
-
this._holroydStart.copy(this._holroydEnd);
|
|
499
|
-
|
|
500
|
-
// Keep parent state consistent
|
|
501
|
-
this._movePrev.copy(this._moveCurr);
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
/**
|
|
505
|
-
* Override pan to use quaternion-based camera orientation in holroyd mode.
|
|
506
|
-
*
|
|
507
|
-
* The parent TrackballControls uses this.object.up for pan direction,
|
|
508
|
-
* but in holroyd mode we rotate via quaternion without updating up.
|
|
509
|
-
* This calculates pan direction from the camera's actual orientation.
|
|
510
|
-
*/
|
|
511
|
-
_panCamera(): void {
|
|
512
|
-
if (!this.holroyd) {
|
|
513
|
-
super._panCamera();
|
|
514
|
-
return;
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
const mouseChange = _panDirection.set(
|
|
518
|
-
this._panEnd.x - this._panStart.x,
|
|
519
|
-
this._panEnd.y - this._panStart.y,
|
|
520
|
-
0,
|
|
521
|
-
);
|
|
522
|
-
|
|
523
|
-
if (mouseChange.lengthSq() === 0) {
|
|
524
|
-
return;
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
// Apply pan scaling based on camera type
|
|
528
|
-
if (isOrthographicCamera(this.object)) {
|
|
529
|
-
// For orthographic: pan distance = frustum size at zoom level
|
|
530
|
-
// mouseChange is already normalized, so just scale by world units visible
|
|
531
|
-
const scaleX = (this.object.right - this.object.left) / this.object.zoom;
|
|
532
|
-
const scaleY = (this.object.top - this.object.bottom) / this.object.zoom;
|
|
533
|
-
mouseChange.x *= scaleX * this.panSpeed * 4;
|
|
534
|
-
mouseChange.y *= scaleY * this.panSpeed * 4;
|
|
535
|
-
} else if (isPerspectiveCamera(this.object) && this.domElement) {
|
|
536
|
-
// For perspective: correct for aspect ratio since _getMouseOnScreen normalizes by width
|
|
537
|
-
const aspect = this.domElement.clientWidth / this.domElement.clientHeight;
|
|
538
|
-
mouseChange.x *= aspect;
|
|
539
|
-
mouseChange.multiplyScalar(this._eye.length() * this.panSpeed * 1.6);
|
|
540
|
-
} else {
|
|
541
|
-
// Fallback for other camera types
|
|
542
|
-
mouseChange.multiplyScalar(this._eye.length() * this.panSpeed * 2.0);
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
// Get camera's actual right and up vectors from quaternion
|
|
546
|
-
// Camera looks down -Z in its local space, so:
|
|
547
|
-
// - local +X is right
|
|
548
|
-
// - local +Y is up
|
|
549
|
-
_cameraRight.set(1, 0, 0).applyQuaternion(this.object.quaternion);
|
|
550
|
-
_cameraUp.set(0, 1, 0).applyQuaternion(this.object.quaternion);
|
|
551
|
-
|
|
552
|
-
// Pan = right * mouseX + up * mouseY (negate X for correct direction)
|
|
553
|
-
_cameraRight.multiplyScalar(-mouseChange.x);
|
|
554
|
-
_cameraUp.multiplyScalar(mouseChange.y);
|
|
555
|
-
|
|
556
|
-
this.object.position.add(_cameraRight).add(_cameraUp);
|
|
557
|
-
this.target.add(_cameraRight).add(_cameraUp);
|
|
558
|
-
|
|
559
|
-
this._panStart.copy(this._panEnd);
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
/**
|
|
563
|
-
* Rotate camera around world X-axis.
|
|
564
|
-
*/
|
|
565
|
-
rotateX(angle: number): void {
|
|
566
|
-
this._rotateAroundAxis("x", angle);
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
/**
|
|
570
|
-
* Rotate camera around world Y-axis.
|
|
571
|
-
*/
|
|
572
|
-
rotateY(angle: number): void {
|
|
573
|
-
this._rotateAroundAxis("y", angle);
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
/**
|
|
577
|
-
* Rotate camera around world Z-axis.
|
|
578
|
-
*/
|
|
579
|
-
rotateZ(angle: number): void {
|
|
580
|
-
this._rotateAroundAxis("z", angle);
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
/**
|
|
584
|
-
* Internal method to rotate around a world axis.
|
|
585
|
-
*/
|
|
586
|
-
private _rotateAroundAxis(axisName: Axis, angle: number): void {
|
|
587
|
-
const axis = AXIS_VECTORS[axisName];
|
|
588
|
-
_quaternion.setFromAxisAngle(axis, angle);
|
|
589
|
-
|
|
590
|
-
this.object.quaternion.premultiply(_quaternion);
|
|
591
|
-
this.object.position
|
|
592
|
-
.sub(this.target)
|
|
593
|
-
.applyQuaternion(_quaternion)
|
|
594
|
-
.add(this.target);
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
export { CADTrackballControls };
|