@realsee/dnalogel 2.0.0-alpha.3 → 2.0.0-alpha.4
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 +7 -0
- package/TERMS.txt +56 -0
- package/dist/dnalogel.cjs.js +2 -2
- package/dist/dnalogel.es.js +512 -4
- package/dist/dnalogel.umd.js +2 -2
- package/libs/PanoRulerPlugin/index.d.ts +31 -0
- package/libs/PanoRulerPlugin/index.js +397 -0
- package/libs/PanoRulerPlugin/style.d.ts +2 -0
- package/libs/PanoRulerPlugin/style.js +87 -0
- package/libs/PanoRulerPlugin/typings.d.ts +21 -0
- package/libs/PanoRulerPlugin/typings.js +1 -0
- package/libs/index.d.ts +2 -0
- package/libs/index.js +1 -0
- package/package.json +3 -2
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { FivePlugin } from '@realsee/five';
|
|
2
|
+
import { RoomInfo, RoomRules } from './typings';
|
|
3
|
+
export interface PanoRulerPluginOptions {
|
|
4
|
+
distanceText?: (distance: number) => string;
|
|
5
|
+
className?: string;
|
|
6
|
+
}
|
|
7
|
+
interface PanoRulerPluginPluginState {
|
|
8
|
+
enable: boolean;
|
|
9
|
+
loaded: boolean;
|
|
10
|
+
options: PanoRulerPluginOptions;
|
|
11
|
+
}
|
|
12
|
+
export interface PanoRulerPluginParameterType {
|
|
13
|
+
roomInfo?: RoomInfo;
|
|
14
|
+
roomRules?: RoomRules;
|
|
15
|
+
options?: PanoRulerPluginOptions;
|
|
16
|
+
}
|
|
17
|
+
export interface PanoRulerPluginExportType {
|
|
18
|
+
enable: () => void;
|
|
19
|
+
disable: () => void;
|
|
20
|
+
load: (roomInfo?: RoomInfo, roomRules?: RoomRules, options?: PanoRulerPluginOptions) => Promise<boolean>;
|
|
21
|
+
state: PanoRulerPluginPluginState;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* 全景标尺插件
|
|
25
|
+
*/
|
|
26
|
+
export declare const PanoRulerPlugin: FivePlugin<PanoRulerPluginParameterType, PanoRulerPluginExportType>;
|
|
27
|
+
export declare const panoRulerPluginServerParams: {
|
|
28
|
+
name: string;
|
|
29
|
+
version: number;
|
|
30
|
+
};
|
|
31
|
+
export default PanoRulerPlugin;
|
|
@@ -0,0 +1,397 @@
|
|
|
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.width = distance + 'px';
|
|
345
|
+
$line.style.left = startLeft + 'px';
|
|
346
|
+
$line.style.top = startTop + 'px';
|
|
347
|
+
$line.style.transform = `rotate(${-deg}deg)`;
|
|
348
|
+
$label.style.left = `${labelOffset}%`;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
const nextFrameFreshRule = () => nextFrame(freshRule);
|
|
353
|
+
const throttleFreshRule = throttle(freshRule, 80);
|
|
354
|
+
const enable = () => {
|
|
355
|
+
if (!state.loaded)
|
|
356
|
+
return false;
|
|
357
|
+
if (state.enable)
|
|
358
|
+
return true;
|
|
359
|
+
$rule.setAttribute('class', 'PanoRulerPlugin' + (state.options.className ? ' ' + state.options.className : ''));
|
|
360
|
+
five.getElement()?.parentElement?.append($rule);
|
|
361
|
+
freshRule();
|
|
362
|
+
five.on('panoArrived', freshRule);
|
|
363
|
+
five.on('modeChange', freshRule);
|
|
364
|
+
five.on('cameraDirectionUpdate', nextFrameFreshRule);
|
|
365
|
+
five.on('movingToPano', nextFrameFreshRule);
|
|
366
|
+
five.on('mouseWheel', throttleFreshRule);
|
|
367
|
+
five.on('pinchGesture', throttleFreshRule);
|
|
368
|
+
state.enable = true;
|
|
369
|
+
return true;
|
|
370
|
+
};
|
|
371
|
+
const disable = () => {
|
|
372
|
+
if (!state.enable)
|
|
373
|
+
return true;
|
|
374
|
+
five.off('panoArrived', freshRule);
|
|
375
|
+
five.off('modeChange', freshRule);
|
|
376
|
+
five.off('cameraDirectionUpdate', nextFrameFreshRule);
|
|
377
|
+
five.off('movingToPano', nextFrameFreshRule);
|
|
378
|
+
five.off('mouseWheel', throttleFreshRule);
|
|
379
|
+
five.off('pinchGesture', throttleFreshRule);
|
|
380
|
+
if ($rule) {
|
|
381
|
+
$rule.remove();
|
|
382
|
+
}
|
|
383
|
+
state.enable = false;
|
|
384
|
+
return true;
|
|
385
|
+
};
|
|
386
|
+
return {
|
|
387
|
+
enable,
|
|
388
|
+
disable,
|
|
389
|
+
load,
|
|
390
|
+
state,
|
|
391
|
+
};
|
|
392
|
+
};
|
|
393
|
+
export const panoRulerPluginServerParams = {
|
|
394
|
+
name: 'PanoRulerPlugin',
|
|
395
|
+
version: 0,
|
|
396
|
+
};
|
|
397
|
+
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 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,87 @@
|
|
|
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
|
+
border-radius: 6.25rem;
|
|
58
|
+
border: 0.0625rem solid rgba(255,255,255,0.6);
|
|
59
|
+
white-space: nowrap;
|
|
60
|
+
overflow: hidden;
|
|
61
|
+
color: #FFFFFF;
|
|
62
|
+
font-weight: 500;
|
|
63
|
+
font-size: 0.75rem;
|
|
64
|
+
line-height: 1;
|
|
65
|
+
-webkit-animation: viewport-rule-label 0.25s ease 1s;
|
|
66
|
+
animation: viewport-rule-label 0.25s ease 1s;
|
|
67
|
+
animation-fill-mode: both;
|
|
68
|
+
box-shadow: inset 0 0 0.625rem 0 rgba(255,255,255,0.30);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@keyframes viewport-rule-line {
|
|
72
|
+
0% { width: 0% }
|
|
73
|
+
100% { width: 100% }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@keyframes viewport-rule-label {
|
|
77
|
+
0% { opacity: 0; transform: scaleX(0); }
|
|
78
|
+
100% { opacity: 1; transform: translate(-50%, -50%) scaleX(1); }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@keyframes viewport-rule-point {
|
|
82
|
+
0% { transform: scaleX(0); }
|
|
83
|
+
100% { transform: scaleX(1); }
|
|
84
|
+
}
|
|
85
|
+
</style>
|
|
86
|
+
`;
|
|
87
|
+
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 {};
|
package/libs/index.d.ts
CHANGED
|
@@ -15,5 +15,7 @@ export { default as ModelChassisCompassPlugin } from './ModelChassisCompassPlugi
|
|
|
15
15
|
export type { ModelEntryDoorGuidePluginParameterType, ModelEntryDoorGuidePluginData, ModelEntryDoorGuidePluginExportType, } from './ModelEntryDoorGuidePlugin';
|
|
16
16
|
export { default as ModelEntryDoorGuidePlugin } from './ModelEntryDoorGuidePlugin';
|
|
17
17
|
export { default as CameraMovementPlugin } from './CameraMovementPlugin';
|
|
18
|
+
export type { PanoRulerPluginParameterType, PanoRulerPluginExportType, PanoRulerPluginOptions, } from './PanoRulerPlugin';
|
|
19
|
+
export { default as PanoRulerPlugin } from './PanoRulerPlugin';
|
|
18
20
|
export type { PanoCompassPluginParameterType, PanoCompassPluginExportType, PanoCompassPluginData, } from './PanoCompassPlugin';
|
|
19
21
|
export { default as PanoCompassPluginPlugin } from './PanoCompassPlugin';
|
package/libs/index.js
CHANGED
|
@@ -8,4 +8,5 @@ export { default as ModelFloorplanPlugin } from './floorplan/ModelFloorplanPlugi
|
|
|
8
8
|
export { default as ModelChassisCompassPlugin } from './ModelChassisCompassPlugin';
|
|
9
9
|
export { default as ModelEntryDoorGuidePlugin } from './ModelEntryDoorGuidePlugin';
|
|
10
10
|
export { default as CameraMovementPlugin } from './CameraMovementPlugin';
|
|
11
|
+
export { default as PanoRulerPlugin } from './PanoRulerPlugin';
|
|
11
12
|
export { default as PanoCompassPluginPlugin } from './PanoCompassPlugin';
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"author": "REALSEE-DEVELOPER",
|
|
6
6
|
"repository": "https://github.com/realsee-developer/dnalogel.git",
|
|
7
7
|
"private": false,
|
|
8
|
-
"version": "2.0.0-alpha.
|
|
8
|
+
"version": "2.0.0-alpha.4",
|
|
9
9
|
"license": "SEE LICENSE IN TERMS.txt",
|
|
10
10
|
"scripts": {
|
|
11
11
|
"build:dev": "yarn tsb && vite build --watch",
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
},
|
|
19
19
|
"files": [
|
|
20
20
|
"libs",
|
|
21
|
-
"dist"
|
|
21
|
+
"dist",
|
|
22
|
+
"TERMS.txt"
|
|
22
23
|
],
|
|
23
24
|
"devDependencies": {
|
|
24
25
|
"@babel/core": "^7.17.5",
|