@inweb/viewer-three 26.10.1 → 26.10.3
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/plugins/components/AxesHelperComponent.js +5 -9
- package/dist/plugins/components/AxesHelperComponent.js.map +1 -1
- package/dist/plugins/components/AxesHelperComponent.min.js +1 -1
- package/dist/plugins/components/AxesHelperComponent.module.js +5 -9
- package/dist/plugins/components/AxesHelperComponent.module.js.map +1 -1
- package/dist/plugins/components/GridHelperComponent.js +62 -0
- package/dist/plugins/components/GridHelperComponent.js.map +1 -0
- package/dist/plugins/components/GridHelperComponent.min.js +24 -0
- package/dist/plugins/components/GridHelperComponent.module.js +57 -0
- package/dist/plugins/components/GridHelperComponent.module.js.map +1 -0
- package/dist/plugins/loaders/IFCXLoader.js.map +1 -1
- package/dist/plugins/loaders/IFCXLoader.module.js.map +1 -1
- package/dist/viewer-three.js +163 -59
- package/dist/viewer-three.js.map +1 -1
- package/dist/viewer-three.min.js +3 -3
- package/dist/viewer-three.module.js +151 -56
- package/dist/viewer-three.module.js.map +1 -1
- package/lib/Viewer/components/CameraComponent.d.ts +5 -1
- package/lib/Viewer/draggers/CuttingPlaneDragger.d.ts +1 -0
- package/lib/Viewer/draggers/FlyDragger.d.ts +1 -0
- package/lib/Viewer/draggers/MeasureLineDragger.d.ts +1 -0
- package/lib/Viewer/draggers/OrbitDragger.d.ts +1 -0
- package/lib/Viewer/draggers/WalkDragger.d.ts +1 -0
- package/package.json +5 -5
- package/plugins/components/AxesHelperComponent.ts +6 -11
- package/plugins/components/GridHelperComponent.ts +67 -0
- package/plugins/loaders/IFCX/render.js +2 -2
- package/src/Viewer/Viewer.ts +4 -0
- package/src/Viewer/components/CameraComponent.ts +86 -25
- package/src/Viewer/components/SelectionComponent.ts +7 -1
- package/src/Viewer/controls/WalkControls.ts +1 -1
- package/src/Viewer/draggers/CuttingPlaneDragger.ts +12 -6
- package/src/Viewer/draggers/FlyDragger.ts +10 -4
- package/src/Viewer/draggers/MeasureLineDragger.ts +50 -17
- package/src/Viewer/draggers/OrbitDragger.ts +7 -1
- package/src/Viewer/draggers/WalkDragger.ts +10 -4
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import { IComponent } from "@inweb/viewer-core";
|
|
1
|
+
import { CameraMode, IComponent } from "@inweb/viewer-core";
|
|
2
2
|
import type { Viewer } from "../Viewer";
|
|
3
3
|
export declare class CameraComponent implements IComponent {
|
|
4
4
|
protected viewer: Viewer;
|
|
5
5
|
constructor(viewer: Viewer);
|
|
6
6
|
dispose(): void;
|
|
7
|
+
getCameraMode(camera: any): CameraMode;
|
|
8
|
+
switchCamera(camera: any): void;
|
|
9
|
+
switchCameraMode(mode: CameraMode): void;
|
|
10
|
+
optionsChange: () => void;
|
|
7
11
|
geometryEnd: () => void;
|
|
8
12
|
}
|
|
@@ -7,6 +7,7 @@ export declare class FlyDragger implements IDragger {
|
|
|
7
7
|
constructor(viewer: Viewer);
|
|
8
8
|
dispose(): void;
|
|
9
9
|
updateControls: () => void;
|
|
10
|
+
updateControlsCamera: () => void;
|
|
10
11
|
controlsChange: () => void;
|
|
11
12
|
flyspeedChange: (event: any) => void;
|
|
12
13
|
viewerRender: () => void;
|
|
@@ -9,6 +9,7 @@ export declare class OrbitDragger implements IDragger {
|
|
|
9
9
|
initialize(): void;
|
|
10
10
|
dispose(): void;
|
|
11
11
|
updateControls: () => void;
|
|
12
|
+
updateControlsCamera: () => void;
|
|
12
13
|
controlsStart: () => void;
|
|
13
14
|
controlsChange: () => void;
|
|
14
15
|
stopContextMenu: (event: PointerEvent) => void;
|
|
@@ -7,6 +7,7 @@ export declare class WalkDragger implements IDragger {
|
|
|
7
7
|
constructor(viewer: Viewer);
|
|
8
8
|
dispose(): void;
|
|
9
9
|
updateControls: () => void;
|
|
10
|
+
updateControlsCamera: () => void;
|
|
10
11
|
controlsChange: () => void;
|
|
11
12
|
walkspeedChange: (event: any) => void;
|
|
12
13
|
viewerRender: () => void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inweb/viewer-three",
|
|
3
|
-
"version": "26.10.
|
|
3
|
+
"version": "26.10.3",
|
|
4
4
|
"description": "JavaScript library for rendering CAD and BIM files in a browser using Three.js",
|
|
5
5
|
"homepage": "https://cloud.opendesign.com/docs/index.html",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -35,10 +35,10 @@
|
|
|
35
35
|
"docs": "typedoc"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@inweb/client": "~26.10.
|
|
39
|
-
"@inweb/eventemitter2": "~26.10.
|
|
40
|
-
"@inweb/markup": "~26.10.
|
|
41
|
-
"@inweb/viewer-core": "~26.10.
|
|
38
|
+
"@inweb/client": "~26.10.3",
|
|
39
|
+
"@inweb/eventemitter2": "~26.10.3",
|
|
40
|
+
"@inweb/markup": "~26.10.3",
|
|
41
|
+
"@inweb/viewer-core": "~26.10.3"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@types/three": "^0.179.0",
|
|
@@ -30,8 +30,8 @@ class AxesHelperComponent implements IComponent {
|
|
|
30
30
|
private axesHelper2: AxesHelper;
|
|
31
31
|
|
|
32
32
|
constructor(viewer: Viewer) {
|
|
33
|
-
this.axesHelper1 = new AxesHelper();
|
|
34
|
-
this.axesHelper2 = new AxesHelper();
|
|
33
|
+
this.axesHelper1 = new AxesHelper(1);
|
|
34
|
+
this.axesHelper2 = new AxesHelper(1);
|
|
35
35
|
|
|
36
36
|
this.viewer = viewer;
|
|
37
37
|
this.viewer.addEventListener("initialize", this.syncHelper);
|
|
@@ -53,23 +53,18 @@ class AxesHelperComponent implements IComponent {
|
|
|
53
53
|
|
|
54
54
|
syncHelper = () => {
|
|
55
55
|
this.axesHelper1.removeFromParent();
|
|
56
|
-
this.axesHelper1.dispose();
|
|
57
|
-
|
|
58
56
|
this.axesHelper2.removeFromParent();
|
|
59
|
-
this.axesHelper2.dispose();
|
|
60
57
|
|
|
61
58
|
const size = this.viewer.extents.getSize(new Vector3()).length();
|
|
62
59
|
const center = this.viewer.extents.getCenter(new Vector3());
|
|
63
60
|
|
|
64
|
-
this.axesHelper1 = new AxesHelper(size || 1);
|
|
65
|
-
this.axesHelper2 = new AxesHelper(size);
|
|
66
|
-
|
|
67
61
|
this.axesHelper1.position.set(0, 0, 0);
|
|
68
|
-
this.
|
|
69
|
-
|
|
70
|
-
if (this.viewer.extents.isEmpty()) return;
|
|
62
|
+
this.axesHelper1.scale.setScalar(size);
|
|
71
63
|
|
|
72
64
|
this.axesHelper2.position.copy(center);
|
|
65
|
+
this.axesHelper2.scale.setScalar(size);
|
|
66
|
+
|
|
67
|
+
this.viewer.helpers.add(this.axesHelper1);
|
|
73
68
|
this.viewer.helpers.add(this.axesHelper2);
|
|
74
69
|
};
|
|
75
70
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
// Copyright (C) 2002-2025, Open Design Alliance (the "Alliance").
|
|
3
|
+
// All rights reserved.
|
|
4
|
+
//
|
|
5
|
+
// This software and its documentation and related materials are owned by
|
|
6
|
+
// the Alliance. The software may only be incorporated into application
|
|
7
|
+
// programs owned by members of the Alliance, subject to a signed
|
|
8
|
+
// Membership Agreement and Supplemental Software License Agreement with the
|
|
9
|
+
// Alliance. The structure and organization of this software are the valuable
|
|
10
|
+
// trade secrets of the Alliance and its suppliers. The software is also
|
|
11
|
+
// protected by copyright law and international treaty provisions. Application
|
|
12
|
+
// programs incorporating this software must include the following statement
|
|
13
|
+
// with their copyright notices:
|
|
14
|
+
//
|
|
15
|
+
// This application incorporates Open Design Alliance software pursuant to a
|
|
16
|
+
// license agreement with Open Design Alliance.
|
|
17
|
+
// Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance.
|
|
18
|
+
// All rights reserved.
|
|
19
|
+
//
|
|
20
|
+
// By use of this software, its documentation or related materials, you
|
|
21
|
+
// acknowledge and accept the above terms.
|
|
22
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
23
|
+
|
|
24
|
+
import { GridHelper, Vector3 } from "three";
|
|
25
|
+
import { IComponent, components, Viewer } from "@inweb/viewer-three";
|
|
26
|
+
|
|
27
|
+
class GridHelperComponent implements IComponent {
|
|
28
|
+
private viewer: Viewer;
|
|
29
|
+
private gridHelper: GridHelper;
|
|
30
|
+
|
|
31
|
+
constructor(viewer: Viewer) {
|
|
32
|
+
this.gridHelper = new GridHelper(10, 20, 0x444444, 0xaaaaaa);
|
|
33
|
+
this.viewer = viewer;
|
|
34
|
+
this.viewer.on("initialize", this.syncHelper);
|
|
35
|
+
this.viewer.on("geometryend", this.syncHelper);
|
|
36
|
+
this.viewer.on("clear", this.syncHelper);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
dispose() {
|
|
40
|
+
this.gridHelper.removeFromParent();
|
|
41
|
+
this.gridHelper.dispose();
|
|
42
|
+
|
|
43
|
+
this.viewer.off("initialize", this.syncHelper);
|
|
44
|
+
this.viewer.off("geometryend", this.syncHelper);
|
|
45
|
+
this.viewer.off("clear", this.syncHelper);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
syncHelper = () => {
|
|
49
|
+
this.gridHelper.removeFromParent();
|
|
50
|
+
|
|
51
|
+
if (this.viewer.extents.isEmpty()) return;
|
|
52
|
+
|
|
53
|
+
const size = this.viewer.extents.getSize(new Vector3()).multiply(this.viewer.camera.up).length();
|
|
54
|
+
const center = this.viewer.extents.getCenter(new Vector3());
|
|
55
|
+
|
|
56
|
+
const upY = new Vector3(0, 1, 0);
|
|
57
|
+
const up = new Vector3().copy(this.viewer.camera.up);
|
|
58
|
+
|
|
59
|
+
this.gridHelper.scale.setScalar(size);
|
|
60
|
+
this.gridHelper.position.copy(center);
|
|
61
|
+
this.gridHelper.quaternion.setFromUnitVectors(upY, up);
|
|
62
|
+
|
|
63
|
+
this.viewer.helpers.add(this.gridHelper);
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
components.registerComponent("GridHelperComponent", (viewer) => new GridHelperComponent(viewer));
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
|
|
24
24
|
// Repository: https://github.com/buildingSMART/IFC5-development
|
|
25
25
|
// Original File: docs/viewer/render.mjs
|
|
26
|
-
// Commit SHA-1:
|
|
27
|
-
// Commit Date:
|
|
26
|
+
// Commit SHA-1: 7a2b39e56324d892b78b476944ec0e557d50b236
|
|
27
|
+
// Commit Date: 30.07.2025 08:20:14
|
|
28
28
|
|
|
29
29
|
// (C) buildingSMART International
|
|
30
30
|
// published under MIT license
|
package/src/Viewer/Viewer.ts
CHANGED
|
@@ -698,6 +698,8 @@ export class Viewer
|
|
|
698
698
|
this.renderPass.camera = camera;
|
|
699
699
|
this.helpersPass.camera = camera;
|
|
700
700
|
this.ssaaRenderPass.camera = camera;
|
|
701
|
+
|
|
702
|
+
this.emitEvent({ type: "changecameramode", mode: "orthographic" });
|
|
701
703
|
}
|
|
702
704
|
};
|
|
703
705
|
|
|
@@ -723,6 +725,8 @@ export class Viewer
|
|
|
723
725
|
this.renderPass.camera = camera;
|
|
724
726
|
this.helpersPass.camera = camera;
|
|
725
727
|
this.ssaaRenderPass.camera = camera;
|
|
728
|
+
|
|
729
|
+
this.emitEvent({ type: "changecameramode", mode: "perspective" });
|
|
726
730
|
}
|
|
727
731
|
};
|
|
728
732
|
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
// acknowledge and accept the above terms.
|
|
22
22
|
///////////////////////////////////////////////////////////////////////////////
|
|
23
23
|
|
|
24
|
-
import { Sphere, Vector2 } from "three";
|
|
24
|
+
import { MathUtils, OrthographicCamera, PerspectiveCamera, Sphere, Vector2, Vector3 } from "three";
|
|
25
25
|
|
|
26
|
-
import { IComponent } from "@inweb/viewer-core";
|
|
26
|
+
import { CameraMode, IComponent } from "@inweb/viewer-core";
|
|
27
27
|
import type { Viewer } from "../Viewer";
|
|
28
28
|
|
|
29
29
|
export class CameraComponent implements IComponent {
|
|
@@ -32,17 +32,93 @@ export class CameraComponent implements IComponent {
|
|
|
32
32
|
constructor(viewer: Viewer) {
|
|
33
33
|
this.viewer = viewer;
|
|
34
34
|
this.viewer.addEventListener("databasechunk", this.geometryEnd);
|
|
35
|
+
this.viewer.addEventListener("optionschange", this.optionsChange);
|
|
36
|
+
this.viewer.addEventListener("initialize", this.optionsChange);
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
dispose() {
|
|
38
40
|
this.viewer.removeEventListener("databasechunk", this.geometryEnd);
|
|
41
|
+
this.viewer.removeEventListener("optionschange", this.optionsChange);
|
|
42
|
+
this.viewer.removeEventListener("initialize", this.optionsChange);
|
|
39
43
|
}
|
|
40
44
|
|
|
41
|
-
|
|
42
|
-
|
|
45
|
+
getCameraMode(camera: any): CameraMode {
|
|
46
|
+
return camera.isOrthographicCamera ? "orthographic" : "perspective";
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
switchCamera(camera: any) {
|
|
50
|
+
const extentsSize = this.viewer.extents.getBoundingSphere(new Sphere()).radius * 2 || 1;
|
|
43
51
|
const rendererSize = this.viewer.renderer.getSize(new Vector2());
|
|
44
52
|
const aspect = rendererSize.x / rendererSize.y;
|
|
45
53
|
|
|
54
|
+
if (camera.isPerspectiveCamera) {
|
|
55
|
+
camera.aspect = aspect;
|
|
56
|
+
camera.near = extentsSize / 1000;
|
|
57
|
+
camera.far = extentsSize * 1000;
|
|
58
|
+
}
|
|
59
|
+
if (camera.isOrthographicCamera) {
|
|
60
|
+
camera.left = camera.bottom * aspect;
|
|
61
|
+
camera.right = camera.top * aspect;
|
|
62
|
+
camera.near = 0;
|
|
63
|
+
camera.far = extentsSize * 1000;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
camera.updateProjectionMatrix();
|
|
67
|
+
|
|
68
|
+
this.viewer.camera = camera;
|
|
69
|
+
this.viewer.renderPass.camera = camera;
|
|
70
|
+
this.viewer.helpersPass.camera = camera;
|
|
71
|
+
this.viewer.ssaaRenderPass.camera = camera;
|
|
72
|
+
|
|
73
|
+
this.viewer.update();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
switchCameraMode(mode: CameraMode) {
|
|
77
|
+
if (!mode) return;
|
|
78
|
+
|
|
79
|
+
const currentCamera: any = this.viewer.camera;
|
|
80
|
+
if (mode === this.getCameraMode(currentCamera)) return;
|
|
81
|
+
|
|
82
|
+
const target = this.viewer.target.clone();
|
|
83
|
+
|
|
84
|
+
let camera: PerspectiveCamera | OrthographicCamera;
|
|
85
|
+
|
|
86
|
+
if (currentCamera.isOrthographicCamera) {
|
|
87
|
+
const fov = currentCamera.userData.fov || 45;
|
|
88
|
+
const fieldHeight = (currentCamera.top - currentCamera.bottom) / currentCamera.zoom;
|
|
89
|
+
const distance = fieldHeight / (2 * Math.tan(MathUtils.degToRad(fov) / 2));
|
|
90
|
+
const direction = new Vector3().subVectors(currentCamera.position, target).normalize();
|
|
91
|
+
|
|
92
|
+
camera = new PerspectiveCamera(fov);
|
|
93
|
+
camera.position.copy(direction).multiplyScalar(distance).add(target);
|
|
94
|
+
}
|
|
95
|
+
if (currentCamera.isPerspectiveCamera) {
|
|
96
|
+
const fov = currentCamera.fov;
|
|
97
|
+
const distance = currentCamera.position.distanceTo(target);
|
|
98
|
+
const fieldHeight = 2 * Math.tan(MathUtils.degToRad(fov) / 2) * distance;
|
|
99
|
+
|
|
100
|
+
camera = new OrthographicCamera();
|
|
101
|
+
camera.top = fieldHeight / 2;
|
|
102
|
+
camera.bottom = -fieldHeight / 2;
|
|
103
|
+
camera.position.copy(currentCamera.position);
|
|
104
|
+
|
|
105
|
+
camera.userData.fov = fov;
|
|
106
|
+
}
|
|
107
|
+
if (!camera) return;
|
|
108
|
+
|
|
109
|
+
camera.up.copy(currentCamera.up);
|
|
110
|
+
camera.quaternion.copy(currentCamera.quaternion);
|
|
111
|
+
|
|
112
|
+
this.switchCamera(camera);
|
|
113
|
+
|
|
114
|
+
this.viewer.emitEvent({ type: "changecameramode", mode });
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
optionsChange = () => {
|
|
118
|
+
this.switchCameraMode(this.viewer.options.cameraMode);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
geometryEnd = () => {
|
|
46
122
|
let camera: any;
|
|
47
123
|
|
|
48
124
|
// TODO: do not change the camera and target after opening the second model in "append" mode
|
|
@@ -56,29 +132,14 @@ export class CameraComponent implements IComponent {
|
|
|
56
132
|
camera.isDefaultCamera = true;
|
|
57
133
|
camera.scale.set(1, 1, 1); // <- Visualize fix
|
|
58
134
|
|
|
59
|
-
this.
|
|
60
|
-
this.viewer.renderPass.camera = camera;
|
|
61
|
-
this.viewer.helpersPass.camera = camera;
|
|
62
|
-
this.viewer.ssaaRenderPass.camera = camera;
|
|
63
|
-
} else {
|
|
64
|
-
camera = this.viewer.camera;
|
|
65
|
-
}
|
|
135
|
+
this.switchCamera(camera);
|
|
66
136
|
|
|
67
|
-
|
|
68
|
-
camera.aspect = aspect;
|
|
69
|
-
camera.near = extentsSize / 1000;
|
|
70
|
-
camera.far = extentsSize * 1000;
|
|
71
|
-
camera.updateProjectionMatrix();
|
|
72
|
-
}
|
|
73
|
-
if (camera.isOrthographicCamera) {
|
|
74
|
-
camera.left = camera.bottom * aspect;
|
|
75
|
-
camera.right = camera.top * aspect;
|
|
76
|
-
camera.near = 0;
|
|
77
|
-
camera.far = extentsSize * 1000;
|
|
78
|
-
camera.updateProjectionMatrix();
|
|
79
|
-
}
|
|
137
|
+
const mode = this.getCameraMode(camera);
|
|
80
138
|
|
|
81
|
-
|
|
139
|
+
this.viewer.options.cameraMode = mode;
|
|
140
|
+
this.viewer.emitEvent({ type: "changecameramode", mode });
|
|
141
|
+
} else {
|
|
142
|
+
this.switchCamera(this.viewer.camera);
|
|
82
143
|
this.viewer.executeCommand("setDefaultViewPosition");
|
|
83
144
|
}
|
|
84
145
|
};
|
|
@@ -114,7 +114,13 @@ export class SelectionComponent implements IComponent {
|
|
|
114
114
|
Sprite: {},
|
|
115
115
|
};
|
|
116
116
|
|
|
117
|
-
|
|
117
|
+
let intersects = this.raycaster.intersectObjects(objects, false);
|
|
118
|
+
|
|
119
|
+
(this.viewer.renderer.clippingPlanes || []).forEach((plane) => {
|
|
120
|
+
intersects = intersects.filter((intersect) => plane.distanceToPoint(intersect.point) >= 0);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
return intersects;
|
|
118
124
|
}
|
|
119
125
|
|
|
120
126
|
select(objects: Object3D | Object3D[], model?: IModelImpl) {
|
|
@@ -177,7 +177,7 @@ export class WalkControls extends Controls<WalkControlsEventMap> {
|
|
|
177
177
|
Sprite: { threshold: 0 },
|
|
178
178
|
};
|
|
179
179
|
|
|
180
|
-
|
|
180
|
+
const intersects = this.raycaster.intersectObjects(this.groundObjects, false);
|
|
181
181
|
if (intersects.length > 0) {
|
|
182
182
|
const groundY = intersects[0].point.y;
|
|
183
183
|
const targetY = groundY + this.EYE_HEIGHT;
|
|
@@ -62,17 +62,19 @@ export class CuttingPlaneDragger extends OrbitDragger {
|
|
|
62
62
|
this.transform.addEventListener("dragging-changed", this.transformDrag);
|
|
63
63
|
this.viewer.helpers.add(this.transform.getHelper());
|
|
64
64
|
|
|
65
|
-
this.viewer.
|
|
66
|
-
this.viewer.
|
|
67
|
-
this.viewer.
|
|
65
|
+
this.viewer.addEventListener("explode", this.updatePlaneSize);
|
|
66
|
+
this.viewer.addEventListener("show", this.updatePlaneSize);
|
|
67
|
+
this.viewer.addEventListener("showall", this.updatePlaneSize);
|
|
68
|
+
this.viewer.addEventListener("changecameramode", this.updateTransformCamera);
|
|
68
69
|
this.viewer.canvas.addEventListener("dblclick", this.onDoubleClick, true);
|
|
69
70
|
this.viewer.update();
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
override dispose() {
|
|
73
|
-
this.viewer.
|
|
74
|
-
this.viewer.
|
|
75
|
-
this.viewer.
|
|
74
|
+
this.viewer.removeEventListener("explode", this.updatePlaneSize);
|
|
75
|
+
this.viewer.removeEventListener("show", this.updatePlaneSize);
|
|
76
|
+
this.viewer.removeEventListener("showall", this.updatePlaneSize);
|
|
77
|
+
this.viewer.removeEventListener("changecameramode", this.updateTransformCamera);
|
|
76
78
|
this.viewer.canvas.removeEventListener("dblclick", this.onDoubleClick, true);
|
|
77
79
|
|
|
78
80
|
this.transform.removeEventListener("change", this.transformChange);
|
|
@@ -106,6 +108,10 @@ export class CuttingPlaneDragger extends OrbitDragger {
|
|
|
106
108
|
this.viewer.update();
|
|
107
109
|
};
|
|
108
110
|
|
|
111
|
+
updateTransformCamera = () => {
|
|
112
|
+
this.transform.camera = this.viewer.camera;
|
|
113
|
+
};
|
|
114
|
+
|
|
109
115
|
onDoubleClick = (event: PointerEvent) => {
|
|
110
116
|
event.stopPropagation();
|
|
111
117
|
|
|
@@ -36,14 +36,16 @@ export class FlyDragger implements IDragger {
|
|
|
36
36
|
this.controls.addEventListener("change", this.controlsChange);
|
|
37
37
|
this.controls.addEventListener("flyspeedchange", this.flyspeedChange);
|
|
38
38
|
this.viewer = viewer;
|
|
39
|
-
this.viewer.
|
|
40
|
-
this.viewer.
|
|
39
|
+
this.viewer.addEventListener("render", this.viewerRender);
|
|
40
|
+
this.viewer.addEventListener("zoom", this.viewerZoom);
|
|
41
|
+
this.viewer.addEventListener("changecameramode", this.updateControlsCamera);
|
|
41
42
|
this.updateControls();
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
dispose() {
|
|
45
|
-
this.viewer.
|
|
46
|
-
this.viewer.
|
|
46
|
+
this.viewer.removeEventListener("render", this.viewerRender);
|
|
47
|
+
this.viewer.removeEventListener("zoom", this.viewerZoom);
|
|
48
|
+
this.viewer.removeEventListener("changecameramode", this.updateControlsCamera);
|
|
47
49
|
this.controls.removeEventListener("flyspeedchange", this.flyspeedChange);
|
|
48
50
|
this.controls.removeEventListener("change", this.controlsChange);
|
|
49
51
|
this.controls.dispose();
|
|
@@ -54,6 +56,10 @@ export class FlyDragger implements IDragger {
|
|
|
54
56
|
this.controls.movementSpeed = Math.min(size.x, size.y, size.z) / 2;
|
|
55
57
|
};
|
|
56
58
|
|
|
59
|
+
updateControlsCamera = () => {
|
|
60
|
+
this.controls.object = this.viewer.camera;
|
|
61
|
+
};
|
|
62
|
+
|
|
57
63
|
controlsChange = () => {
|
|
58
64
|
this.viewer.update();
|
|
59
65
|
this.viewer.emitEvent({ type: "changecamera" });
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
MathUtils,
|
|
30
30
|
Matrix4,
|
|
31
31
|
Object3D,
|
|
32
|
+
Plane,
|
|
32
33
|
Raycaster,
|
|
33
34
|
Vector2,
|
|
34
35
|
Vector3,
|
|
@@ -70,6 +71,7 @@ export class MeasureLineDragger extends OrbitDragger {
|
|
|
70
71
|
this.viewer.addEventListener("isolate", this.updateSnapper);
|
|
71
72
|
this.viewer.addEventListener("show", this.updateSnapper);
|
|
72
73
|
this.viewer.addEventListener("showall", this.updateSnapper);
|
|
74
|
+
this.viewer.addEventListener("changecameramode", this.updateSnapperCamera);
|
|
73
75
|
}
|
|
74
76
|
|
|
75
77
|
override dispose() {
|
|
@@ -84,6 +86,7 @@ export class MeasureLineDragger extends OrbitDragger {
|
|
|
84
86
|
this.viewer.removeEventListener("isolate", this.updateSnapper);
|
|
85
87
|
this.viewer.removeEventListener("show", this.updateSnapper);
|
|
86
88
|
this.viewer.removeEventListener("showall", this.updateSnapper);
|
|
89
|
+
this.viewer.removeEventListener("changecameramode", this.updateSnapperCamera);
|
|
87
90
|
|
|
88
91
|
this.snapper.dispose();
|
|
89
92
|
|
|
@@ -143,7 +146,12 @@ export class MeasureLineDragger extends OrbitDragger {
|
|
|
143
146
|
};
|
|
144
147
|
|
|
145
148
|
updateSnapper = () => {
|
|
146
|
-
this.snapper.
|
|
149
|
+
this.snapper.setFromViewer(this.viewer);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
updateSnapperCamera = () => {
|
|
153
|
+
this.snapper.camera = this.viewer.camera;
|
|
154
|
+
this.overlay.camera = this.viewer.camera;
|
|
147
155
|
};
|
|
148
156
|
}
|
|
149
157
|
|
|
@@ -155,9 +163,10 @@ const _center = new Vector3();
|
|
|
155
163
|
const _projection = new Vector3();
|
|
156
164
|
|
|
157
165
|
class MeasureSnapper {
|
|
158
|
-
|
|
166
|
+
public camera: Camera;
|
|
159
167
|
private canvas: HTMLCanvasElement;
|
|
160
168
|
private objects: Object3D[];
|
|
169
|
+
private clippingPlanes: Plane[];
|
|
161
170
|
private raycaster: Raycaster;
|
|
162
171
|
private detectRadiusInPixels: number;
|
|
163
172
|
private edgesCache: WeakMap<any, EdgesGeometry>;
|
|
@@ -166,6 +175,7 @@ class MeasureSnapper {
|
|
|
166
175
|
this.camera = camera;
|
|
167
176
|
this.canvas = canvas;
|
|
168
177
|
this.objects = [];
|
|
178
|
+
this.clippingPlanes = [];
|
|
169
179
|
this.raycaster = new Raycaster();
|
|
170
180
|
this.detectRadiusInPixels = this.isMobile() ? MOBILE_SNAP_DISTANCE : DESKTOP_SNAP_DISTANCE;
|
|
171
181
|
this.edgesCache = new WeakMap();
|
|
@@ -193,7 +203,7 @@ class MeasureSnapper {
|
|
|
193
203
|
return target.set(event.clientX, event.clientY);
|
|
194
204
|
}
|
|
195
205
|
|
|
196
|
-
getPointerIntersects(mouse: Vector2
|
|
206
|
+
getPointerIntersects(mouse: Vector2): Array<Intersection<Object3D>> {
|
|
197
207
|
const rect = this.canvas.getBoundingClientRect();
|
|
198
208
|
const x = ((mouse.x - rect.left) / rect.width) * 2 - 1;
|
|
199
209
|
const y = (-(mouse.y - rect.top) / rect.height) * 2 + 1;
|
|
@@ -210,7 +220,13 @@ class MeasureSnapper {
|
|
|
210
220
|
Sprite: {},
|
|
211
221
|
};
|
|
212
222
|
|
|
213
|
-
|
|
223
|
+
let intersects = this.raycaster.intersectObjects(this.objects, false);
|
|
224
|
+
|
|
225
|
+
this.clippingPlanes.forEach((plane) => {
|
|
226
|
+
intersects = intersects.filter((intersect) => plane.distanceToPoint(intersect.point) >= 0);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
return intersects;
|
|
214
230
|
}
|
|
215
231
|
|
|
216
232
|
getDetectRadius(point: Vector3): number {
|
|
@@ -224,20 +240,20 @@ class MeasureSnapper {
|
|
|
224
240
|
// Notes: Originally AI-generated, modified manually
|
|
225
241
|
|
|
226
242
|
if (camera.isOrthographicCamera) {
|
|
227
|
-
const
|
|
243
|
+
const fieldHeight = (camera.top - camera.bottom) / camera.zoom;
|
|
228
244
|
|
|
229
245
|
const canvasHeight = this.canvas.height;
|
|
230
|
-
const worldUnitsPerPixel =
|
|
246
|
+
const worldUnitsPerPixel = fieldHeight / canvasHeight;
|
|
231
247
|
|
|
232
248
|
return this.detectRadiusInPixels * worldUnitsPerPixel;
|
|
233
249
|
}
|
|
234
250
|
|
|
235
251
|
if (camera.isPerspectiveCamera) {
|
|
236
252
|
const distance = camera.position.distanceTo(point);
|
|
237
|
-
const
|
|
253
|
+
const fieldHeight = 2 * Math.tan(MathUtils.degToRad(camera.fov * 0.5)) * distance;
|
|
238
254
|
|
|
239
255
|
const canvasHeight = this.canvas.height;
|
|
240
|
-
const worldUnitsPerPixel =
|
|
256
|
+
const worldUnitsPerPixel = fieldHeight / canvasHeight;
|
|
241
257
|
|
|
242
258
|
return this.detectRadiusInPixels * worldUnitsPerPixel;
|
|
243
259
|
}
|
|
@@ -250,7 +266,7 @@ class MeasureSnapper {
|
|
|
250
266
|
getSnapPoint(event: PointerEvent): Vector3 {
|
|
251
267
|
const mouse = this.getMousePosition(event, new Vector2());
|
|
252
268
|
|
|
253
|
-
const intersections = this.getPointerIntersects(mouse
|
|
269
|
+
const intersections = this.getPointerIntersects(mouse);
|
|
254
270
|
if (intersections.length === 0) return undefined;
|
|
255
271
|
|
|
256
272
|
// ===================== AI-CODE-START ======================
|
|
@@ -314,11 +330,14 @@ class MeasureSnapper {
|
|
|
314
330
|
return intersectionPoint.clone();
|
|
315
331
|
}
|
|
316
332
|
|
|
317
|
-
|
|
333
|
+
setFromViewer(viewer: Viewer) {
|
|
318
334
|
this.objects.length = 0;
|
|
319
335
|
viewer.models.forEach((model) => {
|
|
320
336
|
model.getVisibleObjects().forEach((object) => this.objects.push(object));
|
|
321
337
|
});
|
|
338
|
+
|
|
339
|
+
this.camera = viewer.camera;
|
|
340
|
+
this.clippingPlanes = viewer.renderer.clippingPlanes || [];
|
|
322
341
|
}
|
|
323
342
|
}
|
|
324
343
|
|
|
@@ -328,22 +347,19 @@ class MeasureOverlay {
|
|
|
328
347
|
public container: HTMLElement;
|
|
329
348
|
public lines: MeasureLine[] = [];
|
|
330
349
|
public projector: MeasureProjector;
|
|
350
|
+
private resizeObserver: ResizeObserver;
|
|
331
351
|
|
|
332
352
|
constructor(camera: Camera, canvas: HTMLCanvasElement) {
|
|
333
353
|
this.camera = camera;
|
|
334
354
|
this.canvas = canvas;
|
|
335
355
|
this.projector = new MeasureProjector(camera, canvas);
|
|
356
|
+
this.resizeObserver = new ResizeObserver(this.resizeContainer);
|
|
336
357
|
}
|
|
337
358
|
|
|
338
359
|
attach() {
|
|
339
360
|
this.container = document.createElement("div");
|
|
340
361
|
this.container.id = "measure-container";
|
|
341
|
-
this.container.style.background = "rgba(0,0,0,0)";
|
|
342
362
|
this.container.style.position = "absolute";
|
|
343
|
-
this.container.style.top = "0px";
|
|
344
|
-
this.container.style.left = "0px";
|
|
345
|
-
this.container.style.width = "100%";
|
|
346
|
-
this.container.style.height = "100%";
|
|
347
363
|
this.container.style.outline = "none";
|
|
348
364
|
this.container.style.pointerEvents = "none";
|
|
349
365
|
this.container.style.overflow = "hidden";
|
|
@@ -351,6 +367,7 @@ class MeasureOverlay {
|
|
|
351
367
|
if (!this.canvas.parentElement) return;
|
|
352
368
|
|
|
353
369
|
this.canvas.parentElement.appendChild(this.container);
|
|
370
|
+
this.resizeObserver.observe(this.canvas.parentElement);
|
|
354
371
|
}
|
|
355
372
|
|
|
356
373
|
dispose() {
|
|
@@ -358,6 +375,8 @@ class MeasureOverlay {
|
|
|
358
375
|
}
|
|
359
376
|
|
|
360
377
|
detach() {
|
|
378
|
+
this.resizeObserver.disconnect();
|
|
379
|
+
|
|
361
380
|
this.container.remove();
|
|
362
381
|
this.container = undefined;
|
|
363
382
|
}
|
|
@@ -368,7 +387,7 @@ class MeasureOverlay {
|
|
|
368
387
|
}
|
|
369
388
|
|
|
370
389
|
render() {
|
|
371
|
-
this.projector.
|
|
390
|
+
this.projector.setFromCamera(this.camera);
|
|
372
391
|
this.lines.forEach((line) => line.render());
|
|
373
392
|
}
|
|
374
393
|
|
|
@@ -383,6 +402,15 @@ class MeasureOverlay {
|
|
|
383
402
|
removeLine(line: MeasureLine) {
|
|
384
403
|
this.lines = this.lines.filter((x) => x !== line);
|
|
385
404
|
}
|
|
405
|
+
|
|
406
|
+
resizeContainer = (entries: ResizeObserverEntry[]) => {
|
|
407
|
+
const { width, height } = entries[0].contentRect;
|
|
408
|
+
|
|
409
|
+
if (!width || !height) return; // <- invisible canvas, or canvas with parent removed
|
|
410
|
+
|
|
411
|
+
this.container.style.width = `${width}px`;
|
|
412
|
+
this.container.style.height = `${height}px`;
|
|
413
|
+
};
|
|
386
414
|
}
|
|
387
415
|
|
|
388
416
|
const _middlePoint = new Vector3();
|
|
@@ -550,7 +578,7 @@ const point1 = new Vector2();
|
|
|
550
578
|
const point2 = new Vector2();
|
|
551
579
|
|
|
552
580
|
class MeasureProjector {
|
|
553
|
-
|
|
581
|
+
public camera: Camera;
|
|
554
582
|
private canvas: HTMLElement;
|
|
555
583
|
|
|
556
584
|
constructor(camera: Camera, canvas: HTMLCanvasElement) {
|
|
@@ -558,6 +586,11 @@ class MeasureProjector {
|
|
|
558
586
|
this.canvas = canvas;
|
|
559
587
|
}
|
|
560
588
|
|
|
589
|
+
setFromCamera(camera: Camera) {
|
|
590
|
+
this.camera = camera;
|
|
591
|
+
this.updateProjectionMatrix();
|
|
592
|
+
}
|
|
593
|
+
|
|
561
594
|
updateProjectionMatrix() {
|
|
562
595
|
const rect = this.canvas.getBoundingClientRect();
|
|
563
596
|
_widthHalf = rect.width / 2;
|