@inweb/viewer-three 26.11.0 → 26.11.1
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/README.md +4 -7
- package/dist/plugins/components/AxesHelperComponent.js.map +1 -0
- package/dist/plugins/components/AxesHelperComponent.module.js.map +1 -0
- package/dist/plugins/components/ExtentsHelperComponent.js.map +1 -0
- package/dist/plugins/components/ExtentsHelperComponent.module.js.map +1 -0
- package/dist/plugins/components/GridHelperComponent.js.map +1 -0
- package/dist/plugins/components/GridHelperComponent.module.js.map +1 -0
- package/dist/plugins/components/LightHelperComponent.js.map +1 -0
- package/dist/plugins/components/LightHelperComponent.module.js.map +1 -0
- package/dist/plugins/components/RoomEnvironmentComponent.js.map +1 -0
- package/dist/plugins/components/RoomEnvironmentComponent.module.js.map +1 -0
- package/dist/plugins/components/StatsPanelComponent.js.map +1 -0
- package/dist/plugins/components/StatsPanelComponent.module.js.map +1 -0
- package/dist/plugins/loaders/GLTFCloudLoader.js.map +1 -0
- package/dist/plugins/loaders/GLTFCloudLoader.module.js.map +1 -0
- package/dist/{extensions → plugins}/loaders/GLTFFileLoader.js +1 -1
- package/dist/plugins/loaders/GLTFFileLoader.js.map +1 -0
- package/dist/plugins/loaders/GLTFFileLoader.min.js +24 -0
- package/dist/plugins/loaders/GLTFFileLoader.module.js.map +1 -0
- package/dist/plugins/loaders/IFCXLoader.js.map +1 -0
- package/dist/plugins/loaders/IFCXLoader.module.js.map +1 -0
- package/dist/plugins/loaders/PotreeLoader.js.map +1 -0
- package/dist/plugins/loaders/PotreeLoader.module.js.map +1 -0
- package/dist/viewer-three.js +153 -249
- package/dist/viewer-three.js.map +1 -1
- package/dist/viewer-three.min.js +3 -3
- package/dist/viewer-three.module.js +155 -243
- package/dist/viewer-three.module.js.map +1 -1
- package/lib/Viewer/Viewer.d.ts +1 -1
- package/lib/Viewer/components/SelectionComponent.d.ts +3 -1
- package/lib/Viewer/draggers/MeasureLineDragger.d.ts +1 -7
- package/lib/Viewer/models/IModelImpl.d.ts +0 -4
- package/lib/Viewer/models/ModelImpl.d.ts +0 -4
- package/package.json +11 -11
- package/src/Viewer/Viewer.ts +1 -1
- package/src/Viewer/components/SelectionComponent.ts +30 -5
- package/src/Viewer/draggers/MeasureLineDragger.ts +226 -84
- package/src/Viewer/loaders/DynamicGltfLoader/DynamicGltfLoader.js +16 -14
- package/src/Viewer/loaders/DynamicGltfLoader/GltfStructure.js +0 -1
- package/src/Viewer/models/IModelImpl.ts +0 -8
- package/src/Viewer/models/ModelImpl.ts +0 -18
- package/src/index-umd.ts +1 -1
- package/dist/extensions/components/AxesHelperComponent.js.map +0 -1
- package/dist/extensions/components/AxesHelperComponent.module.js.map +0 -1
- package/dist/extensions/components/ExtentsHelperComponent.js.map +0 -1
- package/dist/extensions/components/ExtentsHelperComponent.module.js.map +0 -1
- package/dist/extensions/components/GridHelperComponent.js.map +0 -1
- package/dist/extensions/components/GridHelperComponent.module.js.map +0 -1
- package/dist/extensions/components/LightHelperComponent.js.map +0 -1
- package/dist/extensions/components/LightHelperComponent.module.js.map +0 -1
- package/dist/extensions/components/RoomEnvironmentComponent.js.map +0 -1
- package/dist/extensions/components/RoomEnvironmentComponent.module.js.map +0 -1
- package/dist/extensions/components/StatsPanelComponent.js.map +0 -1
- package/dist/extensions/components/StatsPanelComponent.module.js.map +0 -1
- package/dist/extensions/loaders/GLTFCloudLoader.js.map +0 -1
- package/dist/extensions/loaders/GLTFCloudLoader.module.js.map +0 -1
- package/dist/extensions/loaders/GLTFFileLoader.js.map +0 -1
- package/dist/extensions/loaders/GLTFFileLoader.min.js +0 -24
- package/dist/extensions/loaders/GLTFFileLoader.module.js.map +0 -1
- package/dist/extensions/loaders/IFCXLoader.js.map +0 -1
- package/dist/extensions/loaders/IFCXLoader.module.js.map +0 -1
- package/dist/extensions/loaders/PotreeLoader.js.map +0 -1
- package/dist/extensions/loaders/PotreeLoader.module.js.map +0 -1
- package/lib/Viewer/measurement/Snapper.d.ts +0 -15
- package/lib/Viewer/measurement/UnitConverter.d.ts +0 -63
- package/lib/Viewer/measurement/UnitFormatter.d.ts +0 -4
- package/src/Viewer/measurement/Snapper.ts +0 -208
- package/src/Viewer/measurement/UnitConverter.ts +0 -47
- package/src/Viewer/measurement/UnitFormatter.ts +0 -95
- /package/dist/{extensions → plugins}/components/AxesHelperComponent.js +0 -0
- /package/dist/{extensions → plugins}/components/AxesHelperComponent.min.js +0 -0
- /package/dist/{extensions → plugins}/components/AxesHelperComponent.module.js +0 -0
- /package/dist/{extensions → plugins}/components/ExtentsHelperComponent.js +0 -0
- /package/dist/{extensions → plugins}/components/ExtentsHelperComponent.min.js +0 -0
- /package/dist/{extensions → plugins}/components/ExtentsHelperComponent.module.js +0 -0
- /package/dist/{extensions → plugins}/components/GridHelperComponent.js +0 -0
- /package/dist/{extensions → plugins}/components/GridHelperComponent.min.js +0 -0
- /package/dist/{extensions → plugins}/components/GridHelperComponent.module.js +0 -0
- /package/dist/{extensions → plugins}/components/LightHelperComponent.js +0 -0
- /package/dist/{extensions → plugins}/components/LightHelperComponent.min.js +0 -0
- /package/dist/{extensions → plugins}/components/LightHelperComponent.module.js +0 -0
- /package/dist/{extensions → plugins}/components/RoomEnvironmentComponent.js +0 -0
- /package/dist/{extensions → plugins}/components/RoomEnvironmentComponent.min.js +0 -0
- /package/dist/{extensions → plugins}/components/RoomEnvironmentComponent.module.js +0 -0
- /package/dist/{extensions → plugins}/components/StatsPanelComponent.js +0 -0
- /package/dist/{extensions → plugins}/components/StatsPanelComponent.min.js +0 -0
- /package/dist/{extensions → plugins}/components/StatsPanelComponent.module.js +0 -0
- /package/dist/{extensions → plugins}/loaders/GLTFCloudLoader.js +0 -0
- /package/dist/{extensions → plugins}/loaders/GLTFCloudLoader.min.js +0 -0
- /package/dist/{extensions → plugins}/loaders/GLTFCloudLoader.module.js +0 -0
- /package/dist/{extensions → plugins}/loaders/GLTFFileLoader.module.js +0 -0
- /package/dist/{extensions → plugins}/loaders/IFCXLoader.js +0 -0
- /package/dist/{extensions → plugins}/loaders/IFCXLoader.min.js +0 -0
- /package/dist/{extensions → plugins}/loaders/IFCXLoader.module.js +0 -0
- /package/dist/{extensions → plugins}/loaders/PotreeLoader.js +0 -0
- /package/dist/{extensions → plugins}/loaders/PotreeLoader.min.js +0 -0
- /package/dist/{extensions → plugins}/loaders/PotreeLoader.module.js +0 -0
- /package/{extensions → plugins}/components/AxesHelperComponent.ts +0 -0
- /package/{extensions → plugins}/components/ExtentsHelperComponent.ts +0 -0
- /package/{extensions → plugins}/components/GridHelperComponent.ts +0 -0
- /package/{extensions → plugins}/components/LightHelperComponent.ts +0 -0
- /package/{extensions → plugins}/components/RoomEnvironmentComponent.ts +0 -0
- /package/{extensions → plugins}/components/StatsPanelComponent.ts +0 -0
- /package/{extensions → plugins}/loaders/GLTFCloudLoader.ts +0 -0
- /package/{extensions → plugins}/loaders/GLTFFileLoader.ts +0 -0
- /package/{extensions → plugins}/loaders/IFCX/IFCXCloudLoader.ts +0 -0
- /package/{extensions → plugins}/loaders/IFCX/IFCXFileLoader.ts +0 -0
- /package/{extensions → plugins}/loaders/IFCX/IFCXLoader.ts +0 -0
- /package/{extensions → plugins}/loaders/IFCX/index.ts +0 -0
- /package/{extensions → plugins}/loaders/IFCX/render.js +0 -0
- /package/{extensions → plugins}/loaders/Potree/PotreeFileLoader.ts +0 -0
- /package/{extensions → plugins}/loaders/Potree/PotreeModelImpl.ts +0 -0
- /package/{extensions → plugins}/loaders/Potree/index.ts +0 -0
package/lib/Viewer/Viewer.d.ts
CHANGED
|
@@ -105,7 +105,7 @@ export declare class Viewer extends EventEmitter2<ViewerEventMap & CanvasEventMa
|
|
|
105
105
|
* object
|
|
106
106
|
*
|
|
107
107
|
* @param params - Loading parameters.
|
|
108
|
-
* @param params.format - File format. Can be `gltf`, `glb` or format from
|
|
108
|
+
* @param params.format - File format. Can be `gltf`, `glb` or format from a plugin. Required when
|
|
109
109
|
* loading a file as `ArrayBuffer` or `Data URL`.
|
|
110
110
|
* @param params.mode - File opening mode. Can be one of:
|
|
111
111
|
*
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { Object3D, Vector2 } from "three";
|
|
1
|
+
import { Intersection, Object3D, Raycaster, Vector2 } from "three";
|
|
2
2
|
import type { IComponent } from "@inweb/viewer-core";
|
|
3
3
|
import type { Viewer } from "../Viewer";
|
|
4
4
|
import type { IModelImpl } from "../models/IModelImpl";
|
|
5
5
|
import type { HighlighterComponent } from "./HighlighterComponent";
|
|
6
6
|
export declare class SelectionComponent implements IComponent {
|
|
7
7
|
protected viewer: Viewer;
|
|
8
|
+
protected raycaster: Raycaster;
|
|
8
9
|
protected downPosition: Vector2;
|
|
9
10
|
protected highlighter: HighlighterComponent;
|
|
10
11
|
constructor(viewer: Viewer);
|
|
@@ -13,6 +14,7 @@ export declare class SelectionComponent implements IComponent {
|
|
|
13
14
|
onPointerUp: (event: PointerEvent) => void;
|
|
14
15
|
onDoubleClick: (event: MouseEvent) => void;
|
|
15
16
|
getMousePosition(event: MouseEvent, target: Vector2): Vector2;
|
|
17
|
+
getPointerIntersects(mouse: Vector2, objects: Object3D[]): Array<Intersection<Object3D>>;
|
|
16
18
|
select(objects: Object3D | Object3D[], model?: IModelImpl): void;
|
|
17
19
|
deselect(objects: Object3D | Object3D[], model?: IModelImpl): void;
|
|
18
20
|
toggleSelection(objects: Object3D | Object3D[], model?: IModelImpl): void;
|
|
@@ -4,10 +4,6 @@ export declare class MeasureLineDragger extends OrbitDragger {
|
|
|
4
4
|
private overlay;
|
|
5
5
|
private line;
|
|
6
6
|
private snapper;
|
|
7
|
-
private objects;
|
|
8
|
-
private scale;
|
|
9
|
-
private units;
|
|
10
|
-
private precision;
|
|
11
7
|
constructor(viewer: Viewer);
|
|
12
8
|
dispose(): void;
|
|
13
9
|
onPointerDown: (event: PointerEvent) => void;
|
|
@@ -15,9 +11,7 @@ export declare class MeasureLineDragger extends OrbitDragger {
|
|
|
15
11
|
onPointerUp: (event: PointerEvent) => void;
|
|
16
12
|
onPointerCancel: (event: PointerEvent) => void;
|
|
17
13
|
onPointerLeave: () => void;
|
|
18
|
-
clearOverlay: () => void;
|
|
19
14
|
renderOverlay: () => void;
|
|
20
|
-
|
|
15
|
+
updateSnapper: () => void;
|
|
21
16
|
updateSnapperCamera: () => void;
|
|
22
|
-
updateUnits: () => void;
|
|
23
17
|
}
|
|
@@ -5,10 +5,6 @@ import { IModel } from "@inweb/viewer-core";
|
|
|
5
5
|
*/
|
|
6
6
|
export interface IModelImpl extends IModel {
|
|
7
7
|
scene: Object3D;
|
|
8
|
-
getUnits(): string;
|
|
9
|
-
getUnitScale(): number;
|
|
10
|
-
getUnitString(): string;
|
|
11
|
-
getPrecision(): number;
|
|
12
8
|
getExtents(target: Box3): Box3;
|
|
13
9
|
getObjects(): Object3D[];
|
|
14
10
|
getVisibleObjects(): Object3D[];
|
|
@@ -5,10 +5,6 @@ export declare class ModelImpl implements IModelImpl {
|
|
|
5
5
|
scene: Object3D;
|
|
6
6
|
constructor(scene: Object3D);
|
|
7
7
|
dispose(): void;
|
|
8
|
-
getUnits(): string;
|
|
9
|
-
getUnitScale(): number;
|
|
10
|
-
getUnitString(): string;
|
|
11
|
-
getPrecision(): number;
|
|
12
8
|
getExtents(target: Box3): Box3;
|
|
13
9
|
getObjects(): Object3D[];
|
|
14
10
|
getVisibleObjects(): Object3D[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inweb/viewer-three",
|
|
3
|
-
"version": "26.11.
|
|
3
|
+
"version": "26.11.1",
|
|
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",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"three.js"
|
|
15
15
|
],
|
|
16
16
|
"sideEffects": [
|
|
17
|
-
"dist/
|
|
17
|
+
"dist/plugins/**/*"
|
|
18
18
|
],
|
|
19
19
|
"main": "dist/viewer-three.js",
|
|
20
20
|
"module": "dist/viewer-three.module.js",
|
|
@@ -23,22 +23,22 @@
|
|
|
23
23
|
"dist",
|
|
24
24
|
"lib/**/*.d.ts",
|
|
25
25
|
"src",
|
|
26
|
-
"
|
|
26
|
+
"plugins"
|
|
27
27
|
],
|
|
28
28
|
"scripts": {
|
|
29
|
-
"build": "npm run build:viewer && npm run build:
|
|
29
|
+
"build": "npm run build:viewer && npm run build:plugins",
|
|
30
30
|
"build:viewer": "rollup -c rollup.config.js",
|
|
31
|
-
"build:
|
|
32
|
-
"test": "npm run test:viewer && npm run test:
|
|
31
|
+
"build:plugins": "rollup -c rollup.plugins.config.js",
|
|
32
|
+
"test": "npm run test:viewer && npm run test:plugins",
|
|
33
33
|
"test:viewer": "karma start karma.conf.js",
|
|
34
|
-
"test:
|
|
34
|
+
"test:plugins": "karma start karma.plugins.conf.js",
|
|
35
35
|
"docs": "typedoc"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@inweb/client": "~26.11.
|
|
39
|
-
"@inweb/eventemitter2": "~26.11.
|
|
40
|
-
"@inweb/markup": "~26.11.
|
|
41
|
-
"@inweb/viewer-core": "~26.11.
|
|
38
|
+
"@inweb/client": "~26.11.1",
|
|
39
|
+
"@inweb/eventemitter2": "~26.11.1",
|
|
40
|
+
"@inweb/markup": "~26.11.1",
|
|
41
|
+
"@inweb/viewer-core": "~26.11.1"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@types/three": "^0.180.0",
|
package/src/Viewer/Viewer.ts
CHANGED
|
@@ -388,7 +388,7 @@ export class Viewer
|
|
|
388
388
|
* object
|
|
389
389
|
*
|
|
390
390
|
* @param params - Loading parameters.
|
|
391
|
-
* @param params.format - File format. Can be `gltf`, `glb` or format from
|
|
391
|
+
* @param params.format - File format. Can be `gltf`, `glb` or format from a plugin. Required when
|
|
392
392
|
* loading a file as `ArrayBuffer` or `Data URL`.
|
|
393
393
|
* @param params.mode - File opening mode. Can be one of:
|
|
394
394
|
*
|
|
@@ -21,21 +21,22 @@
|
|
|
21
21
|
// acknowledge and accept the above terms.
|
|
22
22
|
///////////////////////////////////////////////////////////////////////////////
|
|
23
23
|
|
|
24
|
-
import { Object3D, Vector2 } from "three";
|
|
24
|
+
import { Intersection, Object3D, Raycaster, Vector2 } from "three";
|
|
25
25
|
|
|
26
26
|
import type { IComponent } from "@inweb/viewer-core";
|
|
27
27
|
import type { Viewer } from "../Viewer";
|
|
28
28
|
import type { IModelImpl } from "../models/IModelImpl";
|
|
29
29
|
import type { HighlighterComponent } from "./HighlighterComponent";
|
|
30
|
-
import { Snapper } from "../measurement/Snapper";
|
|
31
30
|
|
|
32
31
|
export class SelectionComponent implements IComponent {
|
|
33
32
|
protected viewer: Viewer;
|
|
33
|
+
protected raycaster: Raycaster;
|
|
34
34
|
protected downPosition: Vector2;
|
|
35
35
|
protected highlighter: HighlighterComponent;
|
|
36
36
|
|
|
37
37
|
constructor(viewer: Viewer) {
|
|
38
38
|
this.viewer = viewer;
|
|
39
|
+
this.raycaster = new Raycaster();
|
|
39
40
|
this.downPosition = new Vector2();
|
|
40
41
|
|
|
41
42
|
this.viewer.addEventListener("pointerdown", this.onPointerDown);
|
|
@@ -63,12 +64,10 @@ export class SelectionComponent implements IComponent {
|
|
|
63
64
|
const upPosition = this.getMousePosition(event, new Vector2());
|
|
64
65
|
if (upPosition.distanceTo(this.downPosition) !== 0) return;
|
|
65
66
|
|
|
66
|
-
const snapper = new Snapper(this.viewer.camera, this.viewer.renderer, this.viewer.canvas);
|
|
67
|
-
|
|
68
67
|
let intersections = [];
|
|
69
68
|
this.viewer.models.forEach((model) => {
|
|
70
69
|
const objects = model.getVisibleObjects();
|
|
71
|
-
const intersects =
|
|
70
|
+
const intersects = this.getPointerIntersects(upPosition, objects);
|
|
72
71
|
if (intersects.length > 0) intersections.push({ ...intersects[0], model });
|
|
73
72
|
});
|
|
74
73
|
intersections = intersections.sort((a, b) => a.distance - b.distance);
|
|
@@ -100,6 +99,32 @@ export class SelectionComponent implements IComponent {
|
|
|
100
99
|
return target.set(event.clientX, event.clientY);
|
|
101
100
|
}
|
|
102
101
|
|
|
102
|
+
getPointerIntersects(mouse: Vector2, objects: Object3D[]): Array<Intersection<Object3D>> {
|
|
103
|
+
const rect = this.viewer.canvas.getBoundingClientRect();
|
|
104
|
+
const x = ((mouse.x - rect.left) / rect.width) * 2 - 1;
|
|
105
|
+
const y = (-(mouse.y - rect.top) / rect.height) * 2 + 1;
|
|
106
|
+
|
|
107
|
+
const coords = new Vector2(x, y);
|
|
108
|
+
this.raycaster.setFromCamera(coords, this.viewer.camera);
|
|
109
|
+
|
|
110
|
+
this.raycaster.params = {
|
|
111
|
+
Mesh: {},
|
|
112
|
+
Line: { threshold: 0.05 },
|
|
113
|
+
Line2: { threshold: 0.05 },
|
|
114
|
+
LOD: {},
|
|
115
|
+
Points: { threshold: 0.01 },
|
|
116
|
+
Sprite: {},
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
let intersects = this.raycaster.intersectObjects(objects, false);
|
|
120
|
+
|
|
121
|
+
(this.viewer.renderer.clippingPlanes || []).forEach((plane) => {
|
|
122
|
+
intersects = intersects.filter((intersect) => plane.distanceToPoint(intersect.point) >= 0);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
return intersects;
|
|
126
|
+
}
|
|
127
|
+
|
|
103
128
|
select(objects: Object3D | Object3D[], model?: IModelImpl) {
|
|
104
129
|
if (!model) {
|
|
105
130
|
this.viewer.models.forEach((model) => this.select(objects, model));
|
|
@@ -21,24 +21,32 @@
|
|
|
21
21
|
// acknowledge and accept the above terms.
|
|
22
22
|
///////////////////////////////////////////////////////////////////////////////
|
|
23
23
|
|
|
24
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
Camera,
|
|
26
|
+
EdgesGeometry,
|
|
27
|
+
Intersection,
|
|
28
|
+
Line3,
|
|
29
|
+
MathUtils,
|
|
30
|
+
Matrix4,
|
|
31
|
+
Object3D,
|
|
32
|
+
Plane,
|
|
33
|
+
Raycaster,
|
|
34
|
+
Vector2,
|
|
35
|
+
Vector3,
|
|
36
|
+
Vector4,
|
|
37
|
+
} from "three";
|
|
25
38
|
|
|
26
39
|
import type { Viewer } from "../Viewer";
|
|
27
40
|
import { OrbitDragger } from "./OrbitDragger";
|
|
28
|
-
import { convertUnits } from "../measurement/UnitConverter";
|
|
29
|
-
import { formatDistance } from "../measurement/UnitFormatter";
|
|
30
|
-
import { Snapper } from "../measurement/Snapper";
|
|
31
41
|
|
|
32
|
-
const
|
|
42
|
+
const PRECISION = 0.01;
|
|
43
|
+
const DESKTOP_SNAP_DISTANCE = 10;
|
|
44
|
+
const MOBILE_SNAP_DISTANCE = 50;
|
|
33
45
|
|
|
34
46
|
export class MeasureLineDragger extends OrbitDragger {
|
|
35
47
|
private overlay: MeasureOverlay;
|
|
36
48
|
private line: MeasureLine;
|
|
37
|
-
private snapper:
|
|
38
|
-
private objects: Object3D[];
|
|
39
|
-
private scale = 1.0;
|
|
40
|
-
private units = "";
|
|
41
|
-
private precision: any = 2;
|
|
49
|
+
private snapper: MeasureSnapper;
|
|
42
50
|
|
|
43
51
|
constructor(viewer: Viewer) {
|
|
44
52
|
super(viewer);
|
|
@@ -46,15 +54,11 @@ export class MeasureLineDragger extends OrbitDragger {
|
|
|
46
54
|
this.overlay = new MeasureOverlay(viewer.camera, viewer.canvas);
|
|
47
55
|
this.overlay.attach();
|
|
48
56
|
|
|
49
|
-
this.line = new MeasureLine(this.overlay
|
|
57
|
+
this.line = new MeasureLine(this.overlay);
|
|
50
58
|
this.overlay.addLine(this.line);
|
|
51
59
|
|
|
52
|
-
this.snapper = new
|
|
53
|
-
|
|
54
|
-
this.objects = [];
|
|
55
|
-
this.updateObjects();
|
|
56
|
-
|
|
57
|
-
this.updateUnits();
|
|
60
|
+
this.snapper = new MeasureSnapper(viewer.camera, viewer.canvas);
|
|
61
|
+
this.updateSnapper();
|
|
58
62
|
|
|
59
63
|
this.viewer.canvas.addEventListener("pointerdown", this.onPointerDown);
|
|
60
64
|
this.viewer.canvas.addEventListener("pointermove", this.onPointerMove);
|
|
@@ -63,12 +67,11 @@ export class MeasureLineDragger extends OrbitDragger {
|
|
|
63
67
|
this.viewer.canvas.addEventListener("pointerleave", this.onPointerLeave);
|
|
64
68
|
|
|
65
69
|
this.viewer.addEventListener("render", this.renderOverlay);
|
|
66
|
-
this.viewer.addEventListener("hide", this.
|
|
67
|
-
this.viewer.addEventListener("isolate", this.
|
|
68
|
-
this.viewer.addEventListener("show", this.
|
|
69
|
-
this.viewer.addEventListener("showall", this.
|
|
70
|
+
this.viewer.addEventListener("hide", this.updateSnapper);
|
|
71
|
+
this.viewer.addEventListener("isolate", this.updateSnapper);
|
|
72
|
+
this.viewer.addEventListener("show", this.updateSnapper);
|
|
73
|
+
this.viewer.addEventListener("showall", this.updateSnapper);
|
|
70
74
|
this.viewer.addEventListener("changecameramode", this.updateSnapperCamera);
|
|
71
|
-
this.viewer.addEventListener("optionschange", this.updateUnits);
|
|
72
75
|
}
|
|
73
76
|
|
|
74
77
|
override dispose() {
|
|
@@ -79,14 +82,13 @@ export class MeasureLineDragger extends OrbitDragger {
|
|
|
79
82
|
this.viewer.canvas.removeEventListener("pointerleave", this.onPointerLeave);
|
|
80
83
|
|
|
81
84
|
this.viewer.removeEventListener("render", this.renderOverlay);
|
|
82
|
-
this.viewer.removeEventListener("hide", this.
|
|
83
|
-
this.viewer.removeEventListener("isolate", this.
|
|
84
|
-
this.viewer.removeEventListener("show", this.
|
|
85
|
-
this.viewer.removeEventListener("showall", this.
|
|
85
|
+
this.viewer.removeEventListener("hide", this.updateSnapper);
|
|
86
|
+
this.viewer.removeEventListener("isolate", this.updateSnapper);
|
|
87
|
+
this.viewer.removeEventListener("show", this.updateSnapper);
|
|
88
|
+
this.viewer.removeEventListener("showall", this.updateSnapper);
|
|
86
89
|
this.viewer.removeEventListener("changecameramode", this.updateSnapperCamera);
|
|
87
|
-
this.viewer.removeEventListener("optionschange", this.updateUnits);
|
|
88
90
|
|
|
89
|
-
this.
|
|
91
|
+
this.snapper.dispose();
|
|
90
92
|
|
|
91
93
|
this.overlay.detach();
|
|
92
94
|
this.overlay.dispose();
|
|
@@ -97,8 +99,7 @@ export class MeasureLineDragger extends OrbitDragger {
|
|
|
97
99
|
onPointerDown = (event: PointerEvent) => {
|
|
98
100
|
if (event.button !== 0) return;
|
|
99
101
|
|
|
100
|
-
|
|
101
|
-
this.line.startPoint = this.snapper.getSnapPoint(mouse, this.objects);
|
|
102
|
+
this.line.startPoint = this.snapper.getSnapPoint(event);
|
|
102
103
|
this.line.render();
|
|
103
104
|
|
|
104
105
|
this.viewer.canvas.setPointerCapture(event.pointerId);
|
|
@@ -108,8 +109,7 @@ export class MeasureLineDragger extends OrbitDragger {
|
|
|
108
109
|
onPointerMove = (event: PointerEvent) => {
|
|
109
110
|
if (this.orbit.enabled && this.orbit.state !== -1) return;
|
|
110
111
|
|
|
111
|
-
const
|
|
112
|
-
const snapPoint = this.snapper.getSnapPoint(mouse, this.objects);
|
|
112
|
+
const snapPoint = this.snapper.getSnapPoint(event);
|
|
113
113
|
if (snapPoint && this.line.endPoint && snapPoint.equals(this.line.endPoint)) return;
|
|
114
114
|
|
|
115
115
|
this.line.endPoint = snapPoint;
|
|
@@ -119,8 +119,8 @@ export class MeasureLineDragger extends OrbitDragger {
|
|
|
119
119
|
};
|
|
120
120
|
|
|
121
121
|
onPointerUp = (event: PointerEvent) => {
|
|
122
|
-
if (this.line.startPoint && this.line.endPoint && this.line.getDistance()
|
|
123
|
-
this.line = new MeasureLine(this.overlay
|
|
122
|
+
if (this.line.startPoint && this.line.endPoint && this.line.getDistance() >= PRECISION) {
|
|
123
|
+
this.line = new MeasureLine(this.overlay);
|
|
124
124
|
this.overlay.addLine(this.line);
|
|
125
125
|
} else {
|
|
126
126
|
this.line.startPoint = undefined;
|
|
@@ -141,50 +141,204 @@ export class MeasureLineDragger extends OrbitDragger {
|
|
|
141
141
|
this.line.render();
|
|
142
142
|
};
|
|
143
143
|
|
|
144
|
-
clearOverlay = () => {
|
|
145
|
-
this.overlay.clear();
|
|
146
|
-
|
|
147
|
-
this.line = new MeasureLine(this.overlay, this.scale, this.units, this.precision);
|
|
148
|
-
this.overlay.addLine(this.line);
|
|
149
|
-
};
|
|
150
|
-
|
|
151
144
|
renderOverlay = () => {
|
|
152
145
|
this.overlay.render();
|
|
153
146
|
};
|
|
154
147
|
|
|
155
|
-
|
|
156
|
-
this.
|
|
157
|
-
this.viewer.models.forEach((model) => {
|
|
158
|
-
model.getVisibleObjects().forEach((object) => this.objects.push(object));
|
|
159
|
-
});
|
|
148
|
+
updateSnapper = () => {
|
|
149
|
+
this.snapper.setFromViewer(this.viewer);
|
|
160
150
|
};
|
|
161
151
|
|
|
162
152
|
updateSnapperCamera = () => {
|
|
163
153
|
this.snapper.camera = this.viewer.camera;
|
|
164
154
|
this.overlay.camera = this.viewer.camera;
|
|
165
155
|
};
|
|
156
|
+
}
|
|
166
157
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
158
|
+
const _vertex = new Vector3();
|
|
159
|
+
const _start = new Vector3();
|
|
160
|
+
const _end = new Vector3();
|
|
161
|
+
const _line = new Line3();
|
|
162
|
+
const _center = new Vector3();
|
|
163
|
+
const _projection = new Vector3();
|
|
171
164
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
165
|
+
class MeasureSnapper {
|
|
166
|
+
public camera: Camera;
|
|
167
|
+
private canvas: HTMLCanvasElement;
|
|
168
|
+
private objects: Object3D[];
|
|
169
|
+
private clippingPlanes: Plane[];
|
|
170
|
+
private raycaster: Raycaster;
|
|
171
|
+
private detectRadiusInPixels: number;
|
|
172
|
+
private edgesCache: WeakMap<any, EdgesGeometry>;
|
|
173
|
+
|
|
174
|
+
constructor(camera: Camera, canvas: HTMLCanvasElement) {
|
|
175
|
+
this.camera = camera;
|
|
176
|
+
this.canvas = canvas;
|
|
177
|
+
this.objects = [];
|
|
178
|
+
this.clippingPlanes = [];
|
|
179
|
+
this.raycaster = new Raycaster();
|
|
180
|
+
this.detectRadiusInPixels = this.isMobile() ? MOBILE_SNAP_DISTANCE : DESKTOP_SNAP_DISTANCE;
|
|
181
|
+
this.edgesCache = new WeakMap();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
dispose() {
|
|
185
|
+
this.objects = [];
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
isMobile(): boolean {
|
|
189
|
+
if (typeof navigator === "undefined") return false;
|
|
190
|
+
|
|
191
|
+
// ===================== AI-CODE-START ======================
|
|
192
|
+
// Source: Claude Sonnet 4
|
|
193
|
+
// Date: 2025-09-09
|
|
194
|
+
// Reviewer: roman.mochalov@opendesign.com
|
|
195
|
+
// Issue: CLOUD-5799
|
|
196
|
+
|
|
197
|
+
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent);
|
|
198
|
+
|
|
199
|
+
// ===================== AI-CODE-END ======================
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
getMousePosition(event: MouseEvent, target: Vector2): Vector2 {
|
|
203
|
+
return target.set(event.clientX, event.clientY);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
getPointerIntersects(mouse: Vector2): Array<Intersection<Object3D>> {
|
|
207
|
+
const rect = this.canvas.getBoundingClientRect();
|
|
208
|
+
const x = ((mouse.x - rect.left) / rect.width) * 2 - 1;
|
|
209
|
+
const y = (-(mouse.y - rect.top) / rect.height) * 2 + 1;
|
|
210
|
+
|
|
211
|
+
const coords = new Vector2(x, y);
|
|
212
|
+
this.raycaster.setFromCamera(coords, this.camera);
|
|
213
|
+
|
|
214
|
+
this.raycaster.params = {
|
|
215
|
+
Mesh: {},
|
|
216
|
+
Line: { threshold: 0.05 },
|
|
217
|
+
Line2: { threshold: 0.05 },
|
|
218
|
+
LOD: {},
|
|
219
|
+
Points: { threshold: 0.01 },
|
|
220
|
+
Sprite: {},
|
|
221
|
+
};
|
|
222
|
+
|
|
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;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
getDetectRadius(point: Vector3): number {
|
|
233
|
+
const camera: any = this.camera;
|
|
234
|
+
|
|
235
|
+
// ===================== AI-CODE-START ======================
|
|
236
|
+
// Source: Gemini 2.5 Pro
|
|
237
|
+
// Date: 2025-08-27
|
|
238
|
+
// Reviewer: roman.mochalov@opendesign.com
|
|
239
|
+
// Issue: CLOUD-5799
|
|
240
|
+
// Notes: Originally AI-generated, modified manually
|
|
241
|
+
|
|
242
|
+
if (camera.isOrthographicCamera) {
|
|
243
|
+
const fieldHeight = (camera.top - camera.bottom) / camera.zoom;
|
|
244
|
+
|
|
245
|
+
const canvasHeight = this.canvas.height;
|
|
246
|
+
const worldUnitsPerPixel = fieldHeight / canvasHeight;
|
|
247
|
+
|
|
248
|
+
return this.detectRadiusInPixels * worldUnitsPerPixel;
|
|
178
249
|
}
|
|
179
250
|
|
|
180
|
-
if (
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
251
|
+
if (camera.isPerspectiveCamera) {
|
|
252
|
+
const distance = camera.position.distanceTo(point);
|
|
253
|
+
const fieldHeight = 2 * Math.tan(MathUtils.degToRad(camera.fov * 0.5)) * distance;
|
|
254
|
+
|
|
255
|
+
const canvasHeight = this.canvas.height;
|
|
256
|
+
const worldUnitsPerPixel = fieldHeight / canvasHeight;
|
|
257
|
+
|
|
258
|
+
return this.detectRadiusInPixels * worldUnitsPerPixel;
|
|
184
259
|
}
|
|
185
260
|
|
|
186
|
-
|
|
187
|
-
|
|
261
|
+
// ===================== AI-CODE-END ======================
|
|
262
|
+
|
|
263
|
+
return 0.1;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
getSnapPoint(event: PointerEvent): Vector3 {
|
|
267
|
+
const mouse = this.getMousePosition(event, new Vector2());
|
|
268
|
+
|
|
269
|
+
const intersections = this.getPointerIntersects(mouse);
|
|
270
|
+
if (intersections.length === 0) return undefined;
|
|
271
|
+
|
|
272
|
+
// ===================== AI-CODE-START ======================
|
|
273
|
+
// Source: Gemini 2.5 Pro
|
|
274
|
+
// Date: 2025-08-20
|
|
275
|
+
// Reviewer: roman.mochalov@opendesign.com
|
|
276
|
+
// Issue: CLOUD-5799
|
|
277
|
+
// Notes: Originally AI-generated, modified manually
|
|
278
|
+
|
|
279
|
+
const object: any = intersections[0].object;
|
|
280
|
+
const intersectionPoint = intersections[0].point;
|
|
281
|
+
const localPoint = object.worldToLocal(intersectionPoint.clone());
|
|
282
|
+
|
|
283
|
+
let snapPoint: Vector3;
|
|
284
|
+
let snapDistance = this.getDetectRadius(intersectionPoint);
|
|
285
|
+
|
|
286
|
+
const geometry = object.geometry;
|
|
287
|
+
|
|
288
|
+
const positions = geometry.attributes.position.array;
|
|
289
|
+
for (let i = 0; i < positions.length; i += 3) {
|
|
290
|
+
_vertex.set(positions[i], positions[i + 1], positions[i + 2]);
|
|
291
|
+
const distance = _vertex.distanceTo(localPoint);
|
|
292
|
+
if (distance < snapDistance) {
|
|
293
|
+
snapDistance = distance;
|
|
294
|
+
snapPoint = _vertex.clone();
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
if (snapPoint) return object.localToWorld(snapPoint);
|
|
298
|
+
|
|
299
|
+
let edges = this.edgesCache.get(geometry);
|
|
300
|
+
if (!edges) {
|
|
301
|
+
edges = new EdgesGeometry(geometry);
|
|
302
|
+
this.edgesCache.set(geometry, edges);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const edgePositions = edges.attributes.position.array;
|
|
306
|
+
for (let i = 0; i < edgePositions.length; i += 6) {
|
|
307
|
+
_start.set(edgePositions[i], edgePositions[i + 1], edgePositions[i + 2]);
|
|
308
|
+
_end.set(edgePositions[i + 3], edgePositions[i + 4], edgePositions[i + 5]);
|
|
309
|
+
_line.set(_start, _end);
|
|
310
|
+
|
|
311
|
+
_line.getCenter(_center);
|
|
312
|
+
const centerDistance = _center.distanceTo(localPoint);
|
|
313
|
+
if (centerDistance < snapDistance) {
|
|
314
|
+
snapDistance = centerDistance;
|
|
315
|
+
snapPoint = _center.clone();
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
_line.closestPointToPoint(localPoint, true, _projection);
|
|
320
|
+
const lineDistance = _projection.distanceTo(localPoint);
|
|
321
|
+
if (lineDistance < snapDistance) {
|
|
322
|
+
snapDistance = lineDistance;
|
|
323
|
+
snapPoint = _projection.clone();
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
if (snapPoint) return object.localToWorld(snapPoint);
|
|
327
|
+
|
|
328
|
+
// ===================== AI-CODE-END ======================
|
|
329
|
+
|
|
330
|
+
return intersectionPoint.clone();
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
setFromViewer(viewer: Viewer) {
|
|
334
|
+
this.objects.length = 0;
|
|
335
|
+
viewer.models.forEach((model) => {
|
|
336
|
+
model.getVisibleObjects().forEach((object) => this.objects.push(object));
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
this.camera = viewer.camera;
|
|
340
|
+
this.clippingPlanes = viewer.renderer.clippingPlanes || [];
|
|
341
|
+
}
|
|
188
342
|
}
|
|
189
343
|
|
|
190
344
|
class MeasureOverlay {
|
|
@@ -202,10 +356,6 @@ class MeasureOverlay {
|
|
|
202
356
|
this.resizeObserver = new ResizeObserver(this.resizeContainer);
|
|
203
357
|
}
|
|
204
358
|
|
|
205
|
-
dispose() {
|
|
206
|
-
this.clear();
|
|
207
|
-
}
|
|
208
|
-
|
|
209
359
|
attach() {
|
|
210
360
|
this.container = document.createElement("div");
|
|
211
361
|
this.container.id = "measure-container";
|
|
@@ -220,6 +370,10 @@ class MeasureOverlay {
|
|
|
220
370
|
this.resizeObserver.observe(this.canvas);
|
|
221
371
|
}
|
|
222
372
|
|
|
373
|
+
dispose() {
|
|
374
|
+
this.clear();
|
|
375
|
+
}
|
|
376
|
+
|
|
223
377
|
detach() {
|
|
224
378
|
this.resizeObserver.disconnect();
|
|
225
379
|
|
|
@@ -229,7 +383,7 @@ class MeasureOverlay {
|
|
|
229
383
|
|
|
230
384
|
clear() {
|
|
231
385
|
this.lines.forEach((line) => line.dispose());
|
|
232
|
-
this.lines
|
|
386
|
+
this.lines = [];
|
|
233
387
|
}
|
|
234
388
|
|
|
235
389
|
render() {
|
|
@@ -249,14 +403,6 @@ class MeasureOverlay {
|
|
|
249
403
|
this.lines = this.lines.filter((x) => x !== line);
|
|
250
404
|
}
|
|
251
405
|
|
|
252
|
-
updateLineUnits(scale: number, units: string, precision: any) {
|
|
253
|
-
this.lines.forEach((line) => {
|
|
254
|
-
line.scale = scale;
|
|
255
|
-
line.units = units;
|
|
256
|
-
line.precision = precision;
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
|
|
260
406
|
resizeContainer = () => {
|
|
261
407
|
const { offsetLeft, offsetTop, offsetWidth, offsetHeight } = this.canvas;
|
|
262
408
|
|
|
@@ -284,9 +430,8 @@ class MeasureLine {
|
|
|
284
430
|
public endPoint: Vector3;
|
|
285
431
|
|
|
286
432
|
public id = MathUtils.generateUUID();
|
|
287
|
-
public
|
|
288
|
-
public
|
|
289
|
-
public precision: any;
|
|
433
|
+
public unit = "";
|
|
434
|
+
public scale = 1.0;
|
|
290
435
|
public size = 10.0;
|
|
291
436
|
public lineWidth = 2;
|
|
292
437
|
|
|
@@ -298,11 +443,8 @@ class MeasureLine {
|
|
|
298
443
|
font: "1rem system-ui",
|
|
299
444
|
};
|
|
300
445
|
|
|
301
|
-
constructor(overlay: MeasureOverlay
|
|
446
|
+
constructor(overlay: MeasureOverlay) {
|
|
302
447
|
this.overlay = overlay;
|
|
303
|
-
this.scale = scale;
|
|
304
|
-
this.units = units;
|
|
305
|
-
this.precision = precision;
|
|
306
448
|
|
|
307
449
|
this.elementStartPoint = overlay.container.appendChild(document.createElement("div"));
|
|
308
450
|
this.elementEndPoint = overlay.container.appendChild(document.createElement("div"));
|
|
@@ -369,10 +511,10 @@ class MeasureLine {
|
|
|
369
511
|
|
|
370
512
|
const distance = this.getDistance();
|
|
371
513
|
|
|
372
|
-
this.elementLabel.style.display = visible && distance
|
|
514
|
+
this.elementLabel.style.display = visible && distance >= PRECISION ? "block" : "none";
|
|
373
515
|
this.elementLabel.style.left = `${point.x}px`;
|
|
374
516
|
this.elementLabel.style.top = `${point.y}px`;
|
|
375
|
-
this.elementLabel.innerHTML =
|
|
517
|
+
this.elementLabel.innerHTML = `${distance.toFixed(2)} ${this.unit}`;
|
|
376
518
|
} else {
|
|
377
519
|
this.elementLabel.style.display = "none";
|
|
378
520
|
}
|