@realsee/dnalogel 2.0.0-alpha.6 → 2.0.0-alpha.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.
- package/CHANGELOG.md +5 -0
- package/README.md +44 -5
- package/dist/dnalogel.cjs.js +7 -2
- package/dist/dnalogel.es.js +3516 -159
- package/dist/dnalogel.umd.js +7 -2
- package/libs/PanoCompassPlugin/index.d.ts +3 -3
- package/libs/PanoCompassPlugin/index.js +56 -44
- package/libs/PanoMeasurePlugin/Controller/BaseController.d.ts +36 -0
- package/libs/PanoMeasurePlugin/Controller/BaseController.js +69 -0
- package/libs/PanoMeasurePlugin/Controller/EditController.d.ts +52 -0
- package/libs/PanoMeasurePlugin/Controller/EditController.js +216 -0
- package/libs/PanoMeasurePlugin/Controller/ShortcutKeyController.d.ts +12 -0
- package/libs/PanoMeasurePlugin/Controller/ShortcutKeyController.js +44 -0
- package/libs/PanoMeasurePlugin/Controller/WatchController.d.ts +25 -0
- package/libs/PanoMeasurePlugin/Controller/WatchController.js +223 -0
- package/libs/PanoMeasurePlugin/Controller/index.d.ts +61 -0
- package/libs/PanoMeasurePlugin/Controller/index.js +176 -0
- package/libs/PanoMeasurePlugin/Model/index.d.ts +38 -0
- package/libs/PanoMeasurePlugin/Model/index.js +114 -0
- package/libs/PanoMeasurePlugin/Model/line.d.ts +30 -0
- package/libs/PanoMeasurePlugin/Model/line.js +64 -0
- package/libs/PanoMeasurePlugin/Model/point.d.ts +16 -0
- package/libs/PanoMeasurePlugin/Model/point.js +28 -0
- package/libs/PanoMeasurePlugin/Model/polyline.d.ts +16 -0
- package/libs/PanoMeasurePlugin/Model/polyline.js +48 -0
- package/libs/PanoMeasurePlugin/Modules/DeleteDom/_Assets/delete.svg.d.ts +2 -0
- package/libs/PanoMeasurePlugin/Modules/DeleteDom/_Assets/delete.svg.js +15 -0
- package/libs/PanoMeasurePlugin/Modules/DeleteDom/_Assets/delete_bg.png.d.ts +2 -0
- package/libs/PanoMeasurePlugin/Modules/DeleteDom/_Assets/delete_bg.png.js +2 -0
- package/libs/PanoMeasurePlugin/Modules/DeleteDom/_Assets/delete_hover_bg.png.d.ts +2 -0
- package/libs/PanoMeasurePlugin/Modules/DeleteDom/_Assets/delete_hover_bg.png.js +2 -0
- package/libs/PanoMeasurePlugin/Modules/DeleteDom/index.d.ts +22 -0
- package/libs/PanoMeasurePlugin/Modules/DeleteDom/index.js +108 -0
- package/libs/PanoMeasurePlugin/Modules/FiveHelper.d.ts +11 -0
- package/libs/PanoMeasurePlugin/Modules/FiveHelper.js +33 -0
- package/libs/PanoMeasurePlugin/Modules/GuideController.d.ts +18 -0
- package/libs/PanoMeasurePlugin/Modules/GuideController.js +75 -0
- package/libs/PanoMeasurePlugin/Modules/Magnifier.d.ts +24 -0
- package/libs/PanoMeasurePlugin/Modules/Magnifier.js +103 -0
- package/libs/PanoMeasurePlugin/Modules/UIController/HTML.d.ts +6 -0
- package/libs/PanoMeasurePlugin/Modules/UIController/HTML.js +117 -0
- package/libs/PanoMeasurePlugin/Modules/UIController/MainBtnController.d.ts +18 -0
- package/libs/PanoMeasurePlugin/Modules/UIController/MainBtnController.js +102 -0
- package/libs/PanoMeasurePlugin/Modules/UIController/Revoke/index.d.ts +12 -0
- package/libs/PanoMeasurePlugin/Modules/UIController/Revoke/index.js +35 -0
- package/libs/PanoMeasurePlugin/Modules/UIController/index.d.ts +16 -0
- package/libs/PanoMeasurePlugin/Modules/UIController/index.js +69 -0
- package/libs/PanoMeasurePlugin/Modules/UIController/style.d.ts +17 -0
- package/libs/PanoMeasurePlugin/Modules/UIController/style.js +78 -0
- package/libs/PanoMeasurePlugin/index.d.ts +7 -0
- package/libs/PanoMeasurePlugin/index.js +5 -0
- package/libs/PanoMeasurePlugin/typings/data.d.ts +28 -0
- package/libs/PanoMeasurePlugin/typings/data.js +1 -0
- package/libs/PanoMeasurePlugin/typings/event.type.d.ts +28 -0
- package/libs/PanoMeasurePlugin/typings/event.type.js +1 -0
- package/libs/PanoMeasurePlugin/typings/utils.type.d.ts +1 -0
- package/libs/PanoMeasurePlugin/typings/utils.type.js +1 -0
- package/libs/PanoMeasurePlugin/utils/clearGroup.d.ts +2 -0
- package/libs/PanoMeasurePlugin/utils/clearGroup.js +3 -0
- package/libs/PanoMeasurePlugin/utils/constants.d.ts +4 -0
- package/libs/PanoMeasurePlugin/utils/constants.js +31 -0
- package/libs/PanoMeasurePlugin/utils/distanceDom.d.ts +28 -0
- package/libs/PanoMeasurePlugin/utils/distanceDom.js +114 -0
- package/libs/PanoMeasurePlugin/utils/findAssociatedLines.d.ts +2 -0
- package/libs/PanoMeasurePlugin/utils/findAssociatedLines.js +21 -0
- package/libs/PanoMeasurePlugin/utils/findClosestPoint.d.ts +10 -0
- package/libs/PanoMeasurePlugin/utils/findClosestPoint.js +27 -0
- package/libs/PanoMeasurePlugin/utils/getIntersectionFromEvent.d.ts +4 -0
- package/libs/PanoMeasurePlugin/utils/getIntersectionFromEvent.js +9 -0
- package/libs/PanoMeasurePlugin/utils/getPointFromHammerEvent.d.ts +10 -0
- package/libs/PanoMeasurePlugin/utils/getPointFromHammerEvent.js +14 -0
- package/libs/PanoMeasurePlugin/utils/ironbox.d.ts +1 -0
- package/libs/PanoMeasurePlugin/utils/ironbox.js +1 -0
- package/libs/PanoMeasurePlugin/utils/isNDCPointInScreen.d.ts +2 -0
- package/libs/PanoMeasurePlugin/utils/isNDCPointInScreen.js +6 -0
- package/libs/PanoMeasurePlugin/utils/line.d.ts +17 -0
- package/libs/PanoMeasurePlugin/utils/line.js +32 -0
- package/libs/PanoMeasurePlugin/utils/math.d.ts +17 -0
- package/libs/PanoMeasurePlugin/utils/math.js +44 -0
- package/libs/PanoMeasurePlugin/utils/mouseGroup.d.ts +2 -0
- package/libs/PanoMeasurePlugin/utils/mouseGroup.js +21 -0
- package/libs/PanoMeasurePlugin/utils/ndc2Screen.d.ts +5 -0
- package/libs/PanoMeasurePlugin/utils/ndc2Screen.js +6 -0
- package/libs/index.d.ts +2 -0
- package/libs/index.js +1 -0
- package/package.json +1 -1
- package/docs/.nojekyll +0 -1
- package/docs/assets/highlight.css +0 -29
- package/docs/assets/icons.css +0 -1043
- package/docs/assets/icons.png +0 -0
- package/docs/assets/icons@2x.png +0 -0
- package/docs/assets/main.js +0 -52
- package/docs/assets/search.js +0 -1
- package/docs/assets/style.css +0 -1413
- package/docs/assets/widgets.png +0 -0
- package/docs/assets/widgets@2x.png +0 -0
- package/docs/classes/ModelRoomLabelController.html +0 -20
- package/docs/enums/FLOOR_PLAN_ATTACHED_TO.html +0 -1
- package/docs/enums/ModelFloorplanErrorType.html +0 -1
- package/docs/index.html +0 -10
- package/docs/interfaces/ModelChassisCompassPluginData.html +0 -1
- package/docs/interfaces/ModelChassisCompassPluginExportType.html +0 -1
- package/docs/interfaces/ModelChassisCompassPluginParameterType.html +0 -1
- package/docs/interfaces/ModelEntryDoorGuidePluginData.html +0 -1
- package/docs/interfaces/ModelEntryDoorGuidePluginExportType.html +0 -1
- package/docs/interfaces/ModelFloorplanParameterType.html +0 -1
- package/docs/interfaces/ModelFloorplanViewEvent.html +0 -5
- package/docs/interfaces/ModelRoomLabelPluginData.html +0 -1
- package/docs/interfaces/PanoCompassPluginData.html +0 -1
- package/docs/interfaces/PanoCompassPluginParameterType.html +0 -1
- package/docs/interfaces/PanoCursorRaycasterPluginExportType.html +0 -11
- package/docs/interfaces/PanoRulerPluginExportType.html +0 -1
- package/docs/interfaces/PanoRulerPluginOptions.html +0 -1
- package/docs/interfaces/PanoRulerPluginParameterType.html +0 -1
- package/docs/interfaces/RoomLabel.html +0 -13
- package/docs/interfaces/TopviewFloorplanPluginParameterType.html +0 -1
- package/docs/modules.html +0 -26
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import Hammer from 'hammerjs';
|
|
2
|
+
import DeleteDom from '../Modules/DeleteDom';
|
|
3
|
+
import isNDCPointInScreen from '../utils/isNDCPointInScreen';
|
|
4
|
+
import BaseController from './BaseController';
|
|
5
|
+
import { Raycaster, Vector2 } from 'three';
|
|
6
|
+
import { closestPointOnLine } from '../utils/math';
|
|
7
|
+
import { findClosestPoint } from '../utils/findClosestPoint';
|
|
8
|
+
import { findAssociatedLines } from '../utils/findAssociatedLines';
|
|
9
|
+
import { getPointFromHammerEvent } from '../utils/getPointFromHammerEvent';
|
|
10
|
+
export default class WatchController extends BaseController {
|
|
11
|
+
type = 'watch';
|
|
12
|
+
deleteDom;
|
|
13
|
+
highlightedLines = [];
|
|
14
|
+
fiveElement;
|
|
15
|
+
editPointState;
|
|
16
|
+
hammer;
|
|
17
|
+
constructor(params) {
|
|
18
|
+
super(params);
|
|
19
|
+
this.deleteDom = new DeleteDom(this.five, { onClick: this.deleteDomClickCallback }).appendTo(this.container);
|
|
20
|
+
this.model.lines.forEach((line) => {
|
|
21
|
+
line.distanceItem.appendTo(this.container);
|
|
22
|
+
line.distanceItem.update(this.five);
|
|
23
|
+
line.hook.on('selected', this.chooseLine);
|
|
24
|
+
this.group.add(line.mesh);
|
|
25
|
+
});
|
|
26
|
+
const fiveElement = this.five.getElement();
|
|
27
|
+
if (fiveElement) {
|
|
28
|
+
this.fiveElement = fiveElement;
|
|
29
|
+
const hammer = new Hammer(fiveElement);
|
|
30
|
+
this.hammer = hammer;
|
|
31
|
+
hammer.on('pan', this.onPan);
|
|
32
|
+
hammer.on('panstart', this.onPanStart);
|
|
33
|
+
hammer.on('panend', this.onPanEnd);
|
|
34
|
+
}
|
|
35
|
+
this.model.hook.on('lineRemoved', this.lineRemoved);
|
|
36
|
+
this.five.on('cameraUpdate', this.onCameraUpdate);
|
|
37
|
+
this.five.on('wantsTapGesture', this.wantsTapGesture);
|
|
38
|
+
this.five.on('wantsPanGesture', this.wantsPanGesture);
|
|
39
|
+
this.five.needsRender = true;
|
|
40
|
+
}
|
|
41
|
+
dispose() {
|
|
42
|
+
super.dispose();
|
|
43
|
+
this.deleteDom.dispose();
|
|
44
|
+
this.model.hook.off('lineRemoved', this.lineRemoved);
|
|
45
|
+
this.five.off('cameraUpdate', this.onCameraUpdate);
|
|
46
|
+
this.five.off('wantsPanGesture', this.wantsPanGesture);
|
|
47
|
+
this.five.off('wantsTapGesture', this.wantsTapGesture);
|
|
48
|
+
this.five.needsRender = true;
|
|
49
|
+
this.hook.emit('selectedChange', []);
|
|
50
|
+
this.hammer?.destroy();
|
|
51
|
+
}
|
|
52
|
+
highlightLine(line) {
|
|
53
|
+
if (line.selected)
|
|
54
|
+
return;
|
|
55
|
+
line.selected = true;
|
|
56
|
+
this.group.add(line.lightMesh);
|
|
57
|
+
line.distanceItem.highlight();
|
|
58
|
+
this.five.needsRender = true;
|
|
59
|
+
}
|
|
60
|
+
clearHighlightLines() {
|
|
61
|
+
if (this.group.children.length === 0)
|
|
62
|
+
return;
|
|
63
|
+
this.deleteDom.setLines([]).hide();
|
|
64
|
+
this.model.lines.forEach((line) => {
|
|
65
|
+
line.selected = false;
|
|
66
|
+
line.distanceItem.unHighlight();
|
|
67
|
+
this.group.remove(line.lightMesh);
|
|
68
|
+
});
|
|
69
|
+
this.highlightedLines = [];
|
|
70
|
+
this.five.needsRender = true;
|
|
71
|
+
}
|
|
72
|
+
onPanStart = (e) => {
|
|
73
|
+
if (this.model.points.length === 0 || !this.fiveElement)
|
|
74
|
+
return;
|
|
75
|
+
const mouse = getPointFromHammerEvent(e).point;
|
|
76
|
+
const closestPoint = findClosestPoint(this.five, this.model.points, mouse, 20)?.point;
|
|
77
|
+
if (!closestPoint) {
|
|
78
|
+
this.editPointState = undefined;
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const editPointState = {
|
|
82
|
+
point: closestPoint,
|
|
83
|
+
associatedLines: findAssociatedLines(closestPoint.lines[0]),
|
|
84
|
+
};
|
|
85
|
+
const cancelDragLine = this.hook.emit('wantsDragLine', {
|
|
86
|
+
point: closestPoint.id,
|
|
87
|
+
lines: editPointState.associatedLines.map(({ id }) => id),
|
|
88
|
+
});
|
|
89
|
+
if (cancelDragLine)
|
|
90
|
+
return;
|
|
91
|
+
this.editPointState = editPointState;
|
|
92
|
+
this.magnifier.appendTo(this.container);
|
|
93
|
+
this.group.add(this.mouseGroup);
|
|
94
|
+
};
|
|
95
|
+
onPanEnd = () => {
|
|
96
|
+
if (!this.editPointState)
|
|
97
|
+
return;
|
|
98
|
+
// 把拖动的线从虚线转换为实线
|
|
99
|
+
this.editPointState.point.lines.forEach((line) => line.mesh.setMaterial({ dashed: false }));
|
|
100
|
+
// 还原选中态
|
|
101
|
+
this.highlightLines(this.editPointState.associatedLines);
|
|
102
|
+
this.editPointState = undefined;
|
|
103
|
+
this.magnifier.dispose();
|
|
104
|
+
this.group.remove(this.mouseGroup);
|
|
105
|
+
};
|
|
106
|
+
onPan = (e) => {
|
|
107
|
+
if (!this.editPointState || !this.fiveElement)
|
|
108
|
+
return;
|
|
109
|
+
const mouse = getPointFromHammerEvent(e).ndcPoint;
|
|
110
|
+
const raycaster = new Raycaster();
|
|
111
|
+
raycaster.setFromCamera(mouse, this.five.camera);
|
|
112
|
+
const [intersection] = this.five.model.intersectRaycaster(raycaster);
|
|
113
|
+
if (!intersection)
|
|
114
|
+
return;
|
|
115
|
+
this.onIntersectionUpdate(intersection);
|
|
116
|
+
};
|
|
117
|
+
onIntersectionUpdate = (intersection, mesh) => {
|
|
118
|
+
if (!this.editPointState)
|
|
119
|
+
return;
|
|
120
|
+
// 拖动过程中清除选中态
|
|
121
|
+
this.clearHighlightLines();
|
|
122
|
+
// 把拖动点相关的线变成虚线 & 改变相关线的位置
|
|
123
|
+
this.editPointState.point.position.copy(intersection.point);
|
|
124
|
+
this.editPointState.point.lines.forEach((line) => {
|
|
125
|
+
line.mesh.setPoints(line.points[0].position.clone(), line.points[1].position.clone());
|
|
126
|
+
line.lightMesh.setPoints(line.points[0].position.clone(), line.points[1].position.clone());
|
|
127
|
+
line.mesh.setMaterial({ dashed: true });
|
|
128
|
+
});
|
|
129
|
+
this.updateDistanceUI();
|
|
130
|
+
this.magnifier.updateWithPoint(intersection.point);
|
|
131
|
+
this.mouseGroup.position.copy(intersection.point);
|
|
132
|
+
if (mesh) {
|
|
133
|
+
this.mouseGroup.quaternion.copy(mesh.quaternion);
|
|
134
|
+
}
|
|
135
|
+
else if (intersection.face) {
|
|
136
|
+
const face = intersection.face.normal;
|
|
137
|
+
const positionVector = face.clone().multiplyScalar(0.05);
|
|
138
|
+
const position = intersection.point.clone().add(positionVector);
|
|
139
|
+
const lookAtVector = position.clone().add(positionVector);
|
|
140
|
+
this.mouseGroup.lookAt(lookAtVector);
|
|
141
|
+
}
|
|
142
|
+
this.five.needsRender = true;
|
|
143
|
+
this.hook.emit('selectedChange', this.editPointState.associatedLines.map((line) => line.toCompletelyJson()));
|
|
144
|
+
};
|
|
145
|
+
wantsPanGesture = () => {
|
|
146
|
+
if (this.editPointState)
|
|
147
|
+
return false;
|
|
148
|
+
};
|
|
149
|
+
wantsTapGesture = (raycaster) => {
|
|
150
|
+
const [target] = this.five.model.intersectRaycaster(raycaster);
|
|
151
|
+
if (!target)
|
|
152
|
+
return;
|
|
153
|
+
const camera = this.five.camera;
|
|
154
|
+
const ndcTarget = target.point.clone().project(camera);
|
|
155
|
+
const screenWidth = this.container.clientWidth;
|
|
156
|
+
const screenHeight = this.container.clientHeight;
|
|
157
|
+
const targetScreenPosition = new Vector2(ndcTarget.x * screenWidth, ndcTarget.y * screenHeight);
|
|
158
|
+
// 把线的端点全部映射到屏幕上,过滤掉不在视野内的线,同时乘以屏幕宽 & 高
|
|
159
|
+
const screenLines = this.model.lines
|
|
160
|
+
.map((line) => {
|
|
161
|
+
const [startPoint, endPoint] = line.points;
|
|
162
|
+
const ndcStartPoint = startPoint.position.clone().project(camera);
|
|
163
|
+
const ndcEndPoint = endPoint.position.clone().project(camera);
|
|
164
|
+
if (!isNDCPointInScreen(ndcStartPoint) && !isNDCPointInScreen(ndcEndPoint))
|
|
165
|
+
return null;
|
|
166
|
+
const startScreenPosition = new Vector2(ndcStartPoint.x * screenWidth, ndcStartPoint.y * screenHeight);
|
|
167
|
+
const endScreenPosition = new Vector2(ndcEndPoint.x * screenWidth, ndcEndPoint.y * screenHeight);
|
|
168
|
+
return { id: line.id, points: [startScreenPosition, endScreenPosition] };
|
|
169
|
+
})
|
|
170
|
+
.filter((line) => !!line);
|
|
171
|
+
if (screenLines.length === 0)
|
|
172
|
+
return;
|
|
173
|
+
// 找这些线距离鼠标点击位置最近的线
|
|
174
|
+
const distanceLines = screenLines
|
|
175
|
+
.map((line) => {
|
|
176
|
+
const closestPoint = closestPointOnLine(targetScreenPosition, line.points);
|
|
177
|
+
return { id: line.id, distance: closestPoint.distanceTo(targetScreenPosition) };
|
|
178
|
+
})
|
|
179
|
+
.sort((a, b) => a.distance - b.distance);
|
|
180
|
+
const closestScreenLine = distanceLines[0];
|
|
181
|
+
if (closestScreenLine.distance > 20)
|
|
182
|
+
return;
|
|
183
|
+
const closestLine = this.model.lines.find(({ id }) => id === closestScreenLine.id);
|
|
184
|
+
if (!closestLine)
|
|
185
|
+
return;
|
|
186
|
+
this.chooseLine(closestLine);
|
|
187
|
+
return false;
|
|
188
|
+
};
|
|
189
|
+
chooseLine = (line) => {
|
|
190
|
+
const highlightLines = findAssociatedLines(line);
|
|
191
|
+
this.highlightLines(highlightLines);
|
|
192
|
+
this.five.needsRender = true;
|
|
193
|
+
this.hook.emit('selectedChange', highlightLines.map((line) => line.toCompletelyJson()));
|
|
194
|
+
};
|
|
195
|
+
highlightLines(lines) {
|
|
196
|
+
this.clearHighlightLines();
|
|
197
|
+
this.highlightedLines = lines;
|
|
198
|
+
this.deleteDom.setLines(lines).updatePosition().show();
|
|
199
|
+
lines.forEach((line) => this.highlightLine(line));
|
|
200
|
+
}
|
|
201
|
+
lineRemoved = (line) => {
|
|
202
|
+
this.removeLine(line);
|
|
203
|
+
// TODO: 这个逻辑不太好,改进一下
|
|
204
|
+
this.hook.emit('selectedChange', this.model.lines.filter((line) => line.selected).map((line) => line.toCompletelyJson()));
|
|
205
|
+
};
|
|
206
|
+
onCameraUpdate = () => {
|
|
207
|
+
this.updateDistanceUI();
|
|
208
|
+
this.highlightedLines.length > 0 && this.deleteDom.updatePosition();
|
|
209
|
+
};
|
|
210
|
+
deleteDomClickCallback = () => {
|
|
211
|
+
const firstLine = this.highlightedLines[0];
|
|
212
|
+
// TODO: 优化折线的删除逻辑
|
|
213
|
+
firstLine
|
|
214
|
+
.getPolyline()
|
|
215
|
+
.lines.slice()
|
|
216
|
+
.reverse()
|
|
217
|
+
.forEach((line) => this.model.removeLine(line));
|
|
218
|
+
this.highlightedLines = [];
|
|
219
|
+
// this.clearHighlightLines()
|
|
220
|
+
// this.highlightedLines.forEach((line) => this.model.removeLine(line))
|
|
221
|
+
this.deleteDom.setLines([]).hide();
|
|
222
|
+
};
|
|
223
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { PluginData } from '../typings/data';
|
|
2
|
+
import { Five, Subscribe } from '@realsee/five';
|
|
3
|
+
import { PluginEvent } from '../typings/event.type';
|
|
4
|
+
import { UserDistanceItem } from '../utils/distanceDom';
|
|
5
|
+
export declare type Mode = 'Watch' | 'Edit';
|
|
6
|
+
export interface MeasurePluginParameter {
|
|
7
|
+
useUIController?: boolean;
|
|
8
|
+
useGuideController?: boolean;
|
|
9
|
+
userDistanceItemCreator?: () => UserDistanceItem;
|
|
10
|
+
}
|
|
11
|
+
export default class MeasureController {
|
|
12
|
+
hasOpen: boolean;
|
|
13
|
+
hook: Subscribe<PluginEvent>;
|
|
14
|
+
private five;
|
|
15
|
+
private model;
|
|
16
|
+
private group;
|
|
17
|
+
private magnifier;
|
|
18
|
+
private fiveHelper;
|
|
19
|
+
private useUIController?;
|
|
20
|
+
private params;
|
|
21
|
+
private useGuideController?;
|
|
22
|
+
private container;
|
|
23
|
+
private shortcutKeyController?;
|
|
24
|
+
private controller;
|
|
25
|
+
private controllerParams;
|
|
26
|
+
constructor(five: Five, params: MeasurePluginParameter);
|
|
27
|
+
appendTo(parent: HTMLElement): void;
|
|
28
|
+
dispose(): void;
|
|
29
|
+
/** 加载数据
|
|
30
|
+
* @description 数据加载时会覆盖当前已保存的数据
|
|
31
|
+
* @description 如果当前正在编辑中,会自动退出并保存编辑
|
|
32
|
+
*/
|
|
33
|
+
load(data: PluginData): void;
|
|
34
|
+
/** 插件功能入口
|
|
35
|
+
* @description 会隐藏鼠标的默认聚焦环
|
|
36
|
+
* @description 会隐藏当前 VR 内的点位展示
|
|
37
|
+
*/
|
|
38
|
+
enable(): void;
|
|
39
|
+
/** 关闭插件功能
|
|
40
|
+
* @description 清除标尺线条
|
|
41
|
+
* @description 还原点位展示和默认鼠标 UI
|
|
42
|
+
*/
|
|
43
|
+
disable(): void;
|
|
44
|
+
getCurrentMode: () => Mode | null;
|
|
45
|
+
/** 变更场景
|
|
46
|
+
* @description 如果从编辑场景改变到观看场景,不会自动保存,默认丢弃所有改动
|
|
47
|
+
*/
|
|
48
|
+
changeMode: (mode: Mode) => void;
|
|
49
|
+
/** 撤销编辑 */
|
|
50
|
+
revoke(): void;
|
|
51
|
+
removeLineByID(lineID: string): void;
|
|
52
|
+
/**
|
|
53
|
+
* 高亮当前线段
|
|
54
|
+
*/
|
|
55
|
+
highlightLine(lineID: string): boolean;
|
|
56
|
+
clearHighlightLines(): boolean;
|
|
57
|
+
/** 保存编辑 */
|
|
58
|
+
save(): this;
|
|
59
|
+
/** 把当前以保存的数据转换成 JSON 对象 */
|
|
60
|
+
toJson(): PluginData;
|
|
61
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import Magnifier from '../Modules/Magnifier';
|
|
2
|
+
import FiveHelper from '../Modules/FiveHelper';
|
|
3
|
+
import EditController from './EditController';
|
|
4
|
+
import WatchController from './WatchController';
|
|
5
|
+
import { Group } from 'three';
|
|
6
|
+
import { Model } from '../Model';
|
|
7
|
+
import { Subscribe } from '@realsee/five';
|
|
8
|
+
import { getMouseGroup } from '../utils/mouseGroup';
|
|
9
|
+
import { UIController } from '../Modules/UIController';
|
|
10
|
+
import { GuideController } from '../Modules/GuideController';
|
|
11
|
+
import { ShortcutKeyController } from './ShortcutKeyController';
|
|
12
|
+
export default class MeasureController {
|
|
13
|
+
hasOpen = false;
|
|
14
|
+
hook = new Subscribe();
|
|
15
|
+
five;
|
|
16
|
+
model;
|
|
17
|
+
group;
|
|
18
|
+
magnifier;
|
|
19
|
+
fiveHelper;
|
|
20
|
+
useUIController;
|
|
21
|
+
params;
|
|
22
|
+
useGuideController;
|
|
23
|
+
container = document.createElement('div');
|
|
24
|
+
shortcutKeyController;
|
|
25
|
+
controller = null;
|
|
26
|
+
controllerParams;
|
|
27
|
+
constructor(five, params) {
|
|
28
|
+
this.five = five;
|
|
29
|
+
this.params = params;
|
|
30
|
+
this.model = new Model({ userDistanceItemCreator: this.params.userDistanceItemCreator });
|
|
31
|
+
this.fiveHelper = new FiveHelper(five);
|
|
32
|
+
// magnifier
|
|
33
|
+
const magnifierSize = 190;
|
|
34
|
+
const magnifierScale = 2;
|
|
35
|
+
this.magnifier = new Magnifier(five, { scale: magnifierScale, width: magnifierSize, height: magnifierSize });
|
|
36
|
+
// ==================== Group ====================
|
|
37
|
+
this.group = new Group();
|
|
38
|
+
this.group.name = 'plugin-measure-group';
|
|
39
|
+
this.container.classList.add('five-plugin-measure-container');
|
|
40
|
+
this.container.style.position = 'relative';
|
|
41
|
+
this.container.style.pointerEvents = 'none';
|
|
42
|
+
this.container.style.width = '100%';
|
|
43
|
+
this.container.style.height = '100%';
|
|
44
|
+
this.container.style.background = 'rgba(0, 0, 0, 0.15)';
|
|
45
|
+
this.controllerParams = {
|
|
46
|
+
five: this.five,
|
|
47
|
+
hook: this.hook,
|
|
48
|
+
group: this.group,
|
|
49
|
+
model: this.model,
|
|
50
|
+
magnifier: this.magnifier,
|
|
51
|
+
container: this.container,
|
|
52
|
+
fiveHelper: this.fiveHelper,
|
|
53
|
+
mouseGroup: getMouseGroup(),
|
|
54
|
+
userDistanceItemCreator: this.params.userDistanceItemCreator,
|
|
55
|
+
};
|
|
56
|
+
if (this.params.useUIController !== false)
|
|
57
|
+
this.useUIController = new UIController(this, this.controllerParams);
|
|
58
|
+
if (this.params.useUIController !== false)
|
|
59
|
+
this.useGuideController = new GuideController(this, this.controllerParams);
|
|
60
|
+
}
|
|
61
|
+
appendTo(parent) {
|
|
62
|
+
parent.append(this.container);
|
|
63
|
+
}
|
|
64
|
+
dispose() {
|
|
65
|
+
this.disable();
|
|
66
|
+
this.useUIController?.dispose();
|
|
67
|
+
this.magnifier?.dispose();
|
|
68
|
+
this.five.needsRender = true;
|
|
69
|
+
}
|
|
70
|
+
/** 加载数据
|
|
71
|
+
* @description 数据加载时会覆盖当前已保存的数据
|
|
72
|
+
* @description 如果当前正在编辑中,会自动退出并保存编辑
|
|
73
|
+
*/
|
|
74
|
+
load(data) {
|
|
75
|
+
this.model.parse(data);
|
|
76
|
+
this.save();
|
|
77
|
+
}
|
|
78
|
+
/** 插件功能入口
|
|
79
|
+
* @description 会隐藏鼠标的默认聚焦环
|
|
80
|
+
* @description 会隐藏当前 VR 内的点位展示
|
|
81
|
+
*/
|
|
82
|
+
enable() {
|
|
83
|
+
if (this.hasOpen)
|
|
84
|
+
return;
|
|
85
|
+
this.hasOpen = true;
|
|
86
|
+
this.five.scene.add(this.group);
|
|
87
|
+
// 隐藏点位和鼠标聚焦环
|
|
88
|
+
this.five.helperVisible = false;
|
|
89
|
+
this.controller = new WatchController(this.controllerParams);
|
|
90
|
+
this.useUIController?.show();
|
|
91
|
+
this.useGuideController?.show();
|
|
92
|
+
this.shortcutKeyController = new ShortcutKeyController(this, this.five);
|
|
93
|
+
this.hook.emit('enable', true);
|
|
94
|
+
}
|
|
95
|
+
/** 关闭插件功能
|
|
96
|
+
* @description 清除标尺线条
|
|
97
|
+
* @description 还原点位展示和默认鼠标 UI
|
|
98
|
+
*/
|
|
99
|
+
disable() {
|
|
100
|
+
this.hasOpen = false;
|
|
101
|
+
// 展示点位和鼠标聚焦环
|
|
102
|
+
this.controller?.dispose();
|
|
103
|
+
this.useUIController?.hide();
|
|
104
|
+
this.useGuideController?.hide();
|
|
105
|
+
this.shortcutKeyController?.dispose();
|
|
106
|
+
this.controller = null;
|
|
107
|
+
this.five.helperVisible = true;
|
|
108
|
+
this.five.scene.remove(this.group);
|
|
109
|
+
this.five.needsRender = true;
|
|
110
|
+
this.hook.emit('disable', true);
|
|
111
|
+
}
|
|
112
|
+
getCurrentMode = () => {
|
|
113
|
+
if (this.controller instanceof EditController)
|
|
114
|
+
return 'Edit';
|
|
115
|
+
if (this.controller instanceof WatchController)
|
|
116
|
+
return 'Watch';
|
|
117
|
+
return null;
|
|
118
|
+
};
|
|
119
|
+
/** 变更场景
|
|
120
|
+
* @description 如果从编辑场景改变到观看场景,不会自动保存,默认丢弃所有改动
|
|
121
|
+
*/
|
|
122
|
+
changeMode = (mode) => {
|
|
123
|
+
if (!this.hasOpen)
|
|
124
|
+
return;
|
|
125
|
+
if (this.getCurrentMode() === mode)
|
|
126
|
+
return;
|
|
127
|
+
this.controller?.dispose();
|
|
128
|
+
const controllerMap = {
|
|
129
|
+
Watch: WatchController,
|
|
130
|
+
Edit: EditController,
|
|
131
|
+
};
|
|
132
|
+
if (!controllerMap[mode])
|
|
133
|
+
throw new Error('不存在的 Mode');
|
|
134
|
+
this.controller = new controllerMap[mode](this.controllerParams);
|
|
135
|
+
this.hook.emit('modeChange', mode);
|
|
136
|
+
};
|
|
137
|
+
/** 撤销编辑 */
|
|
138
|
+
revoke() {
|
|
139
|
+
if (!(this.controller instanceof EditController))
|
|
140
|
+
return;
|
|
141
|
+
this.controller.revoke();
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
removeLineByID(lineID) {
|
|
145
|
+
this.model.removeLineByID(lineID);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* 高亮当前线段
|
|
149
|
+
*/
|
|
150
|
+
highlightLine(lineID) {
|
|
151
|
+
if (this.getCurrentMode() !== 'Watch')
|
|
152
|
+
return false;
|
|
153
|
+
const line = this.model.getLineByID(lineID);
|
|
154
|
+
if (!line)
|
|
155
|
+
return false;
|
|
156
|
+
this.controller.highlightLine(line);
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
clearHighlightLines() {
|
|
160
|
+
if (this.getCurrentMode() !== 'Watch')
|
|
161
|
+
return false;
|
|
162
|
+
this.controller.clearHighlightLines();
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
/** 保存编辑 */
|
|
166
|
+
save() {
|
|
167
|
+
if (!(this.controller instanceof EditController))
|
|
168
|
+
return this;
|
|
169
|
+
this.model.mergeModel(this.controller.model);
|
|
170
|
+
return this;
|
|
171
|
+
}
|
|
172
|
+
/** 把当前以保存的数据转换成 JSON 对象 */
|
|
173
|
+
toJson() {
|
|
174
|
+
return this.model.toJson();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import Line from './line';
|
|
2
|
+
import Point from './point';
|
|
3
|
+
import { Subscribe, SubscribeEventMap } from '@realsee/five';
|
|
4
|
+
import { PluginData } from '../typings/data';
|
|
5
|
+
import { Polyline } from './polyline';
|
|
6
|
+
import { UserDistanceItem } from '../utils/distanceDom';
|
|
7
|
+
interface IModelEvent extends SubscribeEventMap {
|
|
8
|
+
/** 新增线条
|
|
9
|
+
* @param line 被创建的线条
|
|
10
|
+
*/
|
|
11
|
+
lineAdded: (line: Line) => void;
|
|
12
|
+
/** 删除线条
|
|
13
|
+
* @param lines 被删除的线条数组
|
|
14
|
+
*/
|
|
15
|
+
lineRemoved: (lines: Line) => void;
|
|
16
|
+
}
|
|
17
|
+
export interface ModelParams {
|
|
18
|
+
userDistanceItemCreator?: () => UserDistanceItem;
|
|
19
|
+
}
|
|
20
|
+
export declare class Model {
|
|
21
|
+
readonly lines: Line[];
|
|
22
|
+
readonly points: Point[];
|
|
23
|
+
readonly params: ModelParams;
|
|
24
|
+
readonly polylines: Polyline[];
|
|
25
|
+
readonly hook: Subscribe<IModelEvent>;
|
|
26
|
+
constructor(params: ModelParams);
|
|
27
|
+
addLine(line: Line): this;
|
|
28
|
+
removeLineByID(lineID: string): void;
|
|
29
|
+
removeLine(line: Line): this;
|
|
30
|
+
mergeModel(model: Model): void;
|
|
31
|
+
includes(item: Point | Line): boolean;
|
|
32
|
+
getPointByID(id: string): Point;
|
|
33
|
+
getLineByID(id: string): Line;
|
|
34
|
+
getClosestPoint(): void;
|
|
35
|
+
parse(data: PluginData): this;
|
|
36
|
+
toJson(): PluginData;
|
|
37
|
+
}
|
|
38
|
+
export {};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import Line from './line';
|
|
2
|
+
import Point from './point';
|
|
3
|
+
import { Subscribe } from '@realsee/five';
|
|
4
|
+
import removeArrayItem from '../../shared-utils/removeArrayItem';
|
|
5
|
+
import { Polyline } from './polyline';
|
|
6
|
+
export class Model {
|
|
7
|
+
lines = [];
|
|
8
|
+
points = [];
|
|
9
|
+
params;
|
|
10
|
+
polylines = [];
|
|
11
|
+
hook = new Subscribe();
|
|
12
|
+
constructor(params) {
|
|
13
|
+
this.params = params;
|
|
14
|
+
}
|
|
15
|
+
addLine(line) {
|
|
16
|
+
const [startPoint, endPoint] = line.points;
|
|
17
|
+
if (!startPoint || !endPoint)
|
|
18
|
+
return this;
|
|
19
|
+
if (!this.includes(startPoint))
|
|
20
|
+
this.points.push(startPoint);
|
|
21
|
+
if (!this.includes(endPoint))
|
|
22
|
+
this.points.push(endPoint);
|
|
23
|
+
this.lines.push(line);
|
|
24
|
+
const lastPolyline = this.polylines.find((polyline) => !!polyline.getNext(line));
|
|
25
|
+
if (lastPolyline) {
|
|
26
|
+
lastPolyline.push(line);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
const polyline = new Polyline();
|
|
30
|
+
polyline.push(line);
|
|
31
|
+
this.polylines.push(polyline);
|
|
32
|
+
}
|
|
33
|
+
this.hook.emit('lineAdded', line);
|
|
34
|
+
}
|
|
35
|
+
removeLineByID(lineID) {
|
|
36
|
+
const line = this.getLineByID(lineID);
|
|
37
|
+
if (line)
|
|
38
|
+
this.removeLine(line);
|
|
39
|
+
}
|
|
40
|
+
removeLine(line) {
|
|
41
|
+
// handle polyline
|
|
42
|
+
const polyline = this.polylines.find((polyline) => polyline.lines.includes(line));
|
|
43
|
+
if (polyline) {
|
|
44
|
+
if (polyline.lines.slice(-1)[0] !== line)
|
|
45
|
+
throw new Error('不能从中间删除线段');
|
|
46
|
+
polyline.pop();
|
|
47
|
+
if (polyline.lines.length === 0)
|
|
48
|
+
removeArrayItem(polyline, this.polylines);
|
|
49
|
+
}
|
|
50
|
+
const [point, anotherPoint] = line.points;
|
|
51
|
+
if (point.lines.length === 0)
|
|
52
|
+
removeArrayItem(point, this.points);
|
|
53
|
+
if (anotherPoint.lines.length === 0)
|
|
54
|
+
removeArrayItem(anotherPoint, this.points);
|
|
55
|
+
removeArrayItem(line, this.lines);
|
|
56
|
+
line.clear();
|
|
57
|
+
this.hook.emit('lineRemoved', line);
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
mergeModel(model) {
|
|
61
|
+
model.lines.forEach((line) => this.addLine(line));
|
|
62
|
+
}
|
|
63
|
+
includes(item) {
|
|
64
|
+
if (item.type === 'point') {
|
|
65
|
+
return this.points.find(({ id }) => id === item.id) !== undefined;
|
|
66
|
+
}
|
|
67
|
+
else if (item.type === 'line') {
|
|
68
|
+
return this.lines.find(({ id }) => id === item.id) !== undefined;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
getPointByID(id) {
|
|
72
|
+
return this.points.find(({ id: _id }) => _id === id);
|
|
73
|
+
}
|
|
74
|
+
getLineByID(id) {
|
|
75
|
+
return this.lines.find(({ id: _id }) => _id === id);
|
|
76
|
+
}
|
|
77
|
+
getClosestPoint() { }
|
|
78
|
+
parse(data) {
|
|
79
|
+
const points = data.points.map(({ id, position }) => {
|
|
80
|
+
const point = new Point(position);
|
|
81
|
+
point.id = id;
|
|
82
|
+
return point;
|
|
83
|
+
});
|
|
84
|
+
const lines = data.lines.map((data) => {
|
|
85
|
+
const [pointID, anotherPointID] = data.points;
|
|
86
|
+
const point = this.getPointByID.call({ points }, pointID);
|
|
87
|
+
const anotherPoint = this.getPointByID.call({ points }, anotherPointID);
|
|
88
|
+
if (!point || !anotherPoint)
|
|
89
|
+
throw new Error('线上的点不存在');
|
|
90
|
+
const line = new Line(point, anotherPoint, this);
|
|
91
|
+
line.id = data.id;
|
|
92
|
+
return line;
|
|
93
|
+
});
|
|
94
|
+
const polylines = data.polylines.map((polylineJson) => {
|
|
95
|
+
const polyline = new Polyline();
|
|
96
|
+
polylineJson.lines.forEach((lineID) => {
|
|
97
|
+
const line = lines.find(({ id }) => id === lineID);
|
|
98
|
+
line && polyline.push(line);
|
|
99
|
+
});
|
|
100
|
+
return polyline;
|
|
101
|
+
});
|
|
102
|
+
this.points.splice(0, this.points.length, ...points);
|
|
103
|
+
this.lines.splice(0, this.lines.length, ...lines);
|
|
104
|
+
this.polylines.splice(0, this.polylines.length, ...polylines);
|
|
105
|
+
return this;
|
|
106
|
+
}
|
|
107
|
+
toJson() {
|
|
108
|
+
return {
|
|
109
|
+
points: this.points.map((point) => point.toJson()),
|
|
110
|
+
lines: this.lines.map((line) => line.toJson()),
|
|
111
|
+
polylines: this.polylines.map((polyline) => polyline.toJson()).filter((polylines) => polylines.lines.length > 0),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Model } from '.';
|
|
2
|
+
import Point from './point';
|
|
3
|
+
import { LineJson } from '..';
|
|
4
|
+
import { Polyline } from './polyline';
|
|
5
|
+
import { LineCompletelyJson } from '../typings/data';
|
|
6
|
+
import { Line as FiveLine } from '@realsee/five/line';
|
|
7
|
+
import { Subscribe, SubscribeEventMap } from '@realsee/five';
|
|
8
|
+
import { DistanceItem } from '../utils/distanceDom';
|
|
9
|
+
export interface ILineHook extends SubscribeEventMap {
|
|
10
|
+
selected: (line: Line) => void;
|
|
11
|
+
}
|
|
12
|
+
export default class Line {
|
|
13
|
+
id: string;
|
|
14
|
+
selected: boolean;
|
|
15
|
+
readonly type = "line";
|
|
16
|
+
readonly model: Model;
|
|
17
|
+
readonly mesh: FiveLine;
|
|
18
|
+
readonly points: Point[];
|
|
19
|
+
readonly lightMesh: FiveLine;
|
|
20
|
+
readonly hook: Subscribe<ILineHook>;
|
|
21
|
+
readonly distanceItem: DistanceItem;
|
|
22
|
+
private polyline?;
|
|
23
|
+
constructor(point: Point, anotherPoint: Point, model: Model);
|
|
24
|
+
clear(): void;
|
|
25
|
+
getPolyline(): Polyline;
|
|
26
|
+
findAnotherPoint(point: Point): Point;
|
|
27
|
+
toJson(): LineJson;
|
|
28
|
+
toCompletelyJson(): LineCompletelyJson;
|
|
29
|
+
clone(): Line;
|
|
30
|
+
}
|