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.
Files changed (59) hide show
  1. package/dist/scene/clipping.d.ts +6 -0
  2. package/dist/three-cad-viewer.esm.js +20 -5
  3. package/dist/three-cad-viewer.esm.js.map +1 -1
  4. package/dist/three-cad-viewer.esm.min.js +1 -1
  5. package/dist/three-cad-viewer.js +20 -5
  6. package/dist/three-cad-viewer.min.js +1 -1
  7. package/package.json +2 -3
  8. package/src/_version.ts +0 -1
  9. package/src/camera/camera.ts +0 -445
  10. package/src/camera/controls/CADOrbitControls.ts +0 -241
  11. package/src/camera/controls/CADTrackballControls.ts +0 -598
  12. package/src/camera/controls.ts +0 -380
  13. package/src/core/patches.ts +0 -16
  14. package/src/core/studio-manager.ts +0 -652
  15. package/src/core/types.ts +0 -892
  16. package/src/core/viewer-state.ts +0 -784
  17. package/src/core/viewer.ts +0 -4821
  18. package/src/index.ts +0 -151
  19. package/src/rendering/environment.ts +0 -840
  20. package/src/rendering/light-detection.ts +0 -327
  21. package/src/rendering/material-factory.ts +0 -735
  22. package/src/rendering/material-presets.ts +0 -289
  23. package/src/rendering/raycast.ts +0 -291
  24. package/src/rendering/room-environment.ts +0 -192
  25. package/src/rendering/studio-composer.ts +0 -577
  26. package/src/rendering/studio-floor.ts +0 -108
  27. package/src/rendering/texture-cache.ts +0 -324
  28. package/src/rendering/tree-model.ts +0 -542
  29. package/src/rendering/triplanar.ts +0 -329
  30. package/src/scene/animation.ts +0 -343
  31. package/src/scene/axes.ts +0 -108
  32. package/src/scene/bbox.ts +0 -223
  33. package/src/scene/clipping.ts +0 -640
  34. package/src/scene/grid.ts +0 -864
  35. package/src/scene/nestedgroup.ts +0 -1444
  36. package/src/scene/objectgroup.ts +0 -866
  37. package/src/scene/orientation.ts +0 -259
  38. package/src/scene/render-shape.ts +0 -634
  39. package/src/tools/cad_tools/measure.ts +0 -811
  40. package/src/tools/cad_tools/select.ts +0 -100
  41. package/src/tools/cad_tools/tools.ts +0 -231
  42. package/src/tools/cad_tools/ui.ts +0 -454
  43. package/src/tools/cad_tools/zebra.ts +0 -369
  44. package/src/types/html.d.ts +0 -5
  45. package/src/types/n8ao.d.ts +0 -28
  46. package/src/types/three-augmentation.d.ts +0 -60
  47. package/src/ui/display.ts +0 -3295
  48. package/src/ui/index.html +0 -505
  49. package/src/ui/info.ts +0 -177
  50. package/src/ui/slider.ts +0 -206
  51. package/src/ui/toolbar.ts +0 -347
  52. package/src/ui/treeview.ts +0 -945
  53. package/src/utils/decode-instances.ts +0 -233
  54. package/src/utils/font.ts +0 -60
  55. package/src/utils/gpu-tracker.ts +0 -265
  56. package/src/utils/logger.ts +0 -92
  57. package/src/utils/sizeof.ts +0 -116
  58. package/src/utils/timer.ts +0 -69
  59. 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 };