three-cad-viewer 4.3.4 → 4.3.6
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/scene/clipping.d.ts +6 -0
- package/dist/three-cad-viewer.esm.js +20 -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 +20 -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 -640
- package/src/scene/grid.ts +0 -864
- package/src/scene/nestedgroup.ts +0 -1444
- 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,811 +0,0 @@
|
|
|
1
|
-
import * as THREE from "three";
|
|
2
|
-
import { LineSegments2 } from "three/examples/jsm/lines/LineSegments2.js";
|
|
3
|
-
import { LineSegmentsGeometry } from "three/examples/jsm/lines/LineSegmentsGeometry.js";
|
|
4
|
-
import { LineMaterial } from "three/examples/jsm/lines/LineMaterial.js";
|
|
5
|
-
import { DistancePanel, PropertiesPanel, DistanceResponseData, PropertiesResponseData } from "./ui.js";
|
|
6
|
-
import { deepDispose, isMesh, isLineSegments2, isPerspectiveCamera, isOrthographicCamera } from "../../utils/utils.js";
|
|
7
|
-
import type { PickedObject } from "../../rendering/raycast.js";
|
|
8
|
-
import type { ViewerLike } from "./tools.js";
|
|
9
|
-
|
|
10
|
-
interface PanelDragData {
|
|
11
|
-
x: number | null;
|
|
12
|
-
y: number | null;
|
|
13
|
-
clicked: boolean;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
class DistanceLineArrow extends THREE.Group {
|
|
17
|
-
coneLength: number;
|
|
18
|
-
point1: THREE.Vector3;
|
|
19
|
-
point2: THREE.Vector3;
|
|
20
|
-
linewidth: number;
|
|
21
|
-
color: number;
|
|
22
|
-
arrowStart: boolean;
|
|
23
|
-
arrowEnd: boolean;
|
|
24
|
-
override type: string;
|
|
25
|
-
lineVec!: THREE.Vector3;
|
|
26
|
-
mode: "inward" | "outward";
|
|
27
|
-
renderMode: "arrows" | "dot";
|
|
28
|
-
|
|
29
|
-
constructor(
|
|
30
|
-
coneLength: number,
|
|
31
|
-
point1: THREE.Vector3,
|
|
32
|
-
point2: THREE.Vector3,
|
|
33
|
-
linewidth: number,
|
|
34
|
-
color: number,
|
|
35
|
-
arrowStart: boolean = true,
|
|
36
|
-
arrowEnd: boolean = true,
|
|
37
|
-
) {
|
|
38
|
-
super();
|
|
39
|
-
this.coneLength = coneLength;
|
|
40
|
-
this.point1 = point1;
|
|
41
|
-
this.point2 = point2;
|
|
42
|
-
this.linewidth = linewidth;
|
|
43
|
-
this.color = color;
|
|
44
|
-
this.arrowStart = arrowStart;
|
|
45
|
-
this.arrowEnd = arrowEnd;
|
|
46
|
-
this.type = "DistanceLineArrow";
|
|
47
|
-
this.mode = "inward";
|
|
48
|
-
this.renderMode = "arrows";
|
|
49
|
-
this.initialize();
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
initialize(): void {
|
|
53
|
-
const coneLength = this.coneLength * 0.8;
|
|
54
|
-
const dist = this.point1.distanceTo(this.point2);
|
|
55
|
-
|
|
56
|
-
// Coincident points: show a single dot instead of arrows
|
|
57
|
-
if (dist < 1e-9) {
|
|
58
|
-
this.renderMode = "dot";
|
|
59
|
-
const sphereGeom = new THREE.SphereGeometry(coneLength / 6, 16, 12);
|
|
60
|
-
const sphereMat = new THREE.MeshBasicMaterial({ color: this.color });
|
|
61
|
-
const dot = new THREE.Mesh(sphereGeom, sphereMat);
|
|
62
|
-
dot.name = "dot";
|
|
63
|
-
dot.position.copy(this.point1);
|
|
64
|
-
this.add(dot);
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
this.renderMode = "arrows";
|
|
69
|
-
this.lineVec = this.point1.clone().sub(this.point2.clone()).normalize();
|
|
70
|
-
|
|
71
|
-
// Determine mode based on distance between points
|
|
72
|
-
if (dist < coneLength) {
|
|
73
|
-
this.mode = "outward";
|
|
74
|
-
} else {
|
|
75
|
-
this.mode = "inward";
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// sign: +1 = cones sit inside the line (inward), -1 = cones sit outside (outward)
|
|
79
|
-
const sign = this.mode === "inward" ? 1 : -1;
|
|
80
|
-
|
|
81
|
-
let start: THREE.Vector3, end: THREE.Vector3;
|
|
82
|
-
if (this.arrowStart) {
|
|
83
|
-
start = this.point1
|
|
84
|
-
.clone()
|
|
85
|
-
.sub(this.lineVec.clone().multiplyScalar(sign * coneLength * 0.3));
|
|
86
|
-
} else {
|
|
87
|
-
start = this.point1.clone();
|
|
88
|
-
}
|
|
89
|
-
if (this.arrowEnd) {
|
|
90
|
-
end = this.point2
|
|
91
|
-
.clone()
|
|
92
|
-
.sub(this.lineVec.clone().multiplyScalar(-sign * coneLength * 0.3));
|
|
93
|
-
} else {
|
|
94
|
-
end = this.point2.clone();
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const material = new LineMaterial({
|
|
98
|
-
linewidth: this.linewidth,
|
|
99
|
-
color: this.color,
|
|
100
|
-
});
|
|
101
|
-
const geom = new LineSegmentsGeometry();
|
|
102
|
-
geom.setPositions([...start.toArray(), ...end.toArray()]);
|
|
103
|
-
const line = new LineSegments2(geom, material);
|
|
104
|
-
|
|
105
|
-
const coneGeom = new THREE.ConeGeometry(coneLength / 4, coneLength * 0.6, 10);
|
|
106
|
-
const coneMaterial = new THREE.MeshBasicMaterial({ color: this.color });
|
|
107
|
-
const startCone = new THREE.Mesh(coneGeom, coneMaterial);
|
|
108
|
-
const endCone = new THREE.Mesh(coneGeom, coneMaterial);
|
|
109
|
-
startCone.name = "startCone";
|
|
110
|
-
endCone.name = "endCone";
|
|
111
|
-
|
|
112
|
-
const matrix = new THREE.Matrix4();
|
|
113
|
-
const quaternion = new THREE.Quaternion();
|
|
114
|
-
if (this.mode === "inward") {
|
|
115
|
-
// Cones point outward toward p1/p2
|
|
116
|
-
matrix.lookAt(this.point1, this.point2, startCone.up);
|
|
117
|
-
quaternion.setFromRotationMatrix(matrix);
|
|
118
|
-
startCone.setRotationFromQuaternion(quaternion);
|
|
119
|
-
matrix.lookAt(this.point2, this.point1, endCone.up);
|
|
120
|
-
quaternion.setFromRotationMatrix(matrix);
|
|
121
|
-
endCone.setRotationFromQuaternion(quaternion);
|
|
122
|
-
} else {
|
|
123
|
-
// Cones point inward toward the line center
|
|
124
|
-
matrix.lookAt(this.point2, this.point1, startCone.up);
|
|
125
|
-
quaternion.setFromRotationMatrix(matrix);
|
|
126
|
-
startCone.setRotationFromQuaternion(quaternion);
|
|
127
|
-
matrix.lookAt(this.point1, this.point2, endCone.up);
|
|
128
|
-
quaternion.setFromRotationMatrix(matrix);
|
|
129
|
-
endCone.setRotationFromQuaternion(quaternion);
|
|
130
|
-
}
|
|
131
|
-
startCone.rotateX((90 * Math.PI) / 180);
|
|
132
|
-
endCone.rotateX((90 * Math.PI) / 180);
|
|
133
|
-
|
|
134
|
-
startCone.position.copy(start);
|
|
135
|
-
endCone.position.copy(end);
|
|
136
|
-
|
|
137
|
-
if (this.arrowStart) this.add(startCone);
|
|
138
|
-
if (this.arrowEnd) this.add(endCone);
|
|
139
|
-
|
|
140
|
-
this.add(line);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Update the arrow so it keeps the same size on the screen.
|
|
145
|
-
*/
|
|
146
|
-
update(scaleFactor: number): void {
|
|
147
|
-
if (this.renderMode === "dot") {
|
|
148
|
-
const dot = this.children.find(
|
|
149
|
-
(child) => isMesh(child) && child.name === "dot",
|
|
150
|
-
);
|
|
151
|
-
if (dot && isMesh(dot)) {
|
|
152
|
-
dot.scale.set(scaleFactor, scaleFactor, scaleFactor);
|
|
153
|
-
}
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const coneLength = this.coneLength * 0.8;
|
|
158
|
-
const sign = this.mode === "inward" ? 1 : -1;
|
|
159
|
-
|
|
160
|
-
const newStart = this.point1
|
|
161
|
-
.clone()
|
|
162
|
-
.sub(
|
|
163
|
-
this.lineVec
|
|
164
|
-
.clone()
|
|
165
|
-
.multiplyScalar(sign * scaleFactor * coneLength * 0.3),
|
|
166
|
-
);
|
|
167
|
-
const newEnd = this.point2
|
|
168
|
-
.clone()
|
|
169
|
-
.sub(
|
|
170
|
-
this.lineVec
|
|
171
|
-
.clone()
|
|
172
|
-
.multiplyScalar(-sign * scaleFactor * coneLength * 0.3),
|
|
173
|
-
);
|
|
174
|
-
const line = this.children.find((child) => isLineSegments2(child));
|
|
175
|
-
if (line && isLineSegments2(line)) {
|
|
176
|
-
line.geometry.setPositions([
|
|
177
|
-
...(this.arrowStart ? newStart.toArray() : this.point1.toArray()),
|
|
178
|
-
...(this.arrowEnd ? newEnd.toArray() : this.point2.toArray()),
|
|
179
|
-
]);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
if (this.arrowStart) {
|
|
183
|
-
const startCone = this.children.find(
|
|
184
|
-
(child) => isMesh(child) && child.name === "startCone",
|
|
185
|
-
);
|
|
186
|
-
if (startCone && isMesh(startCone)) {
|
|
187
|
-
startCone.position.copy(newStart);
|
|
188
|
-
startCone.scale.set(scaleFactor, scaleFactor, scaleFactor);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
if (this.arrowEnd) {
|
|
192
|
-
const endCone = this.children.find(
|
|
193
|
-
(child) => isMesh(child) && child.name === "endCone",
|
|
194
|
-
);
|
|
195
|
-
if (endCone && isMesh(endCone)) {
|
|
196
|
-
endCone.position.copy(newEnd);
|
|
197
|
-
endCone.scale.set(scaleFactor, scaleFactor, scaleFactor);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Dispose of all geometries and materials.
|
|
204
|
-
* Handles shared geometry/material between cones.
|
|
205
|
-
*/
|
|
206
|
-
dispose(): void {
|
|
207
|
-
// Dispose line geometry and material
|
|
208
|
-
const line = this.children.find((child) => isLineSegments2(child));
|
|
209
|
-
if (line && isLineSegments2(line)) {
|
|
210
|
-
line.geometry.dispose();
|
|
211
|
-
line.material.dispose();
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Dispose cone geometry and material (shared between start and end cones)
|
|
215
|
-
const startCone = this.children.find(
|
|
216
|
-
(child) => isMesh(child) && child.name === "startCone",
|
|
217
|
-
);
|
|
218
|
-
if (startCone && isMesh(startCone)) {
|
|
219
|
-
startCone.geometry.dispose();
|
|
220
|
-
const material = startCone.material;
|
|
221
|
-
if (Array.isArray(material)) {
|
|
222
|
-
material.forEach((m) => m.dispose());
|
|
223
|
-
} else {
|
|
224
|
-
material.dispose();
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
// endCone shares geometry and material with startCone, no need to dispose again
|
|
228
|
-
|
|
229
|
-
// Dispose dot geometry and material
|
|
230
|
-
const dot = this.children.find(
|
|
231
|
-
(child) => isMesh(child) && child.name === "dot",
|
|
232
|
-
);
|
|
233
|
-
if (dot && isMesh(dot)) {
|
|
234
|
-
dot.geometry.dispose();
|
|
235
|
-
const dotMaterial = dot.material;
|
|
236
|
-
if (Array.isArray(dotMaterial)) {
|
|
237
|
-
dotMaterial.forEach((m) => m.dispose());
|
|
238
|
-
} else {
|
|
239
|
-
dotMaterial.dispose();
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
this.clear();
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
class Measurement {
|
|
248
|
-
selectedShapes: PickedObject[];
|
|
249
|
-
point1: THREE.Vector3 | null;
|
|
250
|
-
point2: THREE.Vector3 | null;
|
|
251
|
-
middlePoint: THREE.Vector3 | null;
|
|
252
|
-
contextEnabled: boolean;
|
|
253
|
-
viewer: ViewerLike | null;
|
|
254
|
-
scene: THREE.Scene | null;
|
|
255
|
-
panel: DistancePanel | PropertiesPanel | null;
|
|
256
|
-
panelCenter: THREE.Vector3 | null;
|
|
257
|
-
panelX: number | null;
|
|
258
|
-
panelY: number | null;
|
|
259
|
-
panelShown: boolean;
|
|
260
|
-
responseData: DistanceResponseData | PropertiesResponseData | null;
|
|
261
|
-
measurementLineColor: number;
|
|
262
|
-
connectingLineColor: number;
|
|
263
|
-
coneLength: number | undefined;
|
|
264
|
-
panelDragData: PanelDragData;
|
|
265
|
-
shift: boolean;
|
|
266
|
-
debug: boolean;
|
|
267
|
-
|
|
268
|
-
constructor(viewer: ViewerLike, panel: DistancePanel | PropertiesPanel) {
|
|
269
|
-
this.selectedShapes = [];
|
|
270
|
-
this.point1 = null;
|
|
271
|
-
this.point2 = null;
|
|
272
|
-
this.middlePoint = null;
|
|
273
|
-
this.contextEnabled = false;
|
|
274
|
-
this.viewer = viewer;
|
|
275
|
-
this.scene = new THREE.Scene();
|
|
276
|
-
this.panel = panel;
|
|
277
|
-
this.panelCenter = null;
|
|
278
|
-
this.panelX = null;
|
|
279
|
-
this.panelY = null;
|
|
280
|
-
this.panelShown = false;
|
|
281
|
-
this.responseData = null;
|
|
282
|
-
this.measurementLineColor = 0x000000;
|
|
283
|
-
this.connectingLineColor = 0x800080;
|
|
284
|
-
this.coneLength = undefined;
|
|
285
|
-
this.debug = false;
|
|
286
|
-
|
|
287
|
-
this.panelDragData = { x: null, y: null, clicked: false };
|
|
288
|
-
this.panel.registerCallback("mousedown", ((e: MouseEvent) => {
|
|
289
|
-
this.panelDragData.clicked = true;
|
|
290
|
-
this.panelDragData.x = e.clientX;
|
|
291
|
-
this.panelDragData.y = e.clientY;
|
|
292
|
-
e.stopPropagation();
|
|
293
|
-
}) as EventListener);
|
|
294
|
-
this.shift = false;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
enableContext(): void {
|
|
298
|
-
this.contextEnabled = true;
|
|
299
|
-
this.panelCenter = new THREE.Vector3(1, 0, 0);
|
|
300
|
-
|
|
301
|
-
document.addEventListener("mouseup", this._mouseup);
|
|
302
|
-
document.addEventListener("mousemove", this._dragPanel);
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
disableContext(): void {
|
|
306
|
-
this._hideMeasurement();
|
|
307
|
-
this.contextEnabled = false;
|
|
308
|
-
this.responseData = null;
|
|
309
|
-
|
|
310
|
-
for (const group of this.selectedShapes) {
|
|
311
|
-
group.obj.clearHighlights();
|
|
312
|
-
}
|
|
313
|
-
this.selectedShapes = [];
|
|
314
|
-
|
|
315
|
-
document.removeEventListener("mouseup", this._mouseup);
|
|
316
|
-
document.removeEventListener("mousemove", this._dragPanel);
|
|
317
|
-
|
|
318
|
-
this.viewer?.checkChanges({ selectedShapeIDs: [] });
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
_hideMeasurement(): void {
|
|
322
|
-
this.panel?.show(false);
|
|
323
|
-
this.disposeArrows();
|
|
324
|
-
this.scene?.clear();
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* Response handler for the measure context
|
|
329
|
-
*/
|
|
330
|
-
handleResponse(_response?: DistanceResponseData | PropertiesResponseData): void {}
|
|
331
|
-
|
|
332
|
-
_createPanel(): void {
|
|
333
|
-
throw new Error("Subclass needs to override this method");
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
_makeLines(): void {
|
|
337
|
-
throw new Error("Subclass needs to override this method");
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
_updateConnectionLine(): void {
|
|
341
|
-
throw new Error("Subclass needs to override this method");
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
/**
|
|
345
|
-
* Get the maximum number of selected obj this measurement can handle
|
|
346
|
-
*/
|
|
347
|
-
_getMaxObjSelected(): number {
|
|
348
|
-
throw new Error("Subclass needs to override this method");
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
/**
|
|
352
|
-
* Wait for the backend to send the data needed to display the real BREP measurement.
|
|
353
|
-
*/
|
|
354
|
-
_waitResponse(resolve: (data: DistanceResponseData | PropertiesResponseData) => void, _reject: (reason?: Error) => void): void {
|
|
355
|
-
if (this.responseData) {
|
|
356
|
-
resolve(this.responseData);
|
|
357
|
-
} else {
|
|
358
|
-
setTimeout(() => {
|
|
359
|
-
this._waitResponse(resolve, _reject);
|
|
360
|
-
}, 100);
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
/**
|
|
365
|
-
* Update the measurement panel, if enough shapes have been selected for the current tool,
|
|
366
|
-
* ask the backend for the real measurement data and display it.
|
|
367
|
-
*/
|
|
368
|
-
_updateMeasurement(): void {
|
|
369
|
-
const getId = (shape: PickedObject): string => {
|
|
370
|
-
if (shape.fromSolid) {
|
|
371
|
-
const solidId = shape.obj.name
|
|
372
|
-
.replace(/\|faces.*$/, "")
|
|
373
|
-
.replace(/\|edges.*$/, "")
|
|
374
|
-
.replace(/\|vertices.*$/, "");
|
|
375
|
-
return solidId.replaceAll("|", "/");
|
|
376
|
-
} else {
|
|
377
|
-
return shape.obj.name.replaceAll("|", "/");
|
|
378
|
-
}
|
|
379
|
-
};
|
|
380
|
-
const ids = this.selectedShapes.map(getId);
|
|
381
|
-
|
|
382
|
-
this.responseData = null;
|
|
383
|
-
if (this.debug) {
|
|
384
|
-
const delay = 50 + Math.floor(Math.random() * 200);
|
|
385
|
-
setTimeout(() => {
|
|
386
|
-
if (this.selectedShapes.length == 0) return;
|
|
387
|
-
|
|
388
|
-
// Helper to get bounding sphere center from first mesh child
|
|
389
|
-
const getBoundingSphereCenter = (obj: THREE.Object3D): THREE.Vector3 | null => {
|
|
390
|
-
const firstChild = obj.children[0];
|
|
391
|
-
if (firstChild && isMesh(firstChild)) {
|
|
392
|
-
firstChild.geometry.computeBoundingSphere();
|
|
393
|
-
return firstChild.geometry.boundingSphere?.center.clone() ?? null;
|
|
394
|
-
}
|
|
395
|
-
return null;
|
|
396
|
-
};
|
|
397
|
-
|
|
398
|
-
let responseData: DistanceResponseData | PropertiesResponseData;
|
|
399
|
-
if (this instanceof DistanceMeasurement) {
|
|
400
|
-
if (this.selectedShapes.length < 2) return;
|
|
401
|
-
const obj1 = this.selectedShapes[0].obj;
|
|
402
|
-
const obj2 = this.selectedShapes[1].obj;
|
|
403
|
-
const center1 = getBoundingSphereCenter(obj1);
|
|
404
|
-
const center2 = getBoundingSphereCenter(obj2);
|
|
405
|
-
if (!center1 || !center2) return;
|
|
406
|
-
this.point1 = obj1.localToWorld(center1);
|
|
407
|
-
this.point2 = obj2.localToWorld(center2);
|
|
408
|
-
responseData = {
|
|
409
|
-
groups: [
|
|
410
|
-
{ distance: 2.345, info: "center" },
|
|
411
|
-
{ "point 1": this.point1.toArray(), "point 2": this.point2.toArray() },
|
|
412
|
-
{ angle: 43.21, "reference 1": "Plane (Face)", "reference 2": "Plane (Face)" },
|
|
413
|
-
],
|
|
414
|
-
type: "backend_response",
|
|
415
|
-
refpoint1: this.point1.toArray(),
|
|
416
|
-
refpoint2: this.point2.toArray(),
|
|
417
|
-
};
|
|
418
|
-
} else if (this instanceof PropertiesMeasurement) {
|
|
419
|
-
const obj = this.selectedShapes[0].obj;
|
|
420
|
-
const center = getBoundingSphereCenter(obj);
|
|
421
|
-
if (!center) return;
|
|
422
|
-
this.point1 = obj.localToWorld(center);
|
|
423
|
-
responseData = {
|
|
424
|
-
type: "backend_response",
|
|
425
|
-
shape_type: "Edge",
|
|
426
|
-
geom_type: "EllipseArc",
|
|
427
|
-
refpoint: this.point1.toArray(),
|
|
428
|
-
groups: [
|
|
429
|
-
{ center: this.point1.toArray(), "major radius": 0.4, "minor radius": 0.2 },
|
|
430
|
-
{ start: [2.4, -1.0, 0.0], end: [1.8, -0.8267949192431111, 0.0] },
|
|
431
|
-
{ length: 0.6868592404716374 },
|
|
432
|
-
{ bb: { min: [1.8, -1.0, 0.0], center: [2.1, -0.9, 0.0], max: [2.4, -0.8, 0.0], size: [0.56, 0.2, 0.0] } },
|
|
433
|
-
],
|
|
434
|
-
};
|
|
435
|
-
} else {
|
|
436
|
-
return;
|
|
437
|
-
}
|
|
438
|
-
this.handleResponse(responseData);
|
|
439
|
-
}, delay);
|
|
440
|
-
} else {
|
|
441
|
-
this.viewer?.checkChanges({
|
|
442
|
-
selectedShapeIDs: [...ids, this.shift],
|
|
443
|
-
});
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
if (this.selectedShapes.length != this._getMaxObjSelected()) {
|
|
447
|
-
this._hideMeasurement();
|
|
448
|
-
return;
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
const p = new Promise<DistanceResponseData | PropertiesResponseData>((resolve, reject) => {
|
|
452
|
-
this._waitResponse(resolve, reject);
|
|
453
|
-
});
|
|
454
|
-
p.then((_data) => {
|
|
455
|
-
this._createPanel();
|
|
456
|
-
this._makeLines();
|
|
457
|
-
this.panel?.show(true);
|
|
458
|
-
this._movePanel();
|
|
459
|
-
});
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
/**
|
|
463
|
-
* React to each new selected element in the viewer.
|
|
464
|
-
*/
|
|
465
|
-
handleSelection = (selectedObj: PickedObject, shift: boolean = false): void => {
|
|
466
|
-
this.shift = shift;
|
|
467
|
-
|
|
468
|
-
if (
|
|
469
|
-
this.selectedShapes.find((o) => o.obj.name === selectedObj.obj.name) !==
|
|
470
|
-
undefined
|
|
471
|
-
)
|
|
472
|
-
this.selectedShapes.splice(this.selectedShapes.indexOf(selectedObj), 1);
|
|
473
|
-
else this.selectedShapes.push(selectedObj);
|
|
474
|
-
|
|
475
|
-
if (this.panel) {
|
|
476
|
-
this.panel.finished = false;
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
this._updateMeasurement();
|
|
480
|
-
};
|
|
481
|
-
|
|
482
|
-
_mouseup = (e: MouseEvent): void => {
|
|
483
|
-
this.panelDragData.clicked = false;
|
|
484
|
-
e.stopPropagation();
|
|
485
|
-
};
|
|
486
|
-
|
|
487
|
-
_movePanel = (): void => {
|
|
488
|
-
if (!this.panel || !this.viewer || !this.viewer.camera || !this.panel.isVisible()) return;
|
|
489
|
-
|
|
490
|
-
const canvasRect = this.viewer.renderer.domElement.getBoundingClientRect();
|
|
491
|
-
const panelRect = this.panel.html.getBoundingClientRect();
|
|
492
|
-
|
|
493
|
-
if (this.panelX == null && this.middlePoint != null) {
|
|
494
|
-
const center = this.middlePoint
|
|
495
|
-
.clone()
|
|
496
|
-
.project(this.viewer.camera.getCamera());
|
|
497
|
-
const panelX = (center.x + 1) * (canvasRect.width / 2);
|
|
498
|
-
const panelY = (1 - center.y) * (canvasRect.height / 2);
|
|
499
|
-
|
|
500
|
-
if (panelX < canvasRect.width / 2) {
|
|
501
|
-
this.panelX = panelX + panelRect.width / 2;
|
|
502
|
-
} else {
|
|
503
|
-
this.panelX = panelX - panelRect.width - panelRect.width / 2;
|
|
504
|
-
}
|
|
505
|
-
this.panelX = Math.max(
|
|
506
|
-
0,
|
|
507
|
-
Math.min(canvasRect.width - panelRect.width, this.panelX),
|
|
508
|
-
);
|
|
509
|
-
|
|
510
|
-
this.panelY = panelY;
|
|
511
|
-
this.panelY = Math.max(
|
|
512
|
-
0,
|
|
513
|
-
Math.min(canvasRect.height - panelRect.height, this.panelY),
|
|
514
|
-
);
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
this.panel.relocate(this.panelX, this.panelY);
|
|
518
|
-
|
|
519
|
-
if (this.panelX == null || this.panelY == null) return;
|
|
520
|
-
|
|
521
|
-
const panelCenterX = this.panelX + panelRect.width / 2;
|
|
522
|
-
const panelCenterY = this.panelY + panelRect.height / 2;
|
|
523
|
-
const ndcX = panelCenterX / (canvasRect.width / 2) - 1;
|
|
524
|
-
const ndcY = 1 - panelCenterY / (canvasRect.height / 2);
|
|
525
|
-
const ndcZ = this.viewer.ortho ? -0.9 : 1;
|
|
526
|
-
const panelCenter = new THREE.Vector3(ndcX, ndcY, ndcZ);
|
|
527
|
-
|
|
528
|
-
const camera = this.viewer.camera.getCamera();
|
|
529
|
-
if (isPerspectiveCamera(camera) || isOrthographicCamera(camera)) {
|
|
530
|
-
camera.updateProjectionMatrix();
|
|
531
|
-
camera.updateMatrixWorld();
|
|
532
|
-
this.panelCenter = panelCenter.unproject(camera);
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
if (this.scene && this.scene.children.length > 0) {
|
|
536
|
-
this._updateConnectionLine();
|
|
537
|
-
}
|
|
538
|
-
};
|
|
539
|
-
|
|
540
|
-
/**
|
|
541
|
-
* This handler is responsible to update the panel center vector when the user drag the panel on the screen.
|
|
542
|
-
*/
|
|
543
|
-
_dragPanel = (e: MouseEvent): void => {
|
|
544
|
-
if (!this.panelDragData.clicked || !this.panel || !this.viewer) return;
|
|
545
|
-
if (this.panelDragData.x == null || this.panelDragData.y == null) return;
|
|
546
|
-
const panelRect = this.panel.html.getBoundingClientRect();
|
|
547
|
-
const canvasRect = this.viewer.renderer.domElement.getBoundingClientRect();
|
|
548
|
-
const dx = e.clientX - this.panelDragData.x;
|
|
549
|
-
const dy = e.clientY - this.panelDragData.y;
|
|
550
|
-
|
|
551
|
-
if (
|
|
552
|
-
!(
|
|
553
|
-
(panelRect.x + dx < canvasRect.x && e.movementX <= 0) ||
|
|
554
|
-
(panelRect.x + dx > canvasRect.x + canvasRect.width - panelRect.width &&
|
|
555
|
-
e.movementX >= 0)
|
|
556
|
-
)
|
|
557
|
-
) {
|
|
558
|
-
if (this.panelX !== null) {
|
|
559
|
-
this.panelX += dx;
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
if (
|
|
563
|
-
!(
|
|
564
|
-
(panelRect.y + dy < canvasRect.y && e.movementY <= 0) ||
|
|
565
|
-
(panelRect.y + dy >
|
|
566
|
-
canvasRect.y + canvasRect.height - panelRect.height &&
|
|
567
|
-
e.movementY >= 0)
|
|
568
|
-
)
|
|
569
|
-
) {
|
|
570
|
-
if (this.panelY !== null) {
|
|
571
|
-
this.panelY += dy;
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
this._updateMeasurement();
|
|
576
|
-
|
|
577
|
-
// Update the drag start position
|
|
578
|
-
this.panelDragData.x = e.clientX;
|
|
579
|
-
this.panelDragData.y = e.clientY;
|
|
580
|
-
};
|
|
581
|
-
|
|
582
|
-
removeLastSelectedObj(force: boolean = false): void {
|
|
583
|
-
if (force || this.selectedShapes.length == this._getMaxObjSelected()) {
|
|
584
|
-
const lastItem = this.selectedShapes.pop();
|
|
585
|
-
if (lastItem) {
|
|
586
|
-
const objs = lastItem.objs();
|
|
587
|
-
for (const obj of objs) {
|
|
588
|
-
obj.clearHighlights();
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
this._updateMeasurement();
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
/**
|
|
596
|
-
* Adjust the arrow cones scale factor to ensure they keep the same size on the screen.
|
|
597
|
-
*/
|
|
598
|
-
_adjustArrowsScaleFactor(zoom: number): void {
|
|
599
|
-
if (!this.scene) return;
|
|
600
|
-
const scaleFactor = 1 / zoom;
|
|
601
|
-
this.scene.children.forEach((ch) => {
|
|
602
|
-
if (ch instanceof DistanceLineArrow) {
|
|
603
|
-
ch.update(scaleFactor);
|
|
604
|
-
}
|
|
605
|
-
});
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
update(): void {
|
|
609
|
-
if (!this.viewer || !this.viewer.camera || !this.scene) return;
|
|
610
|
-
const camera = this.viewer.camera.getCamera();
|
|
611
|
-
const zoom = this.viewer.camera.getZoom();
|
|
612
|
-
const cadWidth = this.viewer.state.get("cadWidth");
|
|
613
|
-
const height = this.viewer.state.get("height");
|
|
614
|
-
if (typeof cadWidth === "number" && typeof height === "number") {
|
|
615
|
-
this.coneLength =
|
|
616
|
-
this.viewer.bb_radius / (Math.max(cadWidth, height) / 60);
|
|
617
|
-
}
|
|
618
|
-
this._adjustArrowsScaleFactor(zoom);
|
|
619
|
-
this.viewer.renderer.clearDepth();
|
|
620
|
-
this._movePanel();
|
|
621
|
-
this.viewer.renderer.render(this.scene, camera);
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
disposeArrows(): void {
|
|
625
|
-
if (this.scene) {
|
|
626
|
-
deepDispose(this.scene);
|
|
627
|
-
this.scene.clear();
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
dispose(): void {
|
|
632
|
-
if (this.panel) {
|
|
633
|
-
this.panel.show(false);
|
|
634
|
-
deepDispose(this.panel);
|
|
635
|
-
}
|
|
636
|
-
this.disposeArrows();
|
|
637
|
-
this.panel = null;
|
|
638
|
-
this.viewer = null;
|
|
639
|
-
this.scene = null;
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
/** Type guard for DistancePanel */
|
|
644
|
-
function isDistancePanel(panel: DistancePanel | PropertiesPanel | null): panel is DistancePanel {
|
|
645
|
-
return panel instanceof DistancePanel;
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
/** Type guard for PropertiesPanel */
|
|
649
|
-
function isPropertiesPanel(panel: DistancePanel | PropertiesPanel | null): panel is PropertiesPanel {
|
|
650
|
-
return panel instanceof PropertiesPanel;
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
/** Type guard for DistanceResponseData */
|
|
654
|
-
function isDistanceResponseData(data: DistanceResponseData | PropertiesResponseData | null): data is DistanceResponseData {
|
|
655
|
-
return data !== null && "refpoint1" in data && "refpoint2" in data;
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
/** Type guard for PropertiesResponseData */
|
|
659
|
-
function isPropertiesResponseData(data: DistanceResponseData | PropertiesResponseData | null): data is PropertiesResponseData {
|
|
660
|
-
return data !== null && "refpoint" in data;
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
class DistanceMeasurement extends Measurement {
|
|
664
|
-
override debug: boolean;
|
|
665
|
-
|
|
666
|
-
constructor(viewer: ViewerLike, debug: boolean) {
|
|
667
|
-
super(viewer, new DistancePanel(viewer.display));
|
|
668
|
-
this.point1 = null;
|
|
669
|
-
this.point2 = null;
|
|
670
|
-
this.middlePoint = null;
|
|
671
|
-
this.debug = debug;
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
override _createPanel(): void {
|
|
675
|
-
if (isDistancePanel(this.panel) && isDistanceResponseData(this.responseData)) {
|
|
676
|
-
this.panel.createTable(this.responseData);
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
override _getMaxObjSelected(): number {
|
|
681
|
-
return 2;
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
_getPoints(): void {
|
|
685
|
-
if (isDistanceResponseData(this.responseData)) {
|
|
686
|
-
if (this.responseData.refpoint1) {
|
|
687
|
-
this.point1 = new THREE.Vector3(...this.responseData.refpoint1);
|
|
688
|
-
}
|
|
689
|
-
if (this.responseData.refpoint2) {
|
|
690
|
-
this.point2 = new THREE.Vector3(...this.responseData.refpoint2);
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
override _makeLines(): void {
|
|
696
|
-
if (!this.scene || this.scene.children.length !== 0) return;
|
|
697
|
-
if (!this.coneLength || !this.point1 || !this.point2 || !this.panelCenter) return;
|
|
698
|
-
|
|
699
|
-
const lineWidth = 1.5;
|
|
700
|
-
const distanceLine = new DistanceLineArrow(
|
|
701
|
-
this.coneLength,
|
|
702
|
-
this.point1,
|
|
703
|
-
this.point2,
|
|
704
|
-
lineWidth,
|
|
705
|
-
this.measurementLineColor,
|
|
706
|
-
);
|
|
707
|
-
this.scene.add(distanceLine);
|
|
708
|
-
|
|
709
|
-
this.middlePoint = new THREE.Vector3()
|
|
710
|
-
.addVectors(this.point1, this.point2)
|
|
711
|
-
.multiplyScalar(0.5);
|
|
712
|
-
const connectingLine = new DistanceLineArrow(
|
|
713
|
-
this.coneLength,
|
|
714
|
-
this.panelCenter,
|
|
715
|
-
this.middlePoint,
|
|
716
|
-
lineWidth,
|
|
717
|
-
this.connectingLineColor,
|
|
718
|
-
false,
|
|
719
|
-
false,
|
|
720
|
-
);
|
|
721
|
-
this.scene.add(connectingLine);
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
override _updateConnectionLine(): void {
|
|
725
|
-
if (!this.scene || !this.middlePoint || !this.panelCenter) return;
|
|
726
|
-
const lineArrow = this.scene.children[1];
|
|
727
|
-
if (!(lineArrow instanceof DistanceLineArrow)) return;
|
|
728
|
-
const line = lineArrow.children.find((ch) => isLineSegments2(ch));
|
|
729
|
-
if (line && isLineSegments2(line)) {
|
|
730
|
-
line.geometry.setPositions([
|
|
731
|
-
...this.middlePoint.toArray(),
|
|
732
|
-
...this.panelCenter.toArray(),
|
|
733
|
-
]);
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
/**
|
|
738
|
-
* Handle the response from the backend.
|
|
739
|
-
*/
|
|
740
|
-
override handleResponse(response: DistanceResponseData): void {
|
|
741
|
-
this.responseData = { ...response };
|
|
742
|
-
this._getPoints();
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
class PropertiesMeasurement extends Measurement {
|
|
747
|
-
override debug: boolean;
|
|
748
|
-
|
|
749
|
-
constructor(viewer: ViewerLike, debug: boolean) {
|
|
750
|
-
super(viewer, new PropertiesPanel(viewer.display));
|
|
751
|
-
this.middlePoint = null;
|
|
752
|
-
this.debug = debug;
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
override _createPanel(): void {
|
|
756
|
-
if (isPropertiesPanel(this.panel) && isPropertiesResponseData(this.responseData)) {
|
|
757
|
-
this.panel.createTable(this.responseData);
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
override _getMaxObjSelected(): number {
|
|
762
|
-
return 1;
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
_getPoint(): void {
|
|
766
|
-
if (isPropertiesResponseData(this.responseData) && this.responseData.refpoint) {
|
|
767
|
-
this.point1 = new THREE.Vector3(...this.responseData.refpoint);
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
override _makeLines(): void {
|
|
772
|
-
if (!this.scene || this.scene.children.length !== 0) return;
|
|
773
|
-
if (!this.coneLength || !this.panelCenter || !this.point1) return;
|
|
774
|
-
|
|
775
|
-
this.middlePoint = this.point1;
|
|
776
|
-
const lineWidth = 1.5;
|
|
777
|
-
const connectingLine = new DistanceLineArrow(
|
|
778
|
-
this.coneLength,
|
|
779
|
-
this.panelCenter,
|
|
780
|
-
this.middlePoint,
|
|
781
|
-
lineWidth,
|
|
782
|
-
this.connectingLineColor,
|
|
783
|
-
false,
|
|
784
|
-
false,
|
|
785
|
-
);
|
|
786
|
-
this.scene.add(connectingLine);
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
override _updateConnectionLine(): void {
|
|
790
|
-
if (!this.scene || !this.middlePoint || !this.panelCenter) return;
|
|
791
|
-
const lineArrow = this.scene.children[0];
|
|
792
|
-
if (!(lineArrow instanceof DistanceLineArrow)) return;
|
|
793
|
-
const line = lineArrow.children.find((ch) => isLineSegments2(ch));
|
|
794
|
-
if (line && isLineSegments2(line)) {
|
|
795
|
-
line.geometry.setPositions([
|
|
796
|
-
...this.middlePoint.toArray(),
|
|
797
|
-
...this.panelCenter.toArray(),
|
|
798
|
-
]);
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
/**
|
|
803
|
-
* Handle the response from the backend.
|
|
804
|
-
*/
|
|
805
|
-
override handleResponse(response: PropertiesResponseData): void {
|
|
806
|
-
this.responseData = { ...response };
|
|
807
|
-
this._getPoint();
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
export { DistanceMeasurement, PropertiesMeasurement };
|