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