@realsee/dnalogel 2.0.0-alpha.13 → 2.0.0-alpha.16
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 +56 -0
- package/README.md +78 -28
- package/dist/ModelItemLabelPlugin/index.js +72 -83
- package/dist/ModelItemLabelPlugin/typings.d.ts +2 -1
- package/dist/ModelRoomLabelPlugin/index.js +1 -0
- package/dist/ModelTVVideoPlugin/index.d.ts +4 -0
- package/dist/ModelTVVideoPlugin/index.js +326 -0
- package/dist/ModelTVVideoPlugin/typings.d.ts +25 -0
- package/dist/ModelTVVideoPlugin/utils/parseData.d.ts +2 -0
- package/dist/PanoMeasurePlugin/index.js +2 -2
- package/dist/index.d.ts +3 -0
- package/dist/index.es.js +349 -86
- package/dist/index.js +349 -85
- package/dist/index.umd.js +349 -85
- package/docs/assets/main.js +1 -1
- package/docs/assets/search.js +1 -1
- package/docs/classes/ModelRoomLabelController.html +6 -6
- package/docs/enums/FLOOR_PLAN_ATTACHED_TO.html +1 -1
- package/docs/enums/ModelFloorplanErrorType.html +1 -1
- package/docs/index.html +78 -33
- package/docs/interfaces/LineJson.html +1 -1
- package/docs/interfaces/ModelChassisCompassPluginData.html +1 -1
- package/docs/interfaces/ModelChassisCompassPluginExportType.html +1 -1
- package/docs/interfaces/ModelChassisCompassPluginParameterType.html +1 -1
- package/docs/interfaces/ModelEntryDoorGuidePluginData.html +1 -1
- package/docs/interfaces/ModelEntryDoorGuidePluginExportType.html +1 -1
- package/docs/interfaces/ModelFloorplanParameterType.html +1 -1
- package/docs/interfaces/ModelFloorplanViewEvent.html +2 -2
- package/docs/interfaces/ModelItemLabelPluginData.html +1 -0
- package/docs/interfaces/ModelItemLabelPluginExportReturnsType.html +1 -0
- package/docs/interfaces/ModelItemLabelPluginParametersType.html +1 -0
- package/docs/interfaces/ModelRoomLabelPluginData.html +1 -1
- package/docs/interfaces/ModelTVVideoPluginData.html +1 -0
- package/docs/interfaces/ModelTVVideoPluginExportType.html +1 -0
- package/docs/interfaces/ModelTVVideoPluginParameterType.html +1 -0
- package/docs/interfaces/PanoCompassPluginData.html +1 -1
- package/docs/interfaces/PanoCompassPluginParameterType.html +1 -1
- package/docs/interfaces/PanoCursorRaycasterPluginExportType.html +6 -6
- package/docs/interfaces/PanoRulerPluginExportType.html +1 -1
- package/docs/interfaces/PanoRulerPluginOptions.html +1 -1
- package/docs/interfaces/PanoRulerPluginParameterType.html +1 -1
- package/docs/interfaces/PanoSpatialTagPluginData.html +1 -1
- package/docs/interfaces/PanoSpatialTagPluginExportType.html +1 -1
- package/docs/interfaces/PanoSpatialTagPluginParameterType.html +1 -1
- package/docs/interfaces/PointJson.html +1 -1
- package/docs/interfaces/RoomLabel.html +7 -7
- package/docs/interfaces/TopviewFloorplanPluginParameterType.html +1 -1
- package/docs/modules.html +12 -12
- package/libs/ModelItemLabelPlugin/ItemLabelComponent.js +48 -61
- package/libs/ModelItemLabelPlugin/index.js +5 -57
- package/libs/ModelItemLabelPlugin/typings.d.ts +2 -1
- package/libs/ModelItemLabelPlugin/typings.js +1 -0
- package/libs/ModelRoomLabelPlugin/Controller.js +1 -0
- package/libs/ModelTVVideoPlugin/index.d.ts +4 -0
- package/libs/ModelTVVideoPlugin/index.js +263 -0
- package/libs/ModelTVVideoPlugin/typings.d.ts +25 -0
- package/libs/ModelTVVideoPlugin/typings.js +7 -0
- package/libs/ModelTVVideoPlugin/utils/parseData.d.ts +2 -0
- package/libs/ModelTVVideoPlugin/utils/parseData.js +13 -0
- package/libs/index.d.ts +3 -0
- package/libs/index.js +1 -1
- package/package.json +2 -1
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
mount_component,
|
|
18
18
|
outro_and_destroy_block,
|
|
19
19
|
safe_not_equal,
|
|
20
|
+
set_style,
|
|
20
21
|
transition_in,
|
|
21
22
|
transition_out,
|
|
22
23
|
update_keyed_each
|
|
@@ -27,6 +28,7 @@ import { beforeUpdate, onDestroy, onMount } from "svelte";
|
|
|
27
28
|
import * as THREE from 'three';
|
|
28
29
|
import ItemLabelItem from './ItemLabelItem';
|
|
29
30
|
import "./events.type";
|
|
31
|
+
import debounce from '../shared-utils/debounce';
|
|
30
32
|
|
|
31
33
|
function add_css(target) {
|
|
32
34
|
append_styles(target, "svelte-rmdeb", ".item-labels-container.svelte-rmdeb{width:100%;height:100%;position:relative}");
|
|
@@ -34,11 +36,11 @@ function add_css(target) {
|
|
|
34
36
|
|
|
35
37
|
function get_each_context(ctx, list, i) {
|
|
36
38
|
const child_ctx = ctx.slice();
|
|
37
|
-
child_ctx[
|
|
39
|
+
child_ctx[28] = list[i];
|
|
38
40
|
return child_ctx;
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
// (
|
|
43
|
+
// (162:1) {#each renderItemLabels as itemLabelItem (itemLabelItem.id)}
|
|
42
44
|
function create_each_block(key_1, ctx) {
|
|
43
45
|
let first;
|
|
44
46
|
let itemlabelitem;
|
|
@@ -46,7 +48,7 @@ function create_each_block(key_1, ctx) {
|
|
|
46
48
|
|
|
47
49
|
itemlabelitem = new ItemLabelItem({
|
|
48
50
|
props: {
|
|
49
|
-
itemLabel: /*itemLabelItem*/ ctx[
|
|
51
|
+
itemLabel: /*itemLabelItem*/ ctx[28],
|
|
50
52
|
hooks: /*hooks*/ ctx[0]
|
|
51
53
|
}
|
|
52
54
|
});
|
|
@@ -67,7 +69,7 @@ function create_each_block(key_1, ctx) {
|
|
|
67
69
|
p(new_ctx, dirty) {
|
|
68
70
|
ctx = new_ctx;
|
|
69
71
|
const itemlabelitem_changes = {};
|
|
70
|
-
if (dirty & /*renderItemLabels*/ 2) itemlabelitem_changes.itemLabel = /*itemLabelItem*/ ctx[
|
|
72
|
+
if (dirty & /*renderItemLabels*/ 2) itemlabelitem_changes.itemLabel = /*itemLabelItem*/ ctx[28];
|
|
71
73
|
if (dirty & /*hooks*/ 1) itemlabelitem_changes.hooks = /*hooks*/ ctx[0];
|
|
72
74
|
itemlabelitem.$set(itemlabelitem_changes);
|
|
73
75
|
},
|
|
@@ -94,7 +96,7 @@ function create_fragment(ctx) {
|
|
|
94
96
|
let div_resize_listener;
|
|
95
97
|
let current;
|
|
96
98
|
let each_value = /*renderItemLabels*/ ctx[1];
|
|
97
|
-
const get_key = ctx => /*itemLabelItem*/ ctx[
|
|
99
|
+
const get_key = ctx => /*itemLabelItem*/ ctx[28].id;
|
|
98
100
|
|
|
99
101
|
for (let i = 0; i < each_value.length; i += 1) {
|
|
100
102
|
let child_ctx = get_each_context(ctx, each_value, i);
|
|
@@ -111,7 +113,8 @@ function create_fragment(ctx) {
|
|
|
111
113
|
}
|
|
112
114
|
|
|
113
115
|
attr(div, "class", "item-labels-container svelte-rmdeb");
|
|
114
|
-
add_render_callback(() => /*div_elementresize_handler*/ ctx[
|
|
116
|
+
add_render_callback(() => /*div_elementresize_handler*/ ctx[10].call(div));
|
|
117
|
+
set_style(div, "opacity", /*itemsVisible*/ ctx[4] ? 1 : 0, false);
|
|
115
118
|
},
|
|
116
119
|
m(target, anchor) {
|
|
117
120
|
insert(target, div, anchor);
|
|
@@ -120,7 +123,7 @@ function create_fragment(ctx) {
|
|
|
120
123
|
each_blocks[i].m(div, null);
|
|
121
124
|
}
|
|
122
125
|
|
|
123
|
-
div_resize_listener = add_resize_listener(div, /*div_elementresize_handler*/ ctx[
|
|
126
|
+
div_resize_listener = add_resize_listener(div, /*div_elementresize_handler*/ ctx[10].bind(div));
|
|
124
127
|
current = true;
|
|
125
128
|
},
|
|
126
129
|
p(ctx, [dirty]) {
|
|
@@ -130,6 +133,10 @@ function create_fragment(ctx) {
|
|
|
130
133
|
each_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx, each_value, each_1_lookup, div, outro_and_destroy_block, create_each_block, null, get_each_context);
|
|
131
134
|
check_outros();
|
|
132
135
|
}
|
|
136
|
+
|
|
137
|
+
if (dirty & /*itemsVisible*/ 16) {
|
|
138
|
+
set_style(div, "opacity", /*itemsVisible*/ ctx[4] ? 1 : 0, false);
|
|
139
|
+
}
|
|
133
140
|
},
|
|
134
141
|
i(local) {
|
|
135
142
|
if (current) return;
|
|
@@ -179,46 +186,14 @@ function instance($$self, $$props, $$invalidate) {
|
|
|
179
186
|
// 计算重叠
|
|
180
187
|
let cssOffsetLists = [];
|
|
181
188
|
|
|
189
|
+
// 动画过程中 itemsVisible 为 false
|
|
190
|
+
let itemsVisible = true;
|
|
191
|
+
|
|
182
192
|
/**
|
|
183
193
|
* 可见性策略:
|
|
184
194
|
* 1、当前楼层(假设先不考虑多楼层)
|
|
185
195
|
* 2、模型未被遮挡(一个 box 有没有被遮挡的计算? TODO)
|
|
186
196
|
* */
|
|
187
|
-
// 画辅助线
|
|
188
|
-
function addHelper(x, y, z, type, helper) {
|
|
189
|
-
let geometry, materials, mesh;
|
|
190
|
-
|
|
191
|
-
if (type === 'box') {
|
|
192
|
-
geometry = new THREE.BoxGeometry(0.6, 2.5, 1.010696);
|
|
193
|
-
let mats = [];
|
|
194
|
-
|
|
195
|
-
for (let i = 0; i < geometry.faces.length; i++) {
|
|
196
|
-
const material = new THREE.MeshBasicMaterial({
|
|
197
|
-
color: new THREE.Color(Math.random() * 0xffffff)
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
mats.push(material);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
materials = mats;
|
|
204
|
-
} else if (type === 'ball') {
|
|
205
|
-
geometry = new THREE.SphereGeometry(0.02, 0.02, 64);
|
|
206
|
-
materials = new THREE.MeshBasicMaterial({ color: new THREE.Color(0xffffff) });
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
mesh = new THREE.Mesh(geometry, materials);
|
|
210
|
-
|
|
211
|
-
// y 轴需要加一半的高度,因为position是从地面开始的,或者我应该算偏移更合理
|
|
212
|
-
mesh.position.set(x, y, z);
|
|
213
|
-
|
|
214
|
-
if (helper) {
|
|
215
|
-
const helper = new THREE.AxesHelper(5);
|
|
216
|
-
mesh.add(helper);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
five.scene.add(mesh);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
197
|
const getLabelVisible = (five, itemLabel) => {
|
|
223
198
|
// 虚拟 VR 仅有一层,不考虑楼层信息
|
|
224
199
|
const raycaster = new Raycaster();
|
|
@@ -272,6 +247,8 @@ function instance($$self, $$props, $$invalidate) {
|
|
|
272
247
|
return Math.ceil(-38.9 * itemSpaceHeight + 130);
|
|
273
248
|
case DISPLAY_STRATEGY_TYPE.LARGE:
|
|
274
249
|
return Math.ceil(-44.44 * itemSpaceHeight + 140);
|
|
250
|
+
case DISPLAY_STRATEGY_TYPE.EXTRA_LARGE:
|
|
251
|
+
return Math.ceil(-92.59 * itemSpaceHeight + 300);
|
|
275
252
|
}
|
|
276
253
|
}
|
|
277
254
|
|
|
@@ -318,15 +295,27 @@ function instance($$self, $$props, $$invalidate) {
|
|
|
318
295
|
addDataUpdateListener();
|
|
319
296
|
});
|
|
320
297
|
|
|
298
|
+
const handleCameraUpdateCallback = () => {
|
|
299
|
+
$$invalidate(4, itemsVisible = false);
|
|
300
|
+
handleCameraUpdate();
|
|
301
|
+
};
|
|
302
|
+
|
|
321
303
|
onMount(() => {
|
|
322
304
|
$$invalidate(1, renderItemLabels = itemLabels);
|
|
323
305
|
curItemLabels = itemLabels;
|
|
324
306
|
onItemLabelUpdate();
|
|
325
|
-
|
|
326
|
-
// five.on('cameraUpdate', onItemLabelUpdate)
|
|
327
307
|
addResizeListener();
|
|
308
|
+
five.on('cameraUpdate', handleCameraUpdateCallback);
|
|
328
309
|
});
|
|
329
310
|
|
|
311
|
+
const handleCameraUpdate = debounce(
|
|
312
|
+
() => {
|
|
313
|
+
$$invalidate(4, itemsVisible = true);
|
|
314
|
+
onItemLabelUpdate();
|
|
315
|
+
},
|
|
316
|
+
300
|
|
317
|
+
);
|
|
318
|
+
|
|
330
319
|
const addResizeListener = () => {
|
|
331
320
|
wrapperSize = {
|
|
332
321
|
width: wrapper.clientWidth,
|
|
@@ -340,7 +329,6 @@ function instance($$self, $$props, $$invalidate) {
|
|
|
340
329
|
if (curItemLabels !== itemLabels) {
|
|
341
330
|
$$invalidate(1, renderItemLabels = itemLabels);
|
|
342
331
|
curItemLabels = itemLabels;
|
|
343
|
-
console.log('--debug--update--');
|
|
344
332
|
onItemLabelUpdate();
|
|
345
333
|
}
|
|
346
334
|
};
|
|
@@ -358,13 +346,11 @@ function instance($$self, $$props, $$invalidate) {
|
|
|
358
346
|
});
|
|
359
347
|
|
|
360
348
|
onDestroy(() => {
|
|
361
|
-
|
|
349
|
+
five.off('cameraUpdate', handleCameraUpdateCallback);
|
|
362
350
|
resizeObserver.unobserve(wrapper);
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
wrapperSize = { width: 0, height: 0 };
|
|
367
|
-
});
|
|
351
|
+
}); // curItemLabels = null
|
|
352
|
+
// renderItemLabels = null
|
|
353
|
+
// wrapperSize = { width: 0, height: 0 }
|
|
368
354
|
|
|
369
355
|
function div_elementresize_handler() {
|
|
370
356
|
containerWidth = this.clientWidth;
|
|
@@ -374,12 +360,12 @@ function instance($$self, $$props, $$invalidate) {
|
|
|
374
360
|
}
|
|
375
361
|
|
|
376
362
|
$$self.$$set = $$props => {
|
|
377
|
-
if ('five' in $$props) $$invalidate(
|
|
378
|
-
if ('modelOcclusionEnable' in $$props) $$invalidate(
|
|
379
|
-
if ('itemLabels' in $$props) $$invalidate(
|
|
380
|
-
if ('wrapper' in $$props) $$invalidate(
|
|
363
|
+
if ('five' in $$props) $$invalidate(5, five = $$props.five);
|
|
364
|
+
if ('modelOcclusionEnable' in $$props) $$invalidate(6, modelOcclusionEnable = $$props.modelOcclusionEnable);
|
|
365
|
+
if ('itemLabels' in $$props) $$invalidate(7, itemLabels = $$props.itemLabels);
|
|
366
|
+
if ('wrapper' in $$props) $$invalidate(8, wrapper = $$props.wrapper);
|
|
381
367
|
if ('hooks' in $$props) $$invalidate(0, hooks = $$props.hooks);
|
|
382
|
-
if ('displayStrategyType' in $$props) $$invalidate(
|
|
368
|
+
if ('displayStrategyType' in $$props) $$invalidate(9, displayStrategyType = $$props.displayStrategyType);
|
|
383
369
|
};
|
|
384
370
|
|
|
385
371
|
return [
|
|
@@ -387,6 +373,7 @@ function instance($$self, $$props, $$invalidate) {
|
|
|
387
373
|
renderItemLabels,
|
|
388
374
|
containerWidth,
|
|
389
375
|
containerHeight,
|
|
376
|
+
itemsVisible,
|
|
390
377
|
five,
|
|
391
378
|
modelOcclusionEnable,
|
|
392
379
|
itemLabels,
|
|
@@ -407,12 +394,12 @@ class Component extends SvelteComponent {
|
|
|
407
394
|
create_fragment,
|
|
408
395
|
safe_not_equal,
|
|
409
396
|
{
|
|
410
|
-
five:
|
|
411
|
-
modelOcclusionEnable:
|
|
412
|
-
itemLabels:
|
|
413
|
-
wrapper:
|
|
397
|
+
five: 5,
|
|
398
|
+
modelOcclusionEnable: 6,
|
|
399
|
+
itemLabels: 7,
|
|
400
|
+
wrapper: 8,
|
|
414
401
|
hooks: 0,
|
|
415
|
-
displayStrategyType:
|
|
402
|
+
displayStrategyType: 9
|
|
416
403
|
},
|
|
417
404
|
add_css
|
|
418
405
|
);
|
|
@@ -14,7 +14,7 @@ export const ModelItemLabelPlugin = (five, params) => {
|
|
|
14
14
|
app: undefined,
|
|
15
15
|
hooks: new Subscribe(),
|
|
16
16
|
modelOcclusionEnable: (_a = params === null || params === void 0 ? void 0 : params.modelOcclusionEnable) !== null && _a !== void 0 ? _a : true,
|
|
17
|
-
displayStrategyType: (_b = params === null || params === void 0 ? void 0 : params.displayStrategyType) !== null && _b !== void 0 ? _b : DISPLAY_STRATEGY_TYPE.SMALL
|
|
17
|
+
displayStrategyType: (_b = params === null || params === void 0 ? void 0 : params.displayStrategyType) !== null && _b !== void 0 ? _b : DISPLAY_STRATEGY_TYPE.SMALL,
|
|
18
18
|
};
|
|
19
19
|
pluginState.container.setAttribute('class', 'model-item-label-plugin-container');
|
|
20
20
|
pluginState.container.style.cssText = `
|
|
@@ -23,8 +23,9 @@ export const ModelItemLabelPlugin = (five, params) => {
|
|
|
23
23
|
top: 0;
|
|
24
24
|
width: 100%;
|
|
25
25
|
height: 100%;
|
|
26
|
-
|
|
26
|
+
z-index: 5;
|
|
27
27
|
`;
|
|
28
|
+
pluginState.container.style.pointerEvents = 'none'; // 写到 cssText 里不生效
|
|
28
29
|
const appendTo = (wrapper) => {
|
|
29
30
|
pluginState.wrapper = wrapper;
|
|
30
31
|
wrapper.appendChild(pluginState.container);
|
|
@@ -32,7 +33,6 @@ export const ModelItemLabelPlugin = (five, params) => {
|
|
|
32
33
|
return true;
|
|
33
34
|
};
|
|
34
35
|
const load = (data) => {
|
|
35
|
-
console.log('🐶--当前标签总条数: ', data.model_item_labels.length);
|
|
36
36
|
pluginState.itemLabels = parseModelItemLabelPluginData(data);
|
|
37
37
|
render();
|
|
38
38
|
};
|
|
@@ -54,40 +54,6 @@ export const ModelItemLabelPlugin = (five, params) => {
|
|
|
54
54
|
removeListener4Five();
|
|
55
55
|
pluginState.container.remove();
|
|
56
56
|
};
|
|
57
|
-
const handleRerender = () => {
|
|
58
|
-
if (!pluginState.enabled || !pluginState.fiveModeEnabled)
|
|
59
|
-
return;
|
|
60
|
-
disable();
|
|
61
|
-
};
|
|
62
|
-
const rerender = (a, b, c) => {
|
|
63
|
-
if (!pluginState.fiveModeEnabled)
|
|
64
|
-
return;
|
|
65
|
-
if (!c)
|
|
66
|
-
return;
|
|
67
|
-
enable();
|
|
68
|
-
render();
|
|
69
|
-
};
|
|
70
|
-
const handleInteriaPanRerender = (a, b) => {
|
|
71
|
-
if (!pluginState.fiveModeEnabled)
|
|
72
|
-
return;
|
|
73
|
-
if (!b)
|
|
74
|
-
return;
|
|
75
|
-
enable();
|
|
76
|
-
render();
|
|
77
|
-
};
|
|
78
|
-
const handleWantsPanGesture = (pose, final) => {
|
|
79
|
-
if (pose) {
|
|
80
|
-
handleRerender();
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
const handlePanGesture = (pose, final) => {
|
|
84
|
-
if (!pluginState.fiveModeEnabled)
|
|
85
|
-
return;
|
|
86
|
-
if (!final)
|
|
87
|
-
return;
|
|
88
|
-
enable();
|
|
89
|
-
render();
|
|
90
|
-
};
|
|
91
57
|
const render = () => {
|
|
92
58
|
var _a;
|
|
93
59
|
if (!pluginState.wrapper)
|
|
@@ -123,32 +89,14 @@ export const ModelItemLabelPlugin = (five, params) => {
|
|
|
123
89
|
const addListener4Five = () => {
|
|
124
90
|
five.on('modeChange', onFiveModeChange);
|
|
125
91
|
five.once('dispose', dispose);
|
|
126
|
-
// five.on('wantsGesture', handleRerender)
|
|
127
|
-
// five.on('gesture', rerender)
|
|
128
|
-
five.on('wantsMouseWheel', handleRerender);
|
|
129
|
-
five.on('mouseWheel', rerender);
|
|
130
|
-
five.on('wantsInteriaPan', handleRerender);
|
|
131
|
-
five.on('interiaPan', handleInteriaPanRerender);
|
|
132
|
-
five.on('wantsPanGesture', handleWantsPanGesture);
|
|
133
|
-
five.on('panGesture', handlePanGesture);
|
|
134
|
-
five.on('wantsPinchGesture', handleRerender);
|
|
135
|
-
five.on('pinchGesture', rerender);
|
|
136
92
|
};
|
|
137
93
|
// 取消 five 监听
|
|
138
94
|
const removeListener4Five = () => {
|
|
139
95
|
five.off('modeChange', onFiveModeChange);
|
|
140
|
-
// five.off('wantsGesture', handleRerender)
|
|
141
|
-
// five.off('gesture', rerender)
|
|
142
|
-
five.off('wantsMouseWheel', handleRerender);
|
|
143
|
-
five.off('mouseWheel', rerender);
|
|
144
|
-
five.off('wantsInteriaPan', handleRerender);
|
|
145
|
-
five.off('interiaPan', handleInteriaPanRerender);
|
|
146
|
-
five.off('wantsPanGesture', handleWantsPanGesture);
|
|
147
|
-
five.off('panGesture', handlePanGesture);
|
|
148
|
-
five.off('wantsPinchGesture', handleRerender);
|
|
149
|
-
five.off('pinchGesture', rerender);
|
|
150
96
|
};
|
|
151
97
|
const onFiveModeChange = (mode) => {
|
|
98
|
+
if (!pluginState.enabled)
|
|
99
|
+
return;
|
|
152
100
|
if (mode !== Five.Mode.Floorplan) {
|
|
153
101
|
pluginState.fiveModeEnabled = false;
|
|
154
102
|
render();
|
|
@@ -4,7 +4,8 @@ import { PluginEvent } from "./events.type";
|
|
|
4
4
|
export declare enum DISPLAY_STRATEGY_TYPE {
|
|
5
5
|
SMALL = "small",
|
|
6
6
|
MIDLLE = "middle",
|
|
7
|
-
LARGE = "large"
|
|
7
|
+
LARGE = "large",
|
|
8
|
+
EXTRA_LARGE = "x-large"
|
|
8
9
|
}
|
|
9
10
|
export interface ModelItemLabelPluginParametersType {
|
|
10
11
|
modelOcclusionEnable?: boolean;
|
|
@@ -3,4 +3,5 @@ export var DISPLAY_STRATEGY_TYPE;
|
|
|
3
3
|
DISPLAY_STRATEGY_TYPE["SMALL"] = "small";
|
|
4
4
|
DISPLAY_STRATEGY_TYPE["MIDLLE"] = "middle";
|
|
5
5
|
DISPLAY_STRATEGY_TYPE["LARGE"] = "large";
|
|
6
|
+
DISPLAY_STRATEGY_TYPE["EXTRA_LARGE"] = "x-large";
|
|
6
7
|
})(DISPLAY_STRATEGY_TYPE || (DISPLAY_STRATEGY_TYPE = {}));
|
|
@@ -74,6 +74,7 @@ export class ModelRoomLabelController {
|
|
|
74
74
|
this.container.style.pointerEvents = 'none';
|
|
75
75
|
this.container.style.left = '0';
|
|
76
76
|
this.container.style.top = '0';
|
|
77
|
+
this.container.style.zIndex = '5';
|
|
77
78
|
// five listener
|
|
78
79
|
this.five.once('dispose', () => this.dispose());
|
|
79
80
|
this.five.on('modeChange', this.onFiveModeChange);
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { FivePlugin } from '@realsee/five';
|
|
2
|
+
import { ModelTVVideoPluginExportType, ModelTVVideoPluginParameterType } from "./typings";
|
|
3
|
+
export declare const ModelTVVideoPlugin: FivePlugin<ModelTVVideoPluginParameterType, ModelTVVideoPluginExportType>;
|
|
4
|
+
export default ModelTVVideoPlugin;
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import * as THREE from 'three';
|
|
11
|
+
import { parseModelTVVideoPoints } from "./utils/parseData";
|
|
12
|
+
export const ModelTVVideoPlugin = (five, { videoElement }) => {
|
|
13
|
+
const state = {
|
|
14
|
+
videoMeshes: [],
|
|
15
|
+
videoTextureEnabled: false,
|
|
16
|
+
videoSource: '',
|
|
17
|
+
rectPoints: [],
|
|
18
|
+
enabled: false,
|
|
19
|
+
videoElement: videoElement,
|
|
20
|
+
};
|
|
21
|
+
const setMuted = (muted) => {
|
|
22
|
+
if (state.videoTexture) {
|
|
23
|
+
state.videoTexture.image.muted = muted;
|
|
24
|
+
state.videoTexture.image.play();
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const getMuted = () => {
|
|
28
|
+
if (state.videoTexture)
|
|
29
|
+
return state.videoTexture.image.muted;
|
|
30
|
+
else
|
|
31
|
+
return true;
|
|
32
|
+
};
|
|
33
|
+
const enable = () => {
|
|
34
|
+
if (state.enabled)
|
|
35
|
+
return;
|
|
36
|
+
if (!state.videoTexture)
|
|
37
|
+
return;
|
|
38
|
+
state.enabled = true;
|
|
39
|
+
state.videoMeshes = createPanoVideoMeshes();
|
|
40
|
+
console.log('state.videoMeshes', state.videoMeshes);
|
|
41
|
+
state.videoMeshes.forEach((mesh) => five.scene.add(mesh));
|
|
42
|
+
const play = () => {
|
|
43
|
+
console.log('play', state.videoTexture);
|
|
44
|
+
if (!state.videoTexture)
|
|
45
|
+
return;
|
|
46
|
+
const timeupdate = () => {
|
|
47
|
+
var _a;
|
|
48
|
+
if (!state.videoTexture)
|
|
49
|
+
return;
|
|
50
|
+
(_a = state.videoTexture) === null || _a === void 0 ? void 0 : _a.image.removeEventListener('timeupdate', timeupdate);
|
|
51
|
+
state.videoTextureEnabled = true;
|
|
52
|
+
state.videoMeshes.forEach((mesh) => {
|
|
53
|
+
if (mesh.material.map !== state.videoTexture)
|
|
54
|
+
mesh.material.map = state.videoTexture;
|
|
55
|
+
});
|
|
56
|
+
five.needsRender = true;
|
|
57
|
+
};
|
|
58
|
+
state.videoTexture.image.addEventListener('timeupdate', timeupdate);
|
|
59
|
+
if (state.videoTexture && state.videoMeshes.length) {
|
|
60
|
+
state.videoTexture.image.play();
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
if (five.model.loaded)
|
|
64
|
+
play();
|
|
65
|
+
else {
|
|
66
|
+
return five.once('modelLoaded', () => play());
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const disable = () => {
|
|
70
|
+
if (!state.enabled)
|
|
71
|
+
return;
|
|
72
|
+
state.enabled = false;
|
|
73
|
+
state.videoMeshes.forEach((mesh) => {
|
|
74
|
+
mesh.geometry.dispose();
|
|
75
|
+
mesh.material.dispose();
|
|
76
|
+
five.scene.remove(mesh);
|
|
77
|
+
if (state.videoTexture)
|
|
78
|
+
state.videoTexture.image.pause();
|
|
79
|
+
});
|
|
80
|
+
state.videoMeshes = [];
|
|
81
|
+
five.needsRender = true;
|
|
82
|
+
};
|
|
83
|
+
const createPanoVideoMeshes = () => {
|
|
84
|
+
return state.rectPoints.map((points, index) => {
|
|
85
|
+
const geometry = new THREE.BufferGeometry();
|
|
86
|
+
const segments = 128;
|
|
87
|
+
const verticesArray = [];
|
|
88
|
+
verticesArray.push(points[0].x, points[0].y, points[0].z);
|
|
89
|
+
for (let i = 1; i < segments; i++) {
|
|
90
|
+
verticesArray.push(points[0].x + ((points[1].x - points[0].x) * i) / segments, points[0].y + ((points[1].y - points[0].y) * i) / segments, points[0].z + ((points[1].z - points[0].z) * i) / segments);
|
|
91
|
+
}
|
|
92
|
+
verticesArray.push(points[1].x, points[1].y, points[1].z);
|
|
93
|
+
verticesArray.push(points[2].x, points[2].y, points[2].z);
|
|
94
|
+
for (let i = 1; i < segments; i++) {
|
|
95
|
+
verticesArray.push(points[2].x + ((points[3].x - points[2].x) * i) / segments, points[2].y + ((points[3].y - points[2].y) * i) / segments, points[2].z + ((points[3].z - points[2].z) * i) / segments);
|
|
96
|
+
}
|
|
97
|
+
verticesArray.push(points[3].x, points[3].y, points[3].z);
|
|
98
|
+
const uvArray = [];
|
|
99
|
+
uvArray.push(0, 1);
|
|
100
|
+
for (let i = 1; i < segments; i++) {
|
|
101
|
+
uvArray.push(0, 1 - i / segments);
|
|
102
|
+
}
|
|
103
|
+
uvArray.push(0, 0);
|
|
104
|
+
uvArray.push(1, 0);
|
|
105
|
+
for (let i = 1; i < segments; i++) {
|
|
106
|
+
uvArray.push(1, i / segments);
|
|
107
|
+
}
|
|
108
|
+
uvArray.push(1, 1);
|
|
109
|
+
const indicesArray = [];
|
|
110
|
+
for (let i = 0; i < segments; i++) {
|
|
111
|
+
indicesArray.push(i, i + 1, segments * 2 - i, i, segments * 2 - i, segments * 2 + 1 - i);
|
|
112
|
+
}
|
|
113
|
+
geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(verticesArray), 3));
|
|
114
|
+
geometry.setAttribute('uv', new THREE.BufferAttribute(new Float32Array(uvArray), 2));
|
|
115
|
+
geometry.setIndex(new THREE.BufferAttribute(new Uint32Array(indicesArray), 1));
|
|
116
|
+
const material = new THREE.MeshBasicMaterial({
|
|
117
|
+
map: state.videoTextureEnabled ? state.videoTexture : state.imageTexture,
|
|
118
|
+
side: THREE.DoubleSide,
|
|
119
|
+
});
|
|
120
|
+
const mesh = new THREE.Mesh(geometry, material);
|
|
121
|
+
mesh.renderOrder = 1;
|
|
122
|
+
mesh.name = `ModelTVVideoPlugin-${index}-${performance.now()}`;
|
|
123
|
+
return mesh;
|
|
124
|
+
});
|
|
125
|
+
};
|
|
126
|
+
const getImageTexture = (source) => {
|
|
127
|
+
const texture = new THREE.TextureLoader().load(source);
|
|
128
|
+
texture.minFilter = THREE.LinearFilter;
|
|
129
|
+
texture.magFilter = THREE.LinearFilter;
|
|
130
|
+
texture.format = THREE.RGBFormat;
|
|
131
|
+
return texture;
|
|
132
|
+
};
|
|
133
|
+
const getVideoTexture = (source, video) => {
|
|
134
|
+
return new Promise((resolve, reject) => {
|
|
135
|
+
const xhr = new XMLHttpRequest();
|
|
136
|
+
xhr.onreadystatechange = () => {
|
|
137
|
+
if (xhr.readyState == 4) {
|
|
138
|
+
if (xhr.status == 200) {
|
|
139
|
+
const url = window.URL || window.webkitURL;
|
|
140
|
+
video = video || document.createElement('video');
|
|
141
|
+
video.crossOrigin = 'anonymous';
|
|
142
|
+
video.autoplay = true;
|
|
143
|
+
video.muted = true;
|
|
144
|
+
video.loop = true;
|
|
145
|
+
video.playsInline = true;
|
|
146
|
+
video.src = url.createObjectURL(xhr.response);
|
|
147
|
+
const texture = new THREE.VideoTexture(video);
|
|
148
|
+
texture.minFilter = THREE.LinearFilter;
|
|
149
|
+
texture.magFilter = THREE.LinearFilter;
|
|
150
|
+
texture.format = THREE.RGBFormat;
|
|
151
|
+
resolve(Object.assign(texture, { videoSource: source }));
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
reject(new Error('Video download Error: ' + xhr.status));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
xhr.onerror = (error) => reject(error);
|
|
159
|
+
xhr.open('GET', source);
|
|
160
|
+
xhr.responseType = 'blob';
|
|
161
|
+
xhr.send();
|
|
162
|
+
});
|
|
163
|
+
};
|
|
164
|
+
const load = (data, videoElement) => __awaiter(void 0, void 0, void 0, function* () {
|
|
165
|
+
const { video_src, video_poster_src, points } = data;
|
|
166
|
+
state.videoSource = video_src;
|
|
167
|
+
// TODO 存疑,先处理多一次遍历 o(2n)
|
|
168
|
+
state.rectPoints = parseModelTVVideoPoints(points)
|
|
169
|
+
.map((items) => items.map(({ x, y, z }) => new THREE.Vector3(x, y, z)));
|
|
170
|
+
state.imageTexture = getImageTexture(video_poster_src);
|
|
171
|
+
if (videoElement) {
|
|
172
|
+
state.videoElement = videoElement;
|
|
173
|
+
}
|
|
174
|
+
state.videoTexture = yield getVideoTexture(state.videoSource, state.videoElement);
|
|
175
|
+
state.enabled = !!data.enable;
|
|
176
|
+
if (state.enabled)
|
|
177
|
+
enable();
|
|
178
|
+
});
|
|
179
|
+
const dispose = () => {
|
|
180
|
+
disable();
|
|
181
|
+
state.videoTexture = undefined;
|
|
182
|
+
five.off('modeChange', () => setMuted(true));
|
|
183
|
+
five.off('wantsTapGesture', (raycaster) => {
|
|
184
|
+
if (!state.enabled)
|
|
185
|
+
return;
|
|
186
|
+
const [intersect] = raycaster.intersectObjects(five.scene.children, true);
|
|
187
|
+
if (!!intersect && /^video/.test(intersect.object.name)) {
|
|
188
|
+
if (state.videoTexture)
|
|
189
|
+
setMuted(!state.videoTexture.image.muted);
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
five.off('panoArrived', () => {
|
|
194
|
+
if (!state.enabled)
|
|
195
|
+
return;
|
|
196
|
+
if (getMuted())
|
|
197
|
+
return;
|
|
198
|
+
const cameraPosition = five.camera.position;
|
|
199
|
+
const visible = state.rectPoints.find((points) => {
|
|
200
|
+
const centerVector = points.reduce((total, curr) => new THREE.Vector3(total.x + curr.x / points.length, total.y + curr.y / points.length, total.z + curr.z / points.length), new THREE.Vector3());
|
|
201
|
+
return (points.map((point) => new THREE.Vector3((point.x + centerVector.x) / 2, (point.y + centerVector.y) / 2, (point.z + centerVector.z) / 2)).filter((vector) => {
|
|
202
|
+
vector = vector.clone().sub(cameraPosition).normalize();
|
|
203
|
+
const raycaster = new THREE.Raycaster(cameraPosition, vector);
|
|
204
|
+
const [intersect] = raycaster.intersectObjects(five.scene.children, true);
|
|
205
|
+
return !!intersect && /^video/.test(intersect.object.name);
|
|
206
|
+
}).length >= 2);
|
|
207
|
+
});
|
|
208
|
+
if (!visible)
|
|
209
|
+
setMuted(true);
|
|
210
|
+
});
|
|
211
|
+
five.off('renderFrame', () => {
|
|
212
|
+
// console.log('renderFrame', state.videoMeshes)
|
|
213
|
+
state.videoMeshes.forEach((meshes) => {
|
|
214
|
+
if (meshes)
|
|
215
|
+
//@ts-ignore
|
|
216
|
+
meshes.needsRender = true;
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
};
|
|
220
|
+
five.on('modeChange', () => setMuted(true));
|
|
221
|
+
five.on('wantsTapGesture', (raycaster) => {
|
|
222
|
+
if (!state.enabled)
|
|
223
|
+
return;
|
|
224
|
+
const targetMesh = five.scene.children.find(item => /^ModelTVVideoPlugin/.test(item.name));
|
|
225
|
+
if (!targetMesh)
|
|
226
|
+
return;
|
|
227
|
+
const intersectObjects = raycaster.intersectObjects([targetMesh]);
|
|
228
|
+
if (intersectObjects.length === 0)
|
|
229
|
+
return;
|
|
230
|
+
if (state.videoTexture)
|
|
231
|
+
setMuted(!state.videoTexture.image.muted); // TODO toggle video play
|
|
232
|
+
return false;
|
|
233
|
+
});
|
|
234
|
+
five.on('panoArrived', () => {
|
|
235
|
+
if (!state.enabled)
|
|
236
|
+
return;
|
|
237
|
+
if (getMuted())
|
|
238
|
+
return;
|
|
239
|
+
const cameraPosition = five.camera.position;
|
|
240
|
+
const visible = state.rectPoints.find((points) => {
|
|
241
|
+
const centerVector = points.reduce((total, curr) => new THREE.Vector3(total.x + curr.x / points.length, total.y + curr.y / points.length, total.z + curr.z / points.length), new THREE.Vector3());
|
|
242
|
+
return (points.map((point) => new THREE.Vector3((point.x + centerVector.x) / 2, (point.y + centerVector.y) / 2, (point.z + centerVector.z) / 2)).filter((vector) => {
|
|
243
|
+
vector = vector.clone().sub(cameraPosition).normalize();
|
|
244
|
+
const raycaster = new THREE.Raycaster(cameraPosition, vector);
|
|
245
|
+
const [intersect] = raycaster.intersectObjects(five.scene.children, true);
|
|
246
|
+
return !!intersect && /^video/.test(intersect.object.name);
|
|
247
|
+
}).length >= 2);
|
|
248
|
+
});
|
|
249
|
+
if (!visible)
|
|
250
|
+
setMuted(true);
|
|
251
|
+
});
|
|
252
|
+
five.on('renderFrame', () => {
|
|
253
|
+
// console.log('renderFrame', state.videoMeshes)
|
|
254
|
+
state.videoMeshes.forEach((meshes) => {
|
|
255
|
+
if (meshes)
|
|
256
|
+
//@ts-ignore
|
|
257
|
+
meshes.needsRender = true;
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
Object.assign(window, { $state: state });
|
|
261
|
+
return { enable, disable, load, dispose };
|
|
262
|
+
};
|
|
263
|
+
export default ModelTVVideoPlugin;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface ModelTVVideoPluginParameterType {
|
|
2
|
+
videoElement?: HTMLVideoElement;
|
|
3
|
+
}
|
|
4
|
+
export declare enum DIRECTION {
|
|
5
|
+
TOP_LEFT = "topLeft",
|
|
6
|
+
TOP_RIGHT = "topRight",
|
|
7
|
+
BOTTOM_LEFT = "bottomLeft",
|
|
8
|
+
BOTTOM_RIGHT = "bottomRight"
|
|
9
|
+
}
|
|
10
|
+
export interface Point {
|
|
11
|
+
x: number;
|
|
12
|
+
y: number;
|
|
13
|
+
z: number;
|
|
14
|
+
}
|
|
15
|
+
export interface ModelTVVideoPluginData {
|
|
16
|
+
enable?: boolean;
|
|
17
|
+
video_src: string;
|
|
18
|
+
video_poster_src: string;
|
|
19
|
+
points: Point[][] | Record<DIRECTION, Point>[];
|
|
20
|
+
}
|
|
21
|
+
export interface ModelTVVideoPluginExportType {
|
|
22
|
+
enable: () => void;
|
|
23
|
+
disable: () => void;
|
|
24
|
+
load: (data: ModelTVVideoPluginData, videoElement?: HTMLVideoElement) => Promise<void>;
|
|
25
|
+
}
|