@realsee/dnalogel 2.0.0-alpha.3 → 2.0.0-alpha.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/TERMS.txt +56 -0
- package/dist/dnalogel.cjs.js +2 -2
- package/dist/dnalogel.es.js +697 -62
- package/dist/dnalogel.umd.js +2 -2
- package/dist/style.css +1 -1
- package/docs/.nojekyll +1 -0
- package/docs/assets/highlight.css +29 -0
- package/docs/assets/icons.css +1043 -0
- package/docs/assets/icons.png +0 -0
- package/docs/assets/icons@2x.png +0 -0
- package/docs/assets/main.js +52 -0
- package/docs/assets/search.js +1 -0
- package/docs/assets/style.css +1413 -0
- package/docs/assets/widgets.png +0 -0
- package/docs/assets/widgets@2x.png +0 -0
- package/docs/classes/ModelRoomLabelController.html +20 -0
- package/docs/enums/FLOOR_PLAN_ATTACHED_TO.html +1 -0
- package/docs/enums/ModelFloorplanErrorType.html +1 -0
- package/docs/index.html +10 -0
- package/docs/interfaces/ModelChassisCompassPluginData.html +1 -0
- package/docs/interfaces/ModelChassisCompassPluginExportType.html +1 -0
- package/docs/interfaces/ModelChassisCompassPluginParameterType.html +1 -0
- package/docs/interfaces/ModelEntryDoorGuidePluginData.html +1 -0
- package/docs/interfaces/ModelEntryDoorGuidePluginExportType.html +1 -0
- package/docs/interfaces/ModelFloorplanParameterType.html +1 -0
- package/docs/interfaces/ModelFloorplanViewEvent.html +5 -0
- package/docs/interfaces/ModelRoomLabelPluginData.html +1 -0
- package/docs/interfaces/PanoCompassPluginData.html +1 -0
- package/docs/interfaces/PanoCompassPluginParameterType.html +1 -0
- package/docs/interfaces/PanoCursorRaycasterPluginExportType.html +11 -0
- package/docs/interfaces/PanoRulerPluginExportType.html +1 -0
- package/docs/interfaces/PanoRulerPluginOptions.html +1 -0
- package/docs/interfaces/PanoRulerPluginParameterType.html +1 -0
- package/docs/interfaces/RoomLabel.html +13 -0
- package/docs/interfaces/TopviewFloorplanPluginParameterType.html +1 -0
- package/docs/modules.html +26 -0
- package/libs/CSS3DRenderPlugin/index.js +1 -1
- package/libs/PanoCompassPlugin/Assets/roomInfoIcon.d.ts +1 -0
- package/libs/PanoCompassPlugin/Assets/roomInfoIcon.js +1 -0
- package/libs/PanoCompassPlugin/getRoomInfoInstance.d.ts +8 -0
- package/libs/PanoCompassPlugin/getRoomInfoInstance.js +48 -0
- package/libs/PanoCompassPlugin/index.d.ts +29 -3
- package/libs/PanoCompassPlugin/index.js +106 -36
- package/libs/PanoRulerPlugin/index.d.ts +31 -0
- package/libs/PanoRulerPlugin/index.js +398 -0
- package/libs/PanoRulerPlugin/style.d.ts +2 -0
- package/libs/PanoRulerPlugin/style.js +88 -0
- package/libs/PanoRulerPlugin/typings.d.ts +21 -0
- package/libs/PanoRulerPlugin/typings.js +1 -0
- package/libs/floorplan/ModelFloorplanPlugin/Components/Camera.svelte +3 -3
- package/libs/floorplan/ModelFloorplanPlugin/Components/Camera.svelte.map +1 -1
- package/libs/floorplan/ModelFloorplanPlugin/Controller.d.ts +2 -0
- package/libs/floorplan/ModelFloorplanPlugin/Controller.js +3 -1
- package/libs/floorplan/TopviewFloorplanPlugin/Controller.d.ts +2 -0
- package/libs/floorplan/TopviewFloorplanPlugin/Controller.js +4 -3
- package/libs/floorplan/constant.d.ts +5 -0
- package/libs/floorplan/constant.js +6 -0
- package/libs/floorplan/typings/floorplanServerData.d.ts +4 -0
- package/libs/index.d.ts +4 -1
- package/libs/index.js +4 -1
- package/libs/shared-utils/getPxmm.d.ts +6 -1
- package/libs/shared-utils/getPxmm.js +26 -12
- package/package.json +4 -2
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
import { Five } from '@realsee/five';
|
|
2
|
+
import { Raycaster, Vector3 } from 'three';
|
|
3
|
+
import { intersectionOfLine } from '../shared-utils/math/planimetry';
|
|
4
|
+
import { nextFrame } from '../shared-utils/nextFrame';
|
|
5
|
+
import throttle from '../shared-utils/throttle';
|
|
6
|
+
import PanoRulerStyle from './style';
|
|
7
|
+
const getDistance = (p1, p2) => {
|
|
8
|
+
return Math.sqrt(Math.pow(p1.z - p2.z, 2) + Math.pow(p1.x - p2.x, 2));
|
|
9
|
+
};
|
|
10
|
+
const getRoomHeightInfo = (roomInfo, five) => {
|
|
11
|
+
const roomHeightInfo = {};
|
|
12
|
+
const raycaster = new Raycaster();
|
|
13
|
+
const work = five.work;
|
|
14
|
+
if (!work)
|
|
15
|
+
return roomHeightInfo;
|
|
16
|
+
const observers = roomInfo.observers;
|
|
17
|
+
work.observers.forEach((observer, index) => {
|
|
18
|
+
const { standingPosition: position } = observer;
|
|
19
|
+
const point = new Vector3(position.x, position.y, position.z);
|
|
20
|
+
raycaster.set(point, new Vector3(0, 1, 0));
|
|
21
|
+
const [intersection] = five.model.intersectRaycaster(raycaster);
|
|
22
|
+
// 虚景 VR 没有天花板碰撞射线无相交,默认固定层高2.7m
|
|
23
|
+
const verticalY = intersection ? intersection.point.y : 2.7;
|
|
24
|
+
const id = observers[index];
|
|
25
|
+
if (!id)
|
|
26
|
+
return roomHeightInfo;
|
|
27
|
+
const observerRoomName = roomInfo.rooms[id].name;
|
|
28
|
+
if (roomHeightInfo[observerRoomName] === undefined) {
|
|
29
|
+
roomHeightInfo[observerRoomName] = {
|
|
30
|
+
__roof: [verticalY],
|
|
31
|
+
__floor: [point.y],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
roomHeightInfo[observerRoomName].__roof.push(verticalY);
|
|
36
|
+
roomHeightInfo[observerRoomName].__floor.push(point.y);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
// 地板高度求中位数,屋顶高度取最高
|
|
40
|
+
for (const roomName in roomHeightInfo) {
|
|
41
|
+
const room = roomHeightInfo[roomName];
|
|
42
|
+
room.__roof.sort();
|
|
43
|
+
room.__floor.sort();
|
|
44
|
+
room.floor = room.__floor[~~(room.__floor.length / 2)];
|
|
45
|
+
room.roof = room.__roof[room.__roof.length - 1];
|
|
46
|
+
}
|
|
47
|
+
return roomHeightInfo;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* 全景标尺插件
|
|
51
|
+
*/
|
|
52
|
+
export const PanoRulerPlugin = (five, params) => {
|
|
53
|
+
const state = {
|
|
54
|
+
enable: false,
|
|
55
|
+
loaded: false,
|
|
56
|
+
options: params.options || {
|
|
57
|
+
distanceText: (distance) => `${distance.toFixed(1)}m`,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
const tpl = (name, distance) => {
|
|
61
|
+
const domStr = `
|
|
62
|
+
<div class="PanoRulerPlugin-rule-line">
|
|
63
|
+
<em></em>
|
|
64
|
+
<div class="PanoRulerPlugin-rule-label">
|
|
65
|
+
<div class="PanoRulerPlugin-rule-label-name">${state.options.distanceText(distance)}</div>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
`;
|
|
69
|
+
const res = document.createElement('div');
|
|
70
|
+
res.setAttribute('class', 'PanoRulerPlugin-rule');
|
|
71
|
+
res.setAttribute('data-name', name);
|
|
72
|
+
res.setAttribute('style', 'display: none');
|
|
73
|
+
res.innerHTML = domStr;
|
|
74
|
+
return res;
|
|
75
|
+
};
|
|
76
|
+
const $rule = document.createElement('div');
|
|
77
|
+
$rule.setAttribute('style', `position: absolute;pointer-events: none;width: 100%;height: 100%;left: 0;top: 0;overflow: hidden;`);
|
|
78
|
+
// 添加标尺样式
|
|
79
|
+
const style = document.createElement('div');
|
|
80
|
+
style.innerHTML = PanoRulerStyle;
|
|
81
|
+
$rule.appendChild(style);
|
|
82
|
+
const __rule = {};
|
|
83
|
+
const _load = (roomInfo, roomRules) => {
|
|
84
|
+
if (state.loaded) {
|
|
85
|
+
throw new Error('标尺被重复初始化!');
|
|
86
|
+
}
|
|
87
|
+
const roomHeightInfo = getRoomHeightInfo(roomInfo, five);
|
|
88
|
+
const work = five.work;
|
|
89
|
+
if (!work)
|
|
90
|
+
return false;
|
|
91
|
+
for (const obKey in roomRules) {
|
|
92
|
+
const points = roomRules[obKey];
|
|
93
|
+
const { standingPosition: defaultPosition } = work.observers[0];
|
|
94
|
+
const roomPoints = points.map(({ x, z, observers }) => {
|
|
95
|
+
const pointRoomName = observers.length > 0 ? roomInfo.rooms[roomInfo.observers[observers[0]]].name : '';
|
|
96
|
+
const medianFloorHeight = roomHeightInfo[pointRoomName] ? roomHeightInfo[pointRoomName].floor : null;
|
|
97
|
+
let minDistance = Infinity;
|
|
98
|
+
let nearestPoint = {
|
|
99
|
+
index: 0,
|
|
100
|
+
x: defaultPosition.x,
|
|
101
|
+
y: defaultPosition.y,
|
|
102
|
+
z: defaultPosition.z,
|
|
103
|
+
};
|
|
104
|
+
observers.forEach((index) => {
|
|
105
|
+
if (!work.observers[index])
|
|
106
|
+
return;
|
|
107
|
+
const { standingPosition: position } = work.observers[index];
|
|
108
|
+
const observerPoint = {
|
|
109
|
+
index,
|
|
110
|
+
x: position.x,
|
|
111
|
+
y: position.y,
|
|
112
|
+
z: position.z,
|
|
113
|
+
};
|
|
114
|
+
const distance = getDistance({ x, z }, observerPoint);
|
|
115
|
+
if (medianFloorHeight) {
|
|
116
|
+
// 取最近点时排除高度与房间平均高度差大于0.3米的点
|
|
117
|
+
if (distance < minDistance && Math.abs(observerPoint.y - medianFloorHeight) < 0.3) {
|
|
118
|
+
minDistance = distance;
|
|
119
|
+
nearestPoint = observerPoint;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
if (distance < minDistance) {
|
|
124
|
+
minDistance = distance;
|
|
125
|
+
nearestPoint = observerPoint;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
const _origin = new Vector3(x, nearestPoint.y, z);
|
|
130
|
+
Object.assign(_origin, { observers });
|
|
131
|
+
const verticalY = roomHeightInfo[pointRoomName] ? roomHeightInfo[pointRoomName].roof : null;
|
|
132
|
+
const _vertical = !verticalY ? null : new Vector3(x, verticalY, z);
|
|
133
|
+
if (_vertical)
|
|
134
|
+
Object.assign(_vertical, { observers });
|
|
135
|
+
const origin = _origin;
|
|
136
|
+
const vertical = _vertical;
|
|
137
|
+
return { origin, vertical };
|
|
138
|
+
});
|
|
139
|
+
__rule[obKey] = {
|
|
140
|
+
origins: roomPoints.map((roomPoint) => roomPoint.origin),
|
|
141
|
+
rules: [],
|
|
142
|
+
};
|
|
143
|
+
for (const { origin, vertical } of roomPoints) {
|
|
144
|
+
if (!vertical)
|
|
145
|
+
continue;
|
|
146
|
+
const $element = tpl(obKey, origin.distanceTo(vertical));
|
|
147
|
+
$rule.append($element);
|
|
148
|
+
__rule[obKey].rules.push({ vertical: true, rule: [origin, vertical], $element });
|
|
149
|
+
}
|
|
150
|
+
for (let index = 0; index < roomPoints.length; index++) {
|
|
151
|
+
let nextIndex = index + 1;
|
|
152
|
+
if (nextIndex >= roomPoints.length)
|
|
153
|
+
nextIndex = 0;
|
|
154
|
+
const { origin } = roomPoints[index];
|
|
155
|
+
const { origin: next } = roomPoints[nextIndex];
|
|
156
|
+
const $element = tpl(obKey, origin.distanceTo(next));
|
|
157
|
+
$rule.append($element);
|
|
158
|
+
__rule[obKey].rules.push({ vertical: false, rule: [origin, next], $element });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
state.loaded = true;
|
|
162
|
+
return true;
|
|
163
|
+
};
|
|
164
|
+
const load = async (_roomInfo, _roomRules, options) => {
|
|
165
|
+
const roomInfo = _roomInfo || params.roomInfo;
|
|
166
|
+
const roomRules = _roomRules || params.roomRules;
|
|
167
|
+
if (!roomInfo || !roomRules) {
|
|
168
|
+
throw new Error('标尺数据依赖不齐全!');
|
|
169
|
+
}
|
|
170
|
+
state.options = Object.assign({}, state.options, options || {});
|
|
171
|
+
if (five.model.loaded) {
|
|
172
|
+
return _load(roomInfo, roomRules);
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
return await new Promise((resolve) => five.once('modelLoaded', () => resolve(_load(roomInfo, roomRules))));
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
if (params.roomInfo && params.roomRules) {
|
|
179
|
+
load(params.roomInfo, params.roomRules);
|
|
180
|
+
}
|
|
181
|
+
const getRuleScreenIntr = (startPoint, endPoint, canvasWidth, canvasHeight) => {
|
|
182
|
+
const screenLines = [
|
|
183
|
+
[
|
|
184
|
+
{ x: 0, y: 0 },
|
|
185
|
+
{ x: canvasWidth, y: 0 },
|
|
186
|
+
],
|
|
187
|
+
[
|
|
188
|
+
{ x: 0, y: 0 },
|
|
189
|
+
{ x: 0, y: canvasHeight },
|
|
190
|
+
],
|
|
191
|
+
[
|
|
192
|
+
{ x: canvasWidth, y: 0 },
|
|
193
|
+
{ x: canvasWidth, y: canvasHeight },
|
|
194
|
+
],
|
|
195
|
+
[
|
|
196
|
+
{ x: 0, y: canvasHeight },
|
|
197
|
+
{ x: canvasWidth, y: canvasHeight },
|
|
198
|
+
],
|
|
199
|
+
];
|
|
200
|
+
const intr = [];
|
|
201
|
+
for (let i = 0; i < screenLines.length; i++) {
|
|
202
|
+
const result = intersectionOfLine([startPoint, endPoint], [screenLines[i][0], screenLines[i][1]], true);
|
|
203
|
+
if (result)
|
|
204
|
+
intr.push(result);
|
|
205
|
+
}
|
|
206
|
+
if (intr.length === 0)
|
|
207
|
+
return false;
|
|
208
|
+
else {
|
|
209
|
+
return intr;
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
const freshRule = () => {
|
|
213
|
+
const element = five.getElement()?.parentElement;
|
|
214
|
+
if (!element)
|
|
215
|
+
return;
|
|
216
|
+
if (!state.loaded)
|
|
217
|
+
return;
|
|
218
|
+
if (Object.keys(__rule).length <= 0)
|
|
219
|
+
return;
|
|
220
|
+
const { panoIndex, camera, currentMode } = five;
|
|
221
|
+
if (panoIndex === undefined)
|
|
222
|
+
return;
|
|
223
|
+
let name;
|
|
224
|
+
for (const _name in __rule) {
|
|
225
|
+
if (_name.split(',').indexOf(panoIndex.toString()) >= 0)
|
|
226
|
+
name = _name;
|
|
227
|
+
}
|
|
228
|
+
if (!name)
|
|
229
|
+
return;
|
|
230
|
+
const cameraPosition = camera.position;
|
|
231
|
+
const cameraDirection = camera.getWorldDirection(new Vector3());
|
|
232
|
+
const width = element.clientWidth;
|
|
233
|
+
const height = element.clientHeight;
|
|
234
|
+
// 非全景模式下隐藏所有标尺
|
|
235
|
+
if (currentMode !== Five.Mode.Panorama) {
|
|
236
|
+
for (const _name in __rule) {
|
|
237
|
+
for (const { $element } of __rule[_name].rules) {
|
|
238
|
+
$element.style.display = 'none';
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
// 隐藏非当前点位的标尺
|
|
244
|
+
for (const _name in __rule) {
|
|
245
|
+
for (const { $element } of __rule[_name].rules) {
|
|
246
|
+
$element.style.display = _name === name ? 'block' : 'none';
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
const verticalLines = [];
|
|
250
|
+
// 可以看到的角度最小的拐点
|
|
251
|
+
const [visibleOrigin] = __rule[name].origins
|
|
252
|
+
.slice()
|
|
253
|
+
.filter((point) => point.observers.indexOf(panoIndex) >= 0)
|
|
254
|
+
.sort((pointA, pointB) => {
|
|
255
|
+
const angleA = pointA.clone().setY(0).sub(cameraPosition).normalize().angleTo(cameraDirection.clone().setY(0));
|
|
256
|
+
const angleB = pointB.clone().setY(0).sub(cameraPosition).normalize().angleTo(cameraDirection.clone().setY(0));
|
|
257
|
+
return angleA - angleB;
|
|
258
|
+
});
|
|
259
|
+
const shownNotVerticalRules = [];
|
|
260
|
+
for (const { rule, vertical, $element } of __rule[name].rules) {
|
|
261
|
+
const [start, end] = rule;
|
|
262
|
+
const $line = $element.querySelector('.PanoRulerPlugin-rule-line');
|
|
263
|
+
if (!$line)
|
|
264
|
+
return;
|
|
265
|
+
if (!visibleOrigin) {
|
|
266
|
+
$element.style.display = 'none';
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
if (start !== visibleOrigin && end !== visibleOrigin) {
|
|
270
|
+
$element.style.display = 'none';
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
if (start.distanceTo(end) < 0.5) {
|
|
274
|
+
$element.style.display = 'none';
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
if (start.observers.indexOf(panoIndex) === -1 || end.observers.indexOf(panoIndex) === -1) {
|
|
278
|
+
$element.style.display = 'none';
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
const startAngle = start.clone().sub(cameraPosition).normalize().angleTo(cameraDirection);
|
|
282
|
+
if (startAngle > Math.PI / 2) {
|
|
283
|
+
$element.style.display = 'none';
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
const endAngle = end.clone().sub(cameraPosition).normalize().angleTo(cameraDirection);
|
|
287
|
+
if (endAngle > Math.PI / 2) {
|
|
288
|
+
$element.style.display = 'none';
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
const ruleLength = start.distanceTo(end);
|
|
292
|
+
const midPoint = end.clone().sub(end.clone().sub(start).divide(new Vector3(2, 2, 2)));
|
|
293
|
+
const disFromCameraToMid = midPoint.distanceTo(cameraPosition);
|
|
294
|
+
if (disFromCameraToMid / ruleLength > 8) {
|
|
295
|
+
$element.style.display = 'none';
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
if (!vertical)
|
|
299
|
+
shownNotVerticalRules.push(rule);
|
|
300
|
+
if (vertical)
|
|
301
|
+
verticalLines.push({ $element, startAngle });
|
|
302
|
+
const mouseStart = start.clone().project(camera);
|
|
303
|
+
const startLeft = ((mouseStart.x + 1) / 2) * width;
|
|
304
|
+
const startTop = ((-mouseStart.y + 1) / 2) * height;
|
|
305
|
+
const mouseEnd = end.clone().project(camera);
|
|
306
|
+
const endLeft = ((mouseEnd.x + 1) / 2) * width;
|
|
307
|
+
const endTop = ((-mouseEnd.y + 1) / 2) * height;
|
|
308
|
+
const distance = Math.sqrt(Math.pow(endLeft - startLeft, 2) + Math.pow(endTop - startTop, 2));
|
|
309
|
+
let visibleLength = distance;
|
|
310
|
+
let labelOffset = 50;
|
|
311
|
+
const intr = getRuleScreenIntr({ x: ~~startLeft, y: ~~startTop }, {
|
|
312
|
+
x: ~~endLeft,
|
|
313
|
+
y: ~~endTop
|
|
314
|
+
}, width, height);
|
|
315
|
+
if (intr && intr.length === 1) {
|
|
316
|
+
if (visibleOrigin === start) {
|
|
317
|
+
visibleLength = Math.sqrt(Math.pow(intr[0].x - startLeft, 2) + Math.pow(intr[0].y - startTop, 2));
|
|
318
|
+
labelOffset = (visibleLength / distance) * 50;
|
|
319
|
+
}
|
|
320
|
+
else if (visibleOrigin === end) {
|
|
321
|
+
visibleLength = Math.sqrt(Math.pow(intr[0].x - endLeft, 2) + Math.pow(intr[0].y - endTop, 2));
|
|
322
|
+
labelOffset = 100 - (visibleLength / distance) * 50;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
if (intr && intr.length === 2) {
|
|
326
|
+
const middlePoint = {
|
|
327
|
+
x: (intr[0].x + intr[1].x) / 2,
|
|
328
|
+
y: (intr[0].y + intr[1].y) / 2,
|
|
329
|
+
};
|
|
330
|
+
const distanceFromStart = Math.sqrt(Math.pow(middlePoint.x - startLeft, 2) + Math.pow(middlePoint.y - startTop, 2));
|
|
331
|
+
labelOffset = (distanceFromStart / distance) * 100;
|
|
332
|
+
}
|
|
333
|
+
const rad = Math.PI / 2 - Math.atan2(endLeft - startLeft, startTop - endTop);
|
|
334
|
+
const deg = (rad / Math.PI) * 180;
|
|
335
|
+
const $label = $line.querySelector('.PanoRulerPlugin-rule-label');
|
|
336
|
+
// 线的长度小于标签时或者标签与线两端点重合时隐藏,
|
|
337
|
+
const labelWidth = $label.children[0].clientWidth;
|
|
338
|
+
if (labelWidth >= distance ||
|
|
339
|
+
labelWidth / 2 >= (labelOffset / 100) * distance ||
|
|
340
|
+
labelWidth / 2 >= (1 - labelOffset / 100) * distance) {
|
|
341
|
+
$line.style.display = 'none';
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
$line.style.display = 'block';
|
|
345
|
+
$line.style.width = distance + 'px';
|
|
346
|
+
$line.style.left = startLeft + 'px';
|
|
347
|
+
$line.style.top = startTop + 'px';
|
|
348
|
+
$line.style.transform = `rotate(${-deg}deg)`;
|
|
349
|
+
$label.style.left = `${labelOffset}%`;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
const nextFrameFreshRule = () => nextFrame(freshRule);
|
|
354
|
+
const throttleFreshRule = throttle(freshRule, 80);
|
|
355
|
+
const enable = () => {
|
|
356
|
+
if (!state.loaded)
|
|
357
|
+
return false;
|
|
358
|
+
if (state.enable)
|
|
359
|
+
return true;
|
|
360
|
+
$rule.setAttribute('class', 'PanoRulerPlugin' + (state.options.className ? ' ' + state.options.className : ''));
|
|
361
|
+
five.getElement()?.parentElement?.append($rule);
|
|
362
|
+
freshRule();
|
|
363
|
+
five.on('panoArrived', freshRule);
|
|
364
|
+
five.on('modeChange', freshRule);
|
|
365
|
+
five.on('cameraDirectionUpdate', nextFrameFreshRule);
|
|
366
|
+
five.on('movingToPano', nextFrameFreshRule);
|
|
367
|
+
five.on('mouseWheel', throttleFreshRule);
|
|
368
|
+
five.on('pinchGesture', throttleFreshRule);
|
|
369
|
+
state.enable = true;
|
|
370
|
+
return true;
|
|
371
|
+
};
|
|
372
|
+
const disable = () => {
|
|
373
|
+
if (!state.enable)
|
|
374
|
+
return true;
|
|
375
|
+
five.off('panoArrived', freshRule);
|
|
376
|
+
five.off('modeChange', freshRule);
|
|
377
|
+
five.off('cameraDirectionUpdate', nextFrameFreshRule);
|
|
378
|
+
five.off('movingToPano', nextFrameFreshRule);
|
|
379
|
+
five.off('mouseWheel', throttleFreshRule);
|
|
380
|
+
five.off('pinchGesture', throttleFreshRule);
|
|
381
|
+
if ($rule) {
|
|
382
|
+
$rule.remove();
|
|
383
|
+
}
|
|
384
|
+
state.enable = false;
|
|
385
|
+
return true;
|
|
386
|
+
};
|
|
387
|
+
return {
|
|
388
|
+
enable,
|
|
389
|
+
disable,
|
|
390
|
+
load,
|
|
391
|
+
state,
|
|
392
|
+
};
|
|
393
|
+
};
|
|
394
|
+
export const panoRulerPluginServerParams = {
|
|
395
|
+
name: 'PanoRulerPlugin',
|
|
396
|
+
version: 0,
|
|
397
|
+
};
|
|
398
|
+
export default PanoRulerPlugin;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
declare const PanoRulerStyle = "<style type=\"text/css\">\n.PanoRulerPlugin-rule-line {\n position: absolute;\n transform-origin: left center;\n width: 0;\n height: 0.0625rem;\n}\n\n.PanoRulerPlugin-rule-line::after {\n content: '';\n position: absolute;\n left: -0.125rem;\n top: -0.1rem;\n width: 0.25rem;\n height: 0.25rem;\n border-radius: 50%;\n background: #FFFFFF;\n z-index: 1;\n animation: viewport-rule-point 0.1s 1s;\n animation-fill-mode: both;\n}\n\n.PanoRulerPlugin-rule-line::before {\n content: '';\n position: absolute;\n right: -0.125rem;\n top: -0.1rem;\n width: 0.25rem;\n height: 0.25rem;\n border-radius: 50%;\n background: #FFFFFF;\n animation: viewport-rule-point 0.1s 1.5s;\n animation-fill-mode: both;\n}\n\n.PanoRulerPlugin-rule-line em {\n background: #fff;\n display: block;\n height: 100%;\n animation: viewport-rule-line 0.5s ease 1s;\n animation-fill-mode: both;\n box-shadow: 0 0 0.25rem rgb(0 0 0 / 40%);\n}\n\n.PanoRulerPlugin-rule-label {\n position: absolute;\n width: 0;\n height: 0;\n top: 0.0625rem;\n}\n\n.PanoRulerPlugin-rule-label-name {\n position: absolute;\n padding: 0.1875rem 0.375rem;\n background: rgba(195,195,195,0.30);\n backdrop-filter: blur(0.25rem);\n -webkit-backdrop-filter: blur(0.25rem);\n border-radius: 6.25rem;\n border: 0.0625rem solid rgba(255,255,255,0.6);\n white-space: nowrap;\n overflow: hidden;\n color: #FFFFFF;\n font-weight: 500;\n font-size: 0.75rem;\n line-height: 1;\n -webkit-animation: viewport-rule-label 0.25s ease 1s;\n animation: viewport-rule-label 0.25s ease 1s;\n animation-fill-mode: both;\n box-shadow: inset 0 0 0.625rem 0 rgba(255,255,255,0.30);\n}\n\n@keyframes viewport-rule-line {\n 0% { width: 0% }\n 100% { width: 100% }\n}\n\n@keyframes viewport-rule-label {\n 0% { opacity: 0; transform: scaleX(0); }\n 100% { opacity: 1; transform: translate(-50%, -50%) scaleX(1); }\n}\n\n@keyframes viewport-rule-point {\n 0% { transform: scaleX(0); }\n 100% { transform: scaleX(1); }\n}\n</style>\n";
|
|
2
|
+
export default PanoRulerStyle;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
const PanoRulerStyle = `<style type="text/css">
|
|
2
|
+
.PanoRulerPlugin-rule-line {
|
|
3
|
+
position: absolute;
|
|
4
|
+
transform-origin: left center;
|
|
5
|
+
width: 0;
|
|
6
|
+
height: 0.0625rem;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.PanoRulerPlugin-rule-line::after {
|
|
10
|
+
content: '';
|
|
11
|
+
position: absolute;
|
|
12
|
+
left: -0.125rem;
|
|
13
|
+
top: -0.1rem;
|
|
14
|
+
width: 0.25rem;
|
|
15
|
+
height: 0.25rem;
|
|
16
|
+
border-radius: 50%;
|
|
17
|
+
background: #FFFFFF;
|
|
18
|
+
z-index: 1;
|
|
19
|
+
animation: viewport-rule-point 0.1s 1s;
|
|
20
|
+
animation-fill-mode: both;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.PanoRulerPlugin-rule-line::before {
|
|
24
|
+
content: '';
|
|
25
|
+
position: absolute;
|
|
26
|
+
right: -0.125rem;
|
|
27
|
+
top: -0.1rem;
|
|
28
|
+
width: 0.25rem;
|
|
29
|
+
height: 0.25rem;
|
|
30
|
+
border-radius: 50%;
|
|
31
|
+
background: #FFFFFF;
|
|
32
|
+
animation: viewport-rule-point 0.1s 1.5s;
|
|
33
|
+
animation-fill-mode: both;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.PanoRulerPlugin-rule-line em {
|
|
37
|
+
background: #fff;
|
|
38
|
+
display: block;
|
|
39
|
+
height: 100%;
|
|
40
|
+
animation: viewport-rule-line 0.5s ease 1s;
|
|
41
|
+
animation-fill-mode: both;
|
|
42
|
+
box-shadow: 0 0 0.25rem rgb(0 0 0 / 40%);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.PanoRulerPlugin-rule-label {
|
|
46
|
+
position: absolute;
|
|
47
|
+
width: 0;
|
|
48
|
+
height: 0;
|
|
49
|
+
top: 0.0625rem;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.PanoRulerPlugin-rule-label-name {
|
|
53
|
+
position: absolute;
|
|
54
|
+
padding: 0.1875rem 0.375rem;
|
|
55
|
+
background: rgba(195,195,195,0.30);
|
|
56
|
+
backdrop-filter: blur(0.25rem);
|
|
57
|
+
-webkit-backdrop-filter: blur(0.25rem);
|
|
58
|
+
border-radius: 6.25rem;
|
|
59
|
+
border: 0.0625rem solid rgba(255,255,255,0.6);
|
|
60
|
+
white-space: nowrap;
|
|
61
|
+
overflow: hidden;
|
|
62
|
+
color: #FFFFFF;
|
|
63
|
+
font-weight: 500;
|
|
64
|
+
font-size: 0.75rem;
|
|
65
|
+
line-height: 1;
|
|
66
|
+
-webkit-animation: viewport-rule-label 0.25s ease 1s;
|
|
67
|
+
animation: viewport-rule-label 0.25s ease 1s;
|
|
68
|
+
animation-fill-mode: both;
|
|
69
|
+
box-shadow: inset 0 0 0.625rem 0 rgba(255,255,255,0.30);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@keyframes viewport-rule-line {
|
|
73
|
+
0% { width: 0% }
|
|
74
|
+
100% { width: 100% }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@keyframes viewport-rule-label {
|
|
78
|
+
0% { opacity: 0; transform: scaleX(0); }
|
|
79
|
+
100% { opacity: 1; transform: translate(-50%, -50%) scaleX(1); }
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@keyframes viewport-rule-point {
|
|
83
|
+
0% { transform: scaleX(0); }
|
|
84
|
+
100% { transform: scaleX(1); }
|
|
85
|
+
}
|
|
86
|
+
</style>
|
|
87
|
+
`;
|
|
88
|
+
export default PanoRulerStyle;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface Room {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
localName: string;
|
|
5
|
+
area: number;
|
|
6
|
+
}
|
|
7
|
+
export interface Rooms {
|
|
8
|
+
[key: string]: Room;
|
|
9
|
+
}
|
|
10
|
+
export declare type RoomObservers = string[];
|
|
11
|
+
export interface RoomInfo {
|
|
12
|
+
rooms: Rooms;
|
|
13
|
+
observers: RoomObservers;
|
|
14
|
+
}
|
|
15
|
+
export interface RoomRules {
|
|
16
|
+
[key: string]: {
|
|
17
|
+
x: number;
|
|
18
|
+
z: number;
|
|
19
|
+
observers: number[];
|
|
20
|
+
}[];
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -23,7 +23,10 @@ const domSizeStyle = `${size / 16}rem`;
|
|
|
23
23
|
class="floorplan__camera-rotate"
|
|
24
24
|
style:width="{domSizeStyle}"
|
|
25
25
|
style:height="{domSizeStyle}"
|
|
26
|
+
style:left="{'-' + domSizeStyle}"
|
|
27
|
+
style:top="{'-' + domSizeStyle}"
|
|
26
28
|
style:transform="{`rotate(${rotate}deg)`}"
|
|
29
|
+
style:transform-origin="{`${domSizeStyle} ${domSizeStyle}`}"
|
|
27
30
|
style="background-image: {`url(${cameraImageUrl || CAMERA_IMAGE})`}"
|
|
28
31
|
></div>
|
|
29
32
|
</div>
|
|
@@ -37,9 +40,6 @@ const domSizeStyle = `${size / 16}rem`;
|
|
|
37
40
|
|
|
38
41
|
.floorplan__camera-rotate {
|
|
39
42
|
position: absolute;
|
|
40
|
-
left: 15px;
|
|
41
|
-
top: 15px;
|
|
42
|
-
transform-origin: 15px 15px;
|
|
43
43
|
background-repeat: no-repeat;
|
|
44
44
|
background-size: 100%;
|
|
45
45
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"mappings":"AAAA,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;;;;;;;;;;;;;;;;;;AAuBjB,CAAC,CAAC,MAAM;;AAER,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;AAC7F,EAAE,CAAC;AACH,IAAI,KAAK,CAAC,CAAC,iBAAiB,CAAC,MAAM;AACnC,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC;AAC/B,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC;AAChC,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;AAC7C,IAAI,KAAK,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;AACvE,EAAE,CAAC,CAAC,CAAC,GAAG;AACR,CAAC,CAAC,GAAG;;AAEL,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC;AACrB,EAAE,CAAC,iBAAiB,CAAC,QAAQ,CAAC;AAC9B,IAAI,QAAQ,CAAC,CAAC,QAAQ;AACtB,IAAI,KAAK,CAAC,CAAC,CAAC;AACZ,IAAI,MAAM,CAAC,CAAC,CAAC;AACb,EAAE;;AAEF,EAAE,CAAC,iBAAiB,CAAC,MAAM,CAAC;AAC5B,IAAI,QAAQ,CAAC,CAAC,QAAQ;AACtB,IAAI,
|
|
1
|
+
{"version":3,"mappings":"AAAA,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;;;;;;;;;;;;;;;;;;AAuBjB,CAAC,CAAC,MAAM;;AAER,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;AAC7F,EAAE,CAAC;AACH,IAAI,KAAK,CAAC,CAAC,iBAAiB,CAAC,MAAM;AACnC,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC;AAC/B,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC;AAChC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;AACpC,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;AACnC,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;AAC7C,IAAI,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC;AAC/D,IAAI,KAAK,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;AACvE,EAAE,CAAC,CAAC,CAAC,GAAG;AACR,CAAC,CAAC,GAAG;;AAEL,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC;AACrB,EAAE,CAAC,iBAAiB,CAAC,QAAQ,CAAC;AAC9B,IAAI,QAAQ,CAAC,CAAC,QAAQ;AACtB,IAAI,KAAK,CAAC,CAAC,CAAC;AACZ,IAAI,MAAM,CAAC,CAAC,CAAC;AACb,EAAE;;AAEF,EAAE,CAAC,iBAAiB,CAAC,MAAM,CAAC;AAC5B,IAAI,QAAQ,CAAC,CAAC,QAAQ;AACtB,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,MAAM;AAChC,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC;AACzB,EAAE;AACF,CAAC,CAAC,KAAK;","names":[],"sources":["Camera.svelte"]}
|
|
@@ -2,6 +2,7 @@ import { Five, Subscribe } from '@realsee/five';
|
|
|
2
2
|
import type { FloorplanEventHandlers } from './typings/events.type';
|
|
3
3
|
import type { FloorplanServerData } from '../typings/floorplanServerData';
|
|
4
4
|
import type { FloorplanRoomItem } from '../typings/floorplanData';
|
|
5
|
+
import { FLOOR_PLAN_ATTACHED_TO } from '../constant';
|
|
5
6
|
export interface ModelFloorplanParameterType {
|
|
6
7
|
selector?: string | Element;
|
|
7
8
|
scale?: number;
|
|
@@ -12,6 +13,7 @@ export interface ModelFloorplanParameterType {
|
|
|
12
13
|
ruleLabelsEnable?: boolean;
|
|
13
14
|
roomLabelsEnable?: boolean;
|
|
14
15
|
cameraImageUrl?: string;
|
|
16
|
+
attachedTo?: FLOOR_PLAN_ATTACHED_TO;
|
|
15
17
|
getLabelElement?: (room: FloorplanRoomItem) => Element | null;
|
|
16
18
|
}
|
|
17
19
|
export declare type ModelFloorplanPluginsConfigs = Omit<ModelFloorplanParameterType, 'selector' | 'scale'>;
|
|
@@ -98,7 +98,8 @@ export default class ModelFloorplanPluginController {
|
|
|
98
98
|
const width = max.x - min.x;
|
|
99
99
|
const height = max.y - min.y;
|
|
100
100
|
// 每毫米对应的 px 值
|
|
101
|
-
const
|
|
101
|
+
const options = this.configs.attachedTo ? { attachedTo: this.configs.attachedTo } : undefined;
|
|
102
|
+
const pxmm = getPxmm(this.five, this.wrapper, this.floorIndex, options);
|
|
102
103
|
const _width = Math.ceil(width * pxmm);
|
|
103
104
|
const _height = Math.ceil(height * pxmm);
|
|
104
105
|
if (this.size.width === _width && this.size.height === _height)
|
|
@@ -225,6 +226,7 @@ export default class ModelFloorplanPluginController {
|
|
|
225
226
|
if (shownFloor === null)
|
|
226
227
|
return;
|
|
227
228
|
this.floorIndex = shownFloor;
|
|
229
|
+
this.updateSize();
|
|
228
230
|
this.render();
|
|
229
231
|
};
|
|
230
232
|
handleModelLoaded = () => {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Five } from '@realsee/five';
|
|
2
2
|
import type { FloorplanServerData } from '../typings/floorplanServerData';
|
|
3
3
|
import type { FloorplanRoomItem } from '../typings/floorplanData';
|
|
4
|
+
import { FLOOR_PLAN_ATTACHED_TO } from '../constant';
|
|
4
5
|
export interface TopviewFloorplanPluginParameterType {
|
|
5
6
|
selector?: string | Element;
|
|
6
7
|
scale?: number;
|
|
@@ -12,6 +13,7 @@ export interface TopviewFloorplanPluginParameterType {
|
|
|
12
13
|
ruleLabelsEnable?: boolean;
|
|
13
14
|
roomLabelsEnable?: boolean;
|
|
14
15
|
preventRoomClick?: boolean;
|
|
16
|
+
attachedTo?: FLOOR_PLAN_ATTACHED_TO;
|
|
15
17
|
getLabelElement?: (room: FloorplanRoomItem) => Element | null;
|
|
16
18
|
}
|
|
17
19
|
export declare type TopviewFloorplanPluginReturnType = TopviewFloorplanPluginController;
|
|
@@ -62,6 +62,7 @@ export class TopviewFloorplanPluginController {
|
|
|
62
62
|
if (shownFloor === null)
|
|
63
63
|
return;
|
|
64
64
|
this.floorIndex = shownFloor;
|
|
65
|
+
this.updateSize();
|
|
65
66
|
this.render();
|
|
66
67
|
};
|
|
67
68
|
/** panoIndex 改变时,更新 floorIndex */
|
|
@@ -109,8 +110,8 @@ export class TopviewFloorplanPluginController {
|
|
|
109
110
|
};
|
|
110
111
|
show() {
|
|
111
112
|
this.visible = true;
|
|
112
|
-
this.updateSize();
|
|
113
113
|
this.five.model.show(this.floorIndex);
|
|
114
|
+
this.updateSize();
|
|
114
115
|
const renderDuration = 500;
|
|
115
116
|
const modelOpacity = this.configs.modelOpacity;
|
|
116
117
|
if (typeof modelOpacity === 'number') {
|
|
@@ -142,7 +143,8 @@ export class TopviewFloorplanPluginController {
|
|
|
142
143
|
const width = max.x - min.x;
|
|
143
144
|
const height = max.y - min.y;
|
|
144
145
|
// 每毫米对应的 px 值
|
|
145
|
-
const
|
|
146
|
+
const options = this.configs.attachedTo ? { attachedTo: this.configs.attachedTo } : undefined;
|
|
147
|
+
const pxmm = getPxmm(this.five, this.wrapper, this.floorIndex, options);
|
|
146
148
|
const _width = Math.ceil(width * pxmm);
|
|
147
149
|
const _height = Math.ceil(height * pxmm);
|
|
148
150
|
this.pxmm = pxmm;
|
|
@@ -157,7 +159,6 @@ export class TopviewFloorplanPluginController {
|
|
|
157
159
|
return;
|
|
158
160
|
const { getLabelElement, cameraImageUrl, preventRoomClick, hoverEnable, compassEnable, roomLabelsEnable, ruleLabelsEnable, } = this.configs;
|
|
159
161
|
const handleClick = preventRoomClick ? () => false : undefined;
|
|
160
|
-
console.log('🚀 ~ TopviewFloorplanPluginController ~ handleClick', handleClick);
|
|
161
162
|
const props = {
|
|
162
163
|
five: this.five,
|
|
163
164
|
pxmm: this.pxmm,
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export var FLOOR_PLAN_ATTACHED_TO;
|
|
2
|
+
(function (FLOOR_PLAN_ATTACHED_TO) {
|
|
3
|
+
FLOOR_PLAN_ATTACHED_TO[FLOOR_PLAN_ATTACHED_TO["FLOOR"] = 1] = "FLOOR";
|
|
4
|
+
FLOOR_PLAN_ATTACHED_TO[FLOOR_PLAN_ATTACHED_TO["CEILING"] = 2] = "CEILING";
|
|
5
|
+
FLOOR_PLAN_ATTACHED_TO[FLOOR_PLAN_ATTACHED_TO["BOUNDING_CENTER"] = 3] = "BOUNDING_CENTER";
|
|
6
|
+
})(FLOOR_PLAN_ATTACHED_TO || (FLOOR_PLAN_ATTACHED_TO = {}));
|