@realsee/dnalogel 2.0.0-alpha.34 → 2.0.0-alpha.35

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.
Files changed (115) hide show
  1. package/dist/PanoSpatialTagPlugin/Plugin.d.ts +2 -0
  2. package/dist/PanoSpatialTagPlugin/index.js +57 -48
  3. package/dist/index.es.js +57 -48
  4. package/dist/index.js +57 -48
  5. package/dist/index.umd.js +57 -48
  6. package/docs/assets/search.js +1 -1
  7. package/docs/classes/Magnifier.html +4 -4
  8. package/docs/classes/ModelRoomLabelController.html +4 -4
  9. package/docs/classes/PanoFloorplanRadarPluginController.html +2 -2
  10. package/docs/enums/CameraMovementEffect.html +2 -2
  11. package/docs/enums/DIRECTION.html +1 -1
  12. package/docs/enums/DISPLAY_STRATEGY_TYPE.html +1 -1
  13. package/docs/enums/FLOOR_PLAN_ATTACHED_TO.html +1 -1
  14. package/docs/enums/ITEM_LABEL_PLUGIN_DISPLAY_STRATEGY_TYPE.html +1 -1
  15. package/docs/enums/ModelFloorplanErrorType.html +1 -1
  16. package/docs/enums/Rotation.html +2 -2
  17. package/docs/index.html +1 -1
  18. package/docs/interfaces/CSS3DRenderPluginExportType.html +2 -2
  19. package/docs/interfaces/CameraMovementPluginExportType.html +2 -2
  20. package/docs/interfaces/FloorplanBounding.html +4 -4
  21. package/docs/interfaces/FloorplanData.html +2 -2
  22. package/docs/interfaces/FloorplanEntrance.html +7 -7
  23. package/docs/interfaces/FloorplanExtraObject.html +3 -3
  24. package/docs/interfaces/FloorplanExtraObject3D.html +2 -2
  25. package/docs/interfaces/FloorplanFloorData.html +2 -2
  26. package/docs/interfaces/FloorplanImagePosition.html +2 -2
  27. package/docs/interfaces/FloorplanObserver.html +2 -2
  28. package/docs/interfaces/FloorplanOutlineItem.html +2 -2
  29. package/docs/interfaces/FloorplanPosition.html +2 -2
  30. package/docs/interfaces/FloorplanRoomItem.html +6 -6
  31. package/docs/interfaces/FloorplanRoomLabelItem.html +3 -3
  32. package/docs/interfaces/FloorplanServerBounding.html +4 -4
  33. package/docs/interfaces/FloorplanServerComputedData.html +1 -1
  34. package/docs/interfaces/FloorplanServerData.html +2 -2
  35. package/docs/interfaces/FloorplanServerEntrance.html +7 -7
  36. package/docs/interfaces/FloorplanServerFloorData.html +2 -2
  37. package/docs/interfaces/FloorplanServerImagePosition.html +2 -2
  38. package/docs/interfaces/FloorplanServerObserver.html +2 -2
  39. package/docs/interfaces/FloorplanServerOutlineItem.html +2 -2
  40. package/docs/interfaces/FloorplanServerPosition.html +2 -2
  41. package/docs/interfaces/FloorplanServerRoomItem.html +7 -7
  42. package/docs/interfaces/FloorplanServerRoomLabelItem.html +3 -3
  43. package/docs/interfaces/ItemLabelPluginData.html +1 -1
  44. package/docs/interfaces/ItemLabelPluginExportReturnsType.html +1 -1
  45. package/docs/interfaces/ItemLabelPluginParametersType.html +1 -1
  46. package/docs/interfaces/LineJson.html +1 -1
  47. package/docs/interfaces/ModelChassisCompassPluginData.html +1 -1
  48. package/docs/interfaces/ModelChassisCompassPluginExportType.html +1 -1
  49. package/docs/interfaces/ModelChassisCompassPluginParameterType.html +1 -1
  50. package/docs/interfaces/ModelEntryDoorGuidePluginData.html +1 -1
  51. package/docs/interfaces/ModelEntryDoorGuidePluginExportType.html +1 -1
  52. package/docs/interfaces/ModelFloorplanParameterType.html +1 -1
  53. package/docs/interfaces/ModelFloorplanViewEvent.html +2 -2
  54. package/docs/interfaces/ModelItemLabelPluginData.html +1 -1
  55. package/docs/interfaces/ModelItemLabelPluginExportReturnsType.html +1 -1
  56. package/docs/interfaces/ModelItemLabelPluginParametersType.html +1 -1
  57. package/docs/interfaces/ModelRoomLabelPluginData.html +1 -1
  58. package/docs/interfaces/ModelTVVideoPluginData.html +1 -1
  59. package/docs/interfaces/ModelTVVideoPluginExportType.html +1 -1
  60. package/docs/interfaces/ModelTVVideoPluginParameterType.html +1 -1
  61. package/docs/interfaces/ModelViewPluginExportType.html +1 -1
  62. package/docs/interfaces/OpenParameter.html +2 -2
  63. package/docs/interfaces/PanoCompassPluginData.html +1 -1
  64. package/docs/interfaces/PanoCompassPluginParameterType.html +1 -1
  65. package/docs/interfaces/PanoCursorRaycasterPluginExportType.html +6 -6
  66. package/docs/interfaces/PanoCursorRaycasterPluginParameterType.html +1 -1
  67. package/docs/interfaces/PanoFloorplanRadarPluginControllerParameter.html +1 -1
  68. package/docs/interfaces/PanoMeasureParameterType.html +1 -1
  69. package/docs/interfaces/PanoRulerPluginExportType.html +1 -1
  70. package/docs/interfaces/PanoRulerPluginOptions.html +1 -1
  71. package/docs/interfaces/PanoRulerPluginParameterType.html +1 -1
  72. package/docs/interfaces/PanoSpatialTagPluginContentEvent.html +1 -1
  73. package/docs/interfaces/PanoSpatialTagPluginContentReplacement.html +1 -1
  74. package/docs/interfaces/PanoSpatialTagPluginData.html +1 -1
  75. package/docs/interfaces/PanoSpatialTagPluginDataElement.html +1 -1
  76. package/docs/interfaces/PanoSpatialTagPluginExportType.html +1 -1
  77. package/docs/interfaces/PanoSpatialTagPluginOriginElement.html +1 -1
  78. package/docs/interfaces/PanoSpatialTagPluginParameterType.html +1 -1
  79. package/docs/interfaces/PanoSpatialTagPluginPointElement.html +1 -1
  80. package/docs/interfaces/PanoSpatialTagPluginTagElement.html +1 -1
  81. package/docs/interfaces/Point.html +1 -1
  82. package/docs/interfaces/PointJson.html +1 -1
  83. package/docs/interfaces/Room.html +1 -1
  84. package/docs/interfaces/RoomInfo.html +1 -1
  85. package/docs/interfaces/RoomLabel.html +7 -7
  86. package/docs/interfaces/RoomRules.html +1 -1
  87. package/docs/interfaces/Rooms.html +1 -1
  88. package/docs/interfaces/TopviewFloorplanPluginParameterType.html +9 -9
  89. package/docs/modules.html +20 -20
  90. package/libs/ModelSimulationPlugin/index.d.ts +33 -0
  91. package/libs/ModelSimulationPlugin/index.js +166 -0
  92. package/libs/ModelSimulationPlugin/shaders/fragment.d.ts +2 -0
  93. package/libs/ModelSimulationPlugin/shaders/fragment.js +96 -0
  94. package/libs/ModelSimulationPlugin/shaders/vertex.d.ts +2 -0
  95. package/libs/ModelSimulationPlugin/shaders/vertex.js +23 -0
  96. package/libs/PanoSpatialTagPlugin/Components/tag.js +2 -2
  97. package/libs/PanoSpatialTagPlugin/Plugin.d.ts +2 -0
  98. package/libs/PanoSpatialTagPlugin/Plugin.js +55 -46
  99. package/libs/floorplan/ModelFloorplanPlugin/Assets/compass.d.ts +1 -0
  100. package/libs/floorplan/ModelFloorplanPlugin/Assets/compass.js +1 -0
  101. package/libs/floorplan/ModelFloorplanPlugin/Components/Camera.js +120 -0
  102. package/libs/floorplan/ModelFloorplanPlugin/Components/Compass.js +106 -0
  103. package/libs/floorplan/ModelFloorplanPlugin/Components/CurrentFloor/CurrentFloor.js +463 -0
  104. package/libs/floorplan/ModelFloorplanPlugin/Components/Main.js +375 -0
  105. package/libs/floorplan/ModelFloorplanPlugin/Components/RoomLabels/RoomLabel.js +171 -0
  106. package/libs/floorplan/ModelFloorplanPlugin/Components/RoomLabels/RoomLabels.js +265 -0
  107. package/libs/floorplan/ModelFloorplanPlugin/Components/RuleLabels/RuleItem.js +301 -0
  108. package/libs/floorplan/ModelFloorplanPlugin/Components/RuleLabels/RuleLabels.js +219 -0
  109. package/libs/shared-utils/five/useFivePlugin.d.ts +1 -0
  110. package/libs/shared-utils/five/useFivePlugin.js +8 -0
  111. package/libs/shared-utils/useDebounce.d.ts +8 -0
  112. package/libs/shared-utils/useDebounce.js +22 -0
  113. package/libs/shared-utils/useThrottle.d.ts +8 -0
  114. package/libs/shared-utils/useThrottle.js +25 -0
  115. package/package.json +1 -1
@@ -0,0 +1,33 @@
1
+ import type { FivePlugin } from '@realsee/five';
2
+ export interface ModelSimulationPluginExportType {
3
+ enable: () => void;
4
+ disable: () => void;
5
+ play: () => void;
6
+ dispose: () => void;
7
+ init: () => void;
8
+ }
9
+ export interface ModelSimulationPluginParameterType {
10
+ simulationWorkData: ModelSimulationPluginWorkDataType;
11
+ simulationOptions: ModelSimulationPluginOptionsType;
12
+ }
13
+ export interface ModelSimulationPluginWorkDataType {
14
+ resourceUrl: string;
15
+ colorMapPath: string;
16
+ width: number;
17
+ height: number;
18
+ depth: number;
19
+ origin: number[];
20
+ step: number;
21
+ maxT: number;
22
+ minT: number;
23
+ maxKeyframes: number;
24
+ houseHeight: number;
25
+ }
26
+ export interface ModelSimulationPluginOptionsType {
27
+ globalOpacity: number;
28
+ boundingOpacity: number;
29
+ boundingOffset?: number;
30
+ keyFrameRate?: number;
31
+ }
32
+ declare const ModelSimulationPlugin: FivePlugin<ModelSimulationPluginParameterType, ModelSimulationPluginExportType>;
33
+ export default ModelSimulationPlugin;
@@ -0,0 +1,166 @@
1
+ import * as THREE from 'three';
2
+ import vertexShader from './shaders/vertex';
3
+ import fragmentShader from './shaders/fragment';
4
+ import tap from '../shared-utils/tap';
5
+ const houseOutLinePath = [
6
+ [-3.089, -4.219], [-4.834, -4.219],
7
+ [-8.197, -4.219], [-10.31, -4.219],
8
+ [-10.31, -1.002], [-10.31, 0.835],
9
+ [-10.31, 3.927], [-11.237, 3.927],
10
+ [-11.237, 5.45], [-8.104, 5.45],
11
+ [-8.104, 4.557], [-6.914, 4.557],
12
+ [-6.244, 4.557], [-6.244, 5.436],
13
+ [-3.78, 5.436], [-0.356, 5.436],
14
+ [0.286, 5.436], [0.286, 4.073],
15
+ [0.286, 0.395], [2.31, 0.395],
16
+ [2.31, -1.323], [2.31, -4.199],
17
+ [0.468, -4.199], [0.468, -3.547],
18
+ [-2.969, -3.547], [-3.089, -3.547]
19
+ ];
20
+ const defaultKeyframes = 50;
21
+ const defaultBoundingOffset = 0.2;
22
+ let isTexutreReady = false;
23
+ const ModelSimulationPlugin = (five, options) => {
24
+ let heatTexutres = [];
25
+ let geometryTexture = null;
26
+ let simulationMaterial = null;
27
+ let boundingMaterial = null;
28
+ let boundingMesh = null;
29
+ let originMaterial = null;
30
+ const enable = () => {
31
+ };
32
+ const disable = () => {
33
+ five.model.setMaterial(originMaterial);
34
+ };
35
+ const createBounding = (path, material) => {
36
+ const shape = tap(new THREE.Shape(), shape => {
37
+ shape.moveTo(path[path.length - 1][0], path[path.length - 1][1]);
38
+ path.forEach(xz => shape.lineTo(xz[0], xz[1]));
39
+ });
40
+ const settings = {
41
+ depth: options.simulationWorkData.houseHeight + 2 * options.simulationOptions.boundingOffset ?? defaultBoundingOffset,
42
+ bevelEnabled: false,
43
+ };
44
+ const geometry = new THREE.ExtrudeGeometry(shape, settings);
45
+ boundingMesh = new THREE.Mesh(geometry, material);
46
+ boundingMesh.rotateX(Math.PI / 2);
47
+ boundingMesh.translateZ(-options.simulationWorkData.houseHeight - options.simulationOptions.boundingOffset);
48
+ boundingMesh.updateMatrix();
49
+ boundingMesh.updateMatrixWorld();
50
+ return boundingMesh;
51
+ };
52
+ const createSimulationMaterial = (isBounding) => {
53
+ const simulationMaterial = new THREE.RawShaderMaterial({
54
+ uniforms: {
55
+ heatMap: { value: new THREE.Texture() },
56
+ geometryMap: { value: new THREE.Texture() },
57
+ colorMap: { value: new THREE.TextureLoader().load(options.simulationWorkData.colorMapPath) },
58
+ bboxMin: { value: new THREE.Vector3().fromArray(options.simulationWorkData.origin) },
59
+ bboxMax: { value: new THREE.Vector3().fromArray(options.simulationWorkData.origin).add(new THREE.Vector3(options.simulationWorkData.width, options.simulationWorkData.height, options.simulationWorkData.depth).multiplyScalar(options.simulationWorkData.step)) },
60
+ step: { value: options.simulationWorkData.step },
61
+ maxT: { value: options.simulationWorkData.maxT },
62
+ minT: { value: options.simulationWorkData.minT },
63
+ opacity: { value: isBounding ? options.simulationOptions.boundingOpacity : options.simulationOptions.globalOpacity }
64
+ },
65
+ vertexShader,
66
+ fragmentShader,
67
+ });
68
+ if (isBounding)
69
+ simulationMaterial.transparent = true;
70
+ return simulationMaterial;
71
+ };
72
+ const create3DTextureTask = (url) => {
73
+ return fetch(url)
74
+ .then(res => {
75
+ return res.arrayBuffer();
76
+ })
77
+ .then(res => {
78
+ const texture = new THREE.DataTexture3D(new Float32Array(res), options.simulationWorkData.width, options.simulationWorkData.height, options.simulationWorkData.depth);
79
+ texture.type = THREE.FloatType;
80
+ texture.format = THREE.RedFormat;
81
+ texture.minFilter = THREE.LinearMipmapLinearFilter;
82
+ texture.magFilter = THREE.LinearFilter;
83
+ texture.generateMipmaps = true;
84
+ texture.unpackAlignment = 1;
85
+ return texture;
86
+ })
87
+ .catch(err => console.error("拉取三维纹理数据时发生错误"));
88
+ };
89
+ const init = () => {
90
+ simulationMaterial = createSimulationMaterial(false);
91
+ boundingMaterial = createSimulationMaterial(true);
92
+ //加载外bounding
93
+ five.scene.add(createBounding(houseOutLinePath, boundingMaterial));
94
+ //缓存原来的材质
95
+ originMaterial = five.model.getMaterial();
96
+ five.model.traverse(obj => {
97
+ if (!(obj instanceof THREE.Mesh))
98
+ return;
99
+ if (Array.isArray(obj.material)) {
100
+ obj.material = obj.material.map(_mtl => simulationMaterial);
101
+ }
102
+ else {
103
+ obj.material = simulationMaterial;
104
+ }
105
+ });
106
+ //温度数据
107
+ // const geometryTaskUrl = options.resourceUrl + 'geometry.vti'
108
+ const heatTextureTasks = [];
109
+ for (let i = 0; i < options.simulationWorkData.maxKeyframes; i++) {
110
+ const fileUrl = options.simulationWorkData.resourceUrl + i + '.vti';
111
+ heatTextureTasks.push(create3DTextureTask(fileUrl));
112
+ }
113
+ // //有效值数据
114
+ // create3DTextureTask(geometryTaskUrl)
115
+ // .then(texture => {
116
+ // simulationMaterial.uniforms.geometryMap.value = texture
117
+ // boundingMaterial.uniforms.geometryMap.value = texture
118
+ // })
119
+ // .catch(err => console.error("geometry texture loading failed"))
120
+ //加载首帧进行初始化
121
+ create3DTextureTask(options.simulationWorkData.resourceUrl + '0.vti')
122
+ .then(texture => {
123
+ simulationMaterial.uniforms.heatMap.value = texture;
124
+ boundingMaterial.uniforms.heatMap.value = texture;
125
+ five.needsRender = true;
126
+ })
127
+ .catch(err => console.error("loading textures failed"));
128
+ //加载全部
129
+ Promise.all(heatTextureTasks)
130
+ .then(textures => {
131
+ //缓存结果
132
+ isTexutreReady = true;
133
+ console.log("ready");
134
+ heatTexutres = textures;
135
+ })
136
+ .catch(err => console.error("loading textures failed"));
137
+ };
138
+ const play = () => {
139
+ if (!isTexutreReady)
140
+ throw new Error("textures are not ready yet,plz try again later");
141
+ let timeCount = 0;
142
+ const stop = setInterval(() => {
143
+ if (timeCount < options.simulationWorkData.maxKeyframes) {
144
+ simulationMaterial.uniforms.heatMap.value = heatTexutres[timeCount];
145
+ boundingMaterial.uniforms.heatMap.value = heatTexutres[timeCount];
146
+ timeCount++;
147
+ five.needsRender = true;
148
+ }
149
+ }, 1000 / options.simulationOptions.keyFrameRate ?? defaultKeyframes);
150
+ };
151
+ const dispose = () => {
152
+ simulationMaterial.dispose();
153
+ boundingMaterial.dispose();
154
+ boundingMesh.geometry.dispose();
155
+ geometryTexture.dispose();
156
+ heatTexutres.forEach(texture => texture.dispose());
157
+ };
158
+ return {
159
+ enable,
160
+ disable,
161
+ play,
162
+ init,
163
+ dispose
164
+ };
165
+ };
166
+ export default ModelSimulationPlugin;
@@ -0,0 +1,2 @@
1
+ declare var _default: "#version 300 es\n#define MAX_STEPS 50\nprecision highp float;\nprecision highp sampler3D;\n\nuniform sampler3D heatMap;\nuniform sampler3D geometryMap;\nuniform sampler2D colorMap;\n\nuniform vec3 bboxMin;\nuniform vec3 bboxMax;\nuniform float step;\nuniform float minT;\nuniform float maxT;\nuniform float opacity;\n\nvec4 LinearTosRGB( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a );\n}\nvec4 linearToOutputTexel( vec4 value ) { return LinearTosRGB( value ); }\n\n\nfloat getHeatByLegalPos(vec3 pos){\n\tfloat geometryFactor = texture(geometryMap,pos).r;\n\tfloat legalFactor = clamp(geometryFactor,0.0,1.0);\n\treturn texture(heatMap,pos).r * legalFactor;\n}\n\nfloat sampleOc(vec3 pos){\n\t//平滑滤波步长\n\t//todo:可以作为define传入的外部常量还是直接传\n\tfloat step_x = step / (bboxMax.x - bboxMin.x) * 0.5;\n\tfloat step_y = step / (bboxMax.y - bboxMin.y) * 0.5;\n\tfloat step_z = step / (bboxMax.z - bboxMin.z) * 0.5;\n\tvec3 p1 = pos + vec3(step_x,step_y,step_z);\n\tvec3 p2 = pos + vec3(step_x,-step_y,step_z);\n\tvec3 p3 = pos + vec3(step_x,step_y,-step_z);\n\tvec3 p4 = pos + vec3(step_x,-step_y,-step_z);\n\tvec3 p5 = pos + vec3(-step_x,step_y,step_z);\n\tvec3 p6 = pos + vec3(-step_x,-step_y,step_z);\n\tvec3 p7 = pos + vec3(-step_x,step_y,-step_z);\n\tvec3 p8 = pos + vec3(-step_x,-step_y,-step_z);\n\n\treturn (getHeatByLegalPos(p1) + getHeatByLegalPos(p2) + getHeatByLegalPos(p3) + getHeatByLegalPos(p4) + getHeatByLegalPos(p5) + getHeatByLegalPos(p6) + getHeatByLegalPos(p7) + getHeatByLegalPos(p8)) * 0.125;\n\n}\n\n//考虑到有效仿真值连通域在三维空间中是封闭流形,这里直接按照normal方向搜索,牺牲一定精度,以保证展示效果\nfloat sampleByNormal(vec3 normal, vec3 pos){\n\tfloat searchStep = step ;\n\tvec3 currentPos = pos;\n\tfor (int iter=0; iter < MAX_STEPS; iter++) {\n\t\tif(iter >= MAX_STEPS){\n\t\t\treturn texture(heatMap,currentPos).r;\n\t\t}\n\t\tif(texture(geometryMap,currentPos).r < 0.5){\n\t\t\tcurrentPos += normal * searchStep;\n\t\t}else{\n\t\t\treturn texture(heatMap,currentPos).r;\n\t\t}\n\t}\n}\n\nfloat sample1(vec3 normal, vec3 pos){\n\treturn texture(heatMap,pos).r;\n}\n\nin vec2 vUv;\nin vec3 vPosition;\nin vec3 vNormal;\nout highp vec4 pc_fragColor;\n\n\nfloat normalizeHeat(float t){\n\tfloat _t = clamp(t,minT,maxT);\n\treturn (_t - minT)/(maxT - minT);\n}\n\nvec3 posToTexCood(vec3 pos){\n\tfloat x = (pos.x - bboxMin.x) / (bboxMax.x - bboxMin.x);\n\tfloat y = (pos.y - bboxMin.y) / (bboxMax.y - bboxMin.y);\n\tfloat z = (pos.z - bboxMin.z) / (bboxMax.z - bboxMin.z);\n\treturn vec3(x,y,z);\n}\n\n\nvoid main() {\n\tvec3 texCood = posToTexCood(vPosition);\n\t// float color = sampleByNormal(vNormal,texCood);\n\tfloat color = sample1(vNormal,texCood);\n\tfloat normalColor= normalizeHeat(color);\n\tvec4 colorMapColor = texture(colorMap,vec2(normalColor,0.2));\n\tpc_fragColor = vec4(colorMapColor.xyz,opacity);\n\t// pc_fragColor = linearToOutputTexel(vec4(colorMapColor.xyz,opacity));\n\t}\n";
2
+ export default _default;
@@ -0,0 +1,96 @@
1
+ export default `#version 300 es
2
+ #define MAX_STEPS 50
3
+ precision highp float;
4
+ precision highp sampler3D;
5
+
6
+ uniform sampler3D heatMap;
7
+ uniform sampler3D geometryMap;
8
+ uniform sampler2D colorMap;
9
+
10
+ uniform vec3 bboxMin;
11
+ uniform vec3 bboxMax;
12
+ uniform float step;
13
+ uniform float minT;
14
+ uniform float maxT;
15
+ uniform float opacity;
16
+
17
+ vec4 LinearTosRGB( in vec4 value ) {
18
+ return vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a );
19
+ }
20
+ vec4 linearToOutputTexel( vec4 value ) { return LinearTosRGB( value ); }
21
+
22
+
23
+ float getHeatByLegalPos(vec3 pos){
24
+ float geometryFactor = texture(geometryMap,pos).r;
25
+ float legalFactor = clamp(geometryFactor,0.0,1.0);
26
+ return texture(heatMap,pos).r * legalFactor;
27
+ }
28
+
29
+ float sampleOc(vec3 pos){
30
+ //平滑滤波步长
31
+ //todo:可以作为define传入的外部常量还是直接传
32
+ float step_x = step / (bboxMax.x - bboxMin.x) * 0.5;
33
+ float step_y = step / (bboxMax.y - bboxMin.y) * 0.5;
34
+ float step_z = step / (bboxMax.z - bboxMin.z) * 0.5;
35
+ vec3 p1 = pos + vec3(step_x,step_y,step_z);
36
+ vec3 p2 = pos + vec3(step_x,-step_y,step_z);
37
+ vec3 p3 = pos + vec3(step_x,step_y,-step_z);
38
+ vec3 p4 = pos + vec3(step_x,-step_y,-step_z);
39
+ vec3 p5 = pos + vec3(-step_x,step_y,step_z);
40
+ vec3 p6 = pos + vec3(-step_x,-step_y,step_z);
41
+ vec3 p7 = pos + vec3(-step_x,step_y,-step_z);
42
+ vec3 p8 = pos + vec3(-step_x,-step_y,-step_z);
43
+
44
+ return (getHeatByLegalPos(p1) + getHeatByLegalPos(p2) + getHeatByLegalPos(p3) + getHeatByLegalPos(p4) + getHeatByLegalPos(p5) + getHeatByLegalPos(p6) + getHeatByLegalPos(p7) + getHeatByLegalPos(p8)) * 0.125;
45
+
46
+ }
47
+
48
+ //考虑到有效仿真值连通域在三维空间中是封闭流形,这里直接按照normal方向搜索,牺牲一定精度,以保证展示效果
49
+ float sampleByNormal(vec3 normal, vec3 pos){
50
+ float searchStep = step ;
51
+ vec3 currentPos = pos;
52
+ for (int iter=0; iter < MAX_STEPS; iter++) {
53
+ if(iter >= MAX_STEPS){
54
+ return texture(heatMap,currentPos).r;
55
+ }
56
+ if(texture(geometryMap,currentPos).r < 0.5){
57
+ currentPos += normal * searchStep;
58
+ }else{
59
+ return texture(heatMap,currentPos).r;
60
+ }
61
+ }
62
+ }
63
+
64
+ float sample1(vec3 normal, vec3 pos){
65
+ return texture(heatMap,pos).r;
66
+ }
67
+
68
+ in vec2 vUv;
69
+ in vec3 vPosition;
70
+ in vec3 vNormal;
71
+ out highp vec4 pc_fragColor;
72
+
73
+
74
+ float normalizeHeat(float t){
75
+ float _t = clamp(t,minT,maxT);
76
+ return (_t - minT)/(maxT - minT);
77
+ }
78
+
79
+ vec3 posToTexCood(vec3 pos){
80
+ float x = (pos.x - bboxMin.x) / (bboxMax.x - bboxMin.x);
81
+ float y = (pos.y - bboxMin.y) / (bboxMax.y - bboxMin.y);
82
+ float z = (pos.z - bboxMin.z) / (bboxMax.z - bboxMin.z);
83
+ return vec3(x,y,z);
84
+ }
85
+
86
+
87
+ void main() {
88
+ vec3 texCood = posToTexCood(vPosition);
89
+ // float color = sampleByNormal(vNormal,texCood);
90
+ float color = sample1(vNormal,texCood);
91
+ float normalColor= normalizeHeat(color);
92
+ vec4 colorMapColor = texture(colorMap,vec2(normalColor,0.2));
93
+ pc_fragColor = vec4(colorMapColor.xyz,opacity);
94
+ // pc_fragColor = linearToOutputTexel(vec4(colorMapColor.xyz,opacity));
95
+ }
96
+ `;
@@ -0,0 +1,2 @@
1
+ declare var _default: "#version 300 es\n\tin vec3 position;\n\tin vec3 normal;\n\tout vec3 vPosition;\n\tout vec3 vNormal;\n\n\tuniform mat4 modelViewMatrix;\n\tuniform mat4 projectionMatrix;\n\tuniform vec3 cameraPos;\n\t// uniform vec3 normalMatrix;\n\tuniform mat4 modelMatrix;\n\n\tvoid main() {\n\n\t\tvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n\t\tvec4 worldPosition = modelMatrix * vec4(position.xyz,1.0);\n\t\tvPosition = worldPosition.xyz;\n\t\t// vec3 transformedNormal = normalMatrix * normal;\n\t\tvNormal = normalize(normal);\n\n\t\tgl_Position = projectionMatrix * mvPosition;\n\t}\n";
2
+ export default _default;
@@ -0,0 +1,23 @@
1
+ export default `#version 300 es
2
+ in vec3 position;
3
+ in vec3 normal;
4
+ out vec3 vPosition;
5
+ out vec3 vNormal;
6
+
7
+ uniform mat4 modelViewMatrix;
8
+ uniform mat4 projectionMatrix;
9
+ uniform vec3 cameraPos;
10
+ // uniform vec3 normalMatrix;
11
+ uniform mat4 modelMatrix;
12
+
13
+ void main() {
14
+
15
+ vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
16
+ vec4 worldPosition = modelMatrix * vec4(position.xyz,1.0);
17
+ vPosition = worldPosition.xyz;
18
+ // vec3 transformedNormal = normalMatrix * normal;
19
+ vNormal = normalize(normal);
20
+
21
+ gl_Position = projectionMatrix * mvPosition;
22
+ }
23
+ `;
@@ -57,7 +57,7 @@ function create_fragment(ctx) {
57
57
  div3 = element("div");
58
58
  div2 = element("div");
59
59
  attr(div0, "class", "PanoSpatialTagPlugin__tag-shadow svelte-8ki8yx");
60
- set_style(div0, "transform", "translate(" + /*contentWidth*/ ctx[7] / 2 * /*contentZoom*/ ctx[3] * 3 + "px, " + /*contentHeight*/ ctx[8] / 2 * (/*upsideDown*/ ctx[1] ? -1 : 1) * /*contentZoom*/ ctx[3] * 3 + "px)");
60
+ set_style(div0, "transform", "translate(" + /*contentWidth*/ ctx[7] / 2 * /*contentZoom*/ ctx[3] * 3 + "px, " + /*contentHeight*/ ctx[8] / 2 * (/*upsideDown*/ ctx[1] ? -1 : 1) * /*contentZoom*/ ctx[3] * 3 + "px) scale(" + 1 / /*contentZoom*/ ctx[3] + ")");
61
61
  attr(i0, "class", "PanoSpatialTagPlugin__tag-flagpole svelte-8ki8yx");
62
62
  set_style(i0, "width", /*lineWidthZoom*/ ctx[4] + "rem");
63
63
  attr(i1, "class", "PanoSpatialTagPlugin__tag-line1 svelte-8ki8yx");
@@ -97,7 +97,7 @@ function create_fragment(ctx) {
97
97
  },
98
98
  p(ctx, [dirty]) {
99
99
  if (dirty & /*contentWidth, contentZoom, contentHeight, upsideDown*/ 394) {
100
- set_style(div0, "transform", "translate(" + /*contentWidth*/ ctx[7] / 2 * /*contentZoom*/ ctx[3] * 3 + "px, " + /*contentHeight*/ ctx[8] / 2 * (/*upsideDown*/ ctx[1] ? -1 : 1) * /*contentZoom*/ ctx[3] * 3 + "px)");
100
+ set_style(div0, "transform", "translate(" + /*contentWidth*/ ctx[7] / 2 * /*contentZoom*/ ctx[3] * 3 + "px, " + /*contentHeight*/ ctx[8] / 2 * (/*upsideDown*/ ctx[1] ? -1 : 1) * /*contentZoom*/ ctx[3] * 3 + "px) scale(" + 1 / /*contentZoom*/ ctx[3] + ")");
101
101
  }
102
102
 
103
103
  if (dirty & /*lineWidthZoom*/ 16) {
@@ -15,6 +15,8 @@ export interface PanoSpatialTagPluginParameterType {
15
15
  minRad?: number;
16
16
  nearTolerance?: number;
17
17
  upsideHeight?: number;
18
+ minDistance?: number;
19
+ maxDistance?: number;
18
20
  }
19
21
  export interface PanoSpatialTagPluginExportType {
20
22
  load: (data: PanoSpatialTagPluginData) => void;
@@ -6,22 +6,22 @@ import Tag from './Components/tag';
6
6
  import { pluginStyle } from './style';
7
7
  import render from '../shared-utils/tinyEJSrender';
8
8
  const MESH_SIZE = 0.001;
9
- const RAY_ORIGIN_Y = 1.4;
10
9
  const RAY_TOLERANT_DISTANCE = 0.01;
11
10
  const BLUR_IMAGE_URL = 'https://vrlab-image4.ljcdn.com/release/web/PanoSpatialTagPlugin__blur.png';
12
- const MIN_DISTANCE = 1.2;
13
- const MAX_DISTANCE = 3.5;
14
11
  /**
15
12
  * 空间游走标签插件
16
13
  */
17
14
  export const PanoSpatialTagPlugin = (five, params) => {
18
- var _a, _b, _c, _d, _e, _f;
15
+ var _a, _b, _c, _d, _e, _f, _g, _h;
19
16
  let wrapper = params === null || params === void 0 ? void 0 : params.container;
17
+ let centerY = 1.4;
20
18
  const wait = (_a = params === null || params === void 0 ? void 0 : params.wait) !== null && _a !== void 0 ? _a : 200;
21
19
  const maxNumberOnScreen = (_b = params === null || params === void 0 ? void 0 : params.maxNumberOnScreen) !== null && _b !== void 0 ? _b : 3;
22
20
  const minRad = (_c = params === null || params === void 0 ? void 0 : params.minRad) !== null && _c !== void 0 ? _c : Math.PI / 4;
23
21
  const nearTolerance = (_d = params === null || params === void 0 ? void 0 : params.nearTolerance) !== null && _d !== void 0 ? _d : 100;
24
22
  const upsideHeight = (_e = params === null || params === void 0 ? void 0 : params.upsideHeight) !== null && _e !== void 0 ? _e : 1.6;
23
+ const minDistance = (_f = params === null || params === void 0 ? void 0 : params.minDistance) !== null && _f !== void 0 ? _f : 1.2;
24
+ const maxDistance = (_g = params === null || params === void 0 ? void 0 : params.maxDistance) !== null && _g !== void 0 ? _g : 3.5;
25
25
  // const foldPercent = params?.foldPercent ?? 20
26
26
  const css3DRender = CSS3DRenderPlugin(five);
27
27
  const container = document.createElement('div');
@@ -75,8 +75,8 @@ export const PanoSpatialTagPlugin = (five, params) => {
75
75
  projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
76
76
  frustum.setFromProjectionMatrix(projScreenMatrix);
77
77
  state.tags.forEach(tag => {
78
- const distance = camera.position.clone().setY(RAY_ORIGIN_Y).distanceTo(tag.position);
79
- if (distance < MIN_DISTANCE || distance > MAX_DISTANCE)
78
+ const distance = camera.position.clone().setY(centerY).distanceTo(tag.position);
79
+ if (distance < minDistance || distance > maxDistance)
80
80
  return tag.destroying = true;
81
81
  if (!frustum.containsPoint(tag.position))
82
82
  return tag.destroying = true;
@@ -88,16 +88,16 @@ export const PanoSpatialTagPlugin = (five, params) => {
88
88
  state.tags.forEach(tag => {
89
89
  if (tag.destroying) {
90
90
  tag.app.$set({
91
- contentZoom: 0.1 + camera.position.distanceTo(tag.position) / MAX_DISTANCE,
92
- lineWidthZoom: 0.38 * (0.01 + camera.position.distanceTo(tag.position) / MAX_DISTANCE),
91
+ contentZoom: 0.1 + camera.position.distanceTo(tag.position) / maxDistance,
92
+ lineWidthZoom: 0.38 * (0.01 + camera.position.distanceTo(tag.position) / maxDistance),
93
93
  destroying: tag.destroying,
94
94
  });
95
95
  }
96
96
  else {
97
97
  tag.app.$set({
98
- lineWidthZoom: 0.38 * (0.01 + camera.position.distanceTo(tag.position) / MAX_DISTANCE),
99
- lineHeightZoom: 0.4 + (camera.position.distanceTo(tag.position) - MIN_DISTANCE) / MAX_DISTANCE * 0.6,
100
- contentZoom: 0.1 + camera.position.distanceTo(tag.position) / MAX_DISTANCE,
98
+ lineWidthZoom: 0.38 * (0.01 + camera.position.distanceTo(tag.position) / maxDistance),
99
+ lineHeightZoom: 0.4 + (camera.position.distanceTo(tag.position) - minDistance) / maxDistance * 0.6,
100
+ contentZoom: 0.1 + camera.position.distanceTo(tag.position) / maxDistance,
101
101
  });
102
102
  }
103
103
  });
@@ -170,8 +170,8 @@ export const PanoSpatialTagPlugin = (five, params) => {
170
170
  const points = state.points.reduce((result, point) => {
171
171
  if (state.tags.find(tag => point.id === tag.id && !tag.destroying))
172
172
  return result;
173
- const distance = camera.position.clone().setY(RAY_ORIGIN_Y).distanceTo(point.position);
174
- if (distance < MIN_DISTANCE || distance > MAX_DISTANCE)
173
+ const distance = camera.position.clone().setY(centerY).distanceTo(point.position);
174
+ if (distance < minDistance || distance > maxDistance)
175
175
  return result;
176
176
  if (!frustum.containsPoint(point.position))
177
177
  return result;
@@ -231,10 +231,12 @@ export const PanoSpatialTagPlugin = (five, params) => {
231
231
  Math.pow((mouse.y - _mouse.y) / 2 * clientHeight, 2)) > nearTolerance;
232
232
  }))
233
233
  continue;
234
- const raycaster = new THREE.Raycaster(camera.position.clone().setY(RAY_ORIGIN_Y), point.position.clone().sub(camera.position.clone().setY(RAY_ORIGIN_Y)).normalize(), 0, point.distance + RAY_TOLERANT_DISTANCE);
235
- const [intersect] = five.model.bvhs.loaded ?
236
- five.model.intersectRaycaster(raycaster) :
237
- raycaster.intersectObjects(five.model.children, true);
234
+ const raycaster = new THREE.Raycaster(camera.position.clone().setY(centerY), point.position.clone().sub(camera.position.clone().setY(centerY)).normalize(), 0, point.distance + RAY_TOLERANT_DISTANCE);
235
+ const [intersect] = state.intersectObjects ?
236
+ raycaster.intersectObjects(state.intersectObjects, true) :
237
+ (five.model.bvhs.loaded ?
238
+ five.model.intersectRaycaster(raycaster) :
239
+ raycaster.intersectObjects(five.model.children, true));
238
240
  if (!intersect)
239
241
  continue;
240
242
  if (point.distance - intersect.distance < RAY_TOLERANT_DISTANCE) {
@@ -254,9 +256,9 @@ export const PanoSpatialTagPlugin = (five, params) => {
254
256
  props: {
255
257
  id,
256
258
  content: state.render(state.template, replacement),
257
- lineWidthZoom: 0.38 * (0.01 + camera.position.distanceTo(position) / MAX_DISTANCE),
258
- lineHeightZoom: 0.4 + (camera.position.distanceTo(position) - MIN_DISTANCE) / MAX_DISTANCE * 0.6,
259
- contentZoom: 0.1 + camera.position.distanceTo(position) / MAX_DISTANCE,
259
+ lineWidthZoom: 0.38 * (0.01 + camera.position.distanceTo(position) / maxDistance),
260
+ lineHeightZoom: 0.4 + (camera.position.distanceTo(position) - minDistance) / maxDistance * 0.6,
261
+ contentZoom: 0.1 + camera.position.distanceTo(position) / maxDistance,
260
262
  upsideDown: position.y > upsideHeight,
261
263
  folded: state.folded,
262
264
  events: state.events,
@@ -301,6 +303,13 @@ export const PanoSpatialTagPlugin = (five, params) => {
301
303
  if (data.folded === true)
302
304
  state.folded = data.folded;
303
305
  };
306
+ /** 设置射线碰撞检测objects
307
+ *
308
+ */
309
+ const setIntersectObjects = (objects) => {
310
+ state.intersectObjects = objects;
311
+ updateTags();
312
+ };
304
313
  /** 标签启用
305
314
  * 启用插件并重新计算展示标签
306
315
  */
@@ -360,6 +369,22 @@ export const PanoSpatialTagPlugin = (five, params) => {
360
369
  tag.app.$set({ folded: true });
361
370
  });
362
371
  };
372
+ const onResize = () => {
373
+ five.once('renderFrame', updateOrigins);
374
+ };
375
+ const onModelLoaded = () => {
376
+ if (!wrapper)
377
+ wrapper = five.getElement().parentElement;
378
+ if (wrapper)
379
+ wrapper.appendChild(container);
380
+ state.forbidden = false;
381
+ centerY = five.model.bounding.getCenter(new THREE.Vector3()).y;
382
+ updateTags();
383
+ five.on('panoWillArrive', onPanoWillArrive);
384
+ five.on('panoArrived', onPanoArrived);
385
+ five.on('modeChange', onModeChange);
386
+ five.on('cameraUpdate', onCameraUpdate);
387
+ };
363
388
  const dispose = () => {
364
389
  blurImage = null;
365
390
  css3DRender.disposeAll();
@@ -370,40 +395,24 @@ export const PanoSpatialTagPlugin = (five, params) => {
370
395
  });
371
396
  state.origins = [];
372
397
  state.tags = [];
398
+ five.off('modelLoaded', onModelLoaded);
399
+ five.off('renderFrame', updateOrigins);
373
400
  five.off('panoWillArrive', onPanoWillArrive);
374
401
  five.off('panoArrived', onPanoArrived);
375
402
  five.off('modeChange', onModeChange);
376
403
  five.off('cameraUpdate', onCameraUpdate);
404
+ five.off('dispose', dispose);
405
+ window.removeEventListener('resize', onResize, false);
377
406
  };
378
- if ((_f = five === null || five === void 0 ? void 0 : five.model) === null || _f === void 0 ? void 0 : _f.loaded) {
379
- if (!wrapper)
380
- wrapper = five.getElement().parentElement;
381
- if (wrapper)
382
- wrapper.appendChild(container);
383
- state.forbidden = false;
384
- updateTags();
385
- five.on('panoWillArrive', onPanoWillArrive);
386
- five.on('panoArrived', onPanoArrived);
387
- five.on('modeChange', onModeChange);
388
- five.on('cameraUpdate', onCameraUpdate);
389
- }
390
- else {
391
- five.once('modelLoaded', () => {
392
- if (!wrapper)
393
- wrapper = five.getElement().parentElement;
394
- if (wrapper)
395
- wrapper.appendChild(container);
396
- state.forbidden = false;
397
- updateTags();
398
- five.on('panoWillArrive', onPanoWillArrive);
399
- five.on('panoArrived', onPanoArrived);
400
- five.on('modeChange', onModeChange);
401
- five.on('cameraUpdate', onCameraUpdate);
402
- });
403
- }
407
+ window.addEventListener('resize', onResize, false);
408
+ if ((_h = five === null || five === void 0 ? void 0 : five.model) === null || _h === void 0 ? void 0 : _h.loaded)
409
+ onModelLoaded();
410
+ else
411
+ five.once('modelLoaded', onModelLoaded);
404
412
  five.on('dispose', dispose);
405
413
  return {
406
414
  load,
415
+ setIntersectObjects,
407
416
  unfoldAll,
408
417
  foldAll,
409
418
  unfold,
@@ -0,0 +1 @@
1
+ export declare const COMPASS_IMAGE = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAArUAAAB7CAMAAABdA4ieAAAC91BMVEUAAACOpP/H0P+yxP/a4/+asv/W3/+/z/+cs//S3f+mu//Q2//5+v/H1P/d5f/l6//y9f++zf/i6f+ww/+ovP/7/P/u8v/N2f+Mp//q7/+4yf+uwf+Wr/+7y/+1xv+Ho//x9P+9zP+2x/+swP+gt/+Oqf/Y4v/r8P/l7P/b4//F0/+juf+Zsv+XsP+Trf/p7v97mv9wj/+kuv+ftf/x9P92lf/3+f/I1f+5yv+Eof98m//8/f/4+f90lf+9zf/j6f/T3f/L1/+Bnv/6+/+SrP/m7P/f5v/O2v/L2P/E0v/Az//+///s8P/e5v+rv/+pvv+Anv+qvv+Pqv+Kpv+CoP93l/9xlP/z9v/p7v90lf9uj//P2//v8//o7f/k6v/K1v+6yv9rjv98nv/E0v/9/v/09//h6P/X4f9pjP/9/f/N2f/K1v/C0f+Ho/99nP/1+P9sj//6+//u8/+iuP/u8//g6P/U3v+3x/+0xv+etf+Vrv9pjf/B0P9ukf+ht/+Vr/94mP/G0/+8zP+2x//+/v/09v/W4f9ojv/c5P+xxP/d5v+xw/+vwv+Jo//g6P+nvP+SrP/R3P/1+f/W3//U3//C0P+Jp/9nh/+Kpv+Fov/Y4f/J1/+Pqf+rv/+Oqv+Oqv+muv+twP/f5//t8P97mf/h5/+twP+kuf/L2P+juP+Ipf+huP+Pqf/d5v+zxf+cs/+Zsf+wwv/V3v+8zP+Go/+zxP+GpP/T3f/r8P/u8v/o7f/d5P/m7P/H1P/w9P/D0f+uwf/Z4v/F0v/09//D0v/v8//s8v/d5P/Y4f/s8P+etv/X4f+gt/+ovf+bsv+xw/+Vr//j6v+1x//J1v+4yf/r8P+yxf/T3f/B0f+Fo//09//W4P/h6P/L1v/i6f/x9f/t8v/G0/+uwf/z9f/7/P/n7f/z9v/T3f+4yf/J1v/z9f/w8//q7v/O2v/8/f/5+v/y9f/w8//g6P/z9v+/zv+5yf+9zf/5+v/Q3P/B0P/u8f/R2//////LSJnaAAAA/HRSTlMAAwGMx2jAn2u7ebn0q8zX7J3TiHz45bRS35SFYZmPSembkYNwVMXh2cmodWVjXN43DHZu6gfyrJZGOvrzLAbVvbBB91raz7aypqH+4s6Bfj9/Vk9EMint3C8gt+fc1q+XGAml/O/SxBz7s66kSz3wI/bjc+fQvpOObV8VoyZyXjSpmgv97sMPyorNiYZN0XpYuvHCv6JNEhEWw61YgCUdGBIP5CIgHypaUEU6NS9JQy8lGlY6NCuMfVtPSzspJoRxcExBNTMUyn9zaWRfVkk9Oad9emtkY0RBMLGsnJaQj4ZmXVHhsaKbeW9owb466dnVy7mYj45wyaGV3bdGzuNnAAASkklEQVR42uyaS0iUURiGTUQZ1MaMURSEbOFt4ybGQClFHO9rBS8F3nAURHG0hZvyQrgQRsfNuKmRCTcGGaEzThNqMyouwswKNTVMLe/a/bLo+845/5z/dzIiumj971I3gz6+Puc9x0POT6bRaXfYrJa18YHFhYXXKzMz6+/fb2+/ePFia8uvWlXelFkcr6pO2NqCr2xv77xfn5lZWXm9sDgwvmZZGnHYzaZjHnLk/PaYzL0jS9Pji6+XN3Z3gvJPnTkTEJCT01xSUtWuVick+PlVJ6tUqvj44uLipkxIU3F5fLxKlZxc7efnl6BWt1eVlDTn5AQEnDl16lR+be2n3fWZlYWBNcugze485yFHzq/Kub7eEcv44tzyekpsbEhIZGRWVk1NUFBtPqU2gFGbANRWVycnI7XlCO0XQm25mFp1VRWjFrDNz68NCqqpyYqMDAmJjc3NzQ3cmJwfmLba7CZPDzlyfg5Wh3V6YG559HyhUhkVlZJSUJDLqK3pDkJqoS8ZtVWEWiCTUltejl37BaCVdG2Cul1MLUIb1M2pLShIiYpSKpWF58+37U3Oj1ue2E0ecuT8EK1mm3VoYG4vW6MrLQ0MDDwvUIvQ8q6tBWqZIeRQQ+DU8q51NwQxtbVAbRajNrZARG1gaWBpm0anyY6enB+bGOyV6ZVzIK6bPUPzs17Rx48fb80GaHVtpQRaF7XYtZFIbTcRBAFb7rWc2uJM1rVoCCqgFquWdy0TBDCEbqSWlm2KQC38rZSW6nS67OxW+CxXr0Z7ec/J8MqRxtQ7ONQ/2xIREeHlFR191UVtqRu1wBep2hpqCNxrpYYg6dp4Ude2Y9cG8K5FQ8hyNwSglnZtNoE22svLKyKirKxFsTo28cQsH9v+81Bevb29O1qMZQAtUhs9SqnVsK5199osRi3fENwNgXitcBqrrmZly7r2DKUWq7Zb8FrBEAoptW1tGg3rWk5tC3xSQ53ig8zuf5pzd4df9c8qFIrEOgOhtgyphdCubaVdC9iyrkVo+Wnsm12bID6NZYqWLxX32qpm0fKVD9ii10bSrkVDiJJ2bWuriFqjsaUDPmpdXWIifG7f1bGezT55bfhfYrrfM7Wamufrq9cjtIkGA+1aZgiArdRrlUBtirRr2YbAvBZGBLV71/INAcuWbwjcEERey7tWKe7abGnXdiC1DFq9r29eampF/3O5dv/1ODd7pj5og4ODU13UQtdyQ+Beq9FovnMaE20IAdwQJHut2BCE05jaffkSDAGwxbJl1CK27DQ2iqcxCFKLXWsgXatX+BJqg4O1aWlJz14N35XR/RfTeP/2VOeFpKS0tDQCLVDrq9AjtXVIrdFYVsaoHWWGQKgN3EdtCNVa91sGtdQQmvhpjFUtX74QWjoioNdm0a5lGwL3Wp1GI/HaCCP1WkMddi1Cm5cH1BJsky5UxGHrysLw7wQd9pl/XFfFBaRWq9UCtalIrZ56rcG7A7u2jHktMQQN91qllFq2fImoba468G5M6rVq4rV8Q0CvZdTGgtcitrxrNUgtli1QCyFe6+0yBL3eF6Cl1CYBtYBtnL9/0b2ezWseco56QGLvpV++7O8fF1dRAdhC16Ih0K4lhpBoQBaMLS6vZacxTdu+DSFXsiHkU689YENgd2MSQ0iQ7rXiG90Qvnxh2eJhTOfqWihb7rXUEIBalyFotaRru+IQ28uXY9LBF27JD3OObPqePn9TlJ4eExNDqO0CapOQWsCWdq2CUmtgGwKnli9f6LWFrg0hhFEr7doS1rXSu7HM8nK3DaHd7W6shnutyxACqdficYxSyzYEgVqEVsENQYtdi9j6U2rTi4oqfaZu32n0kHO04nlr+N3HjIaGoqIipNafUNsF1KIicK9VUK8FQ8Cu5YbAqQVslfv2Wuq1+TU7uxszK/DycG1t2mJZWrKOjIzYHA673Wx2Ok3nPImbNJqcfWa7vdfhsMG3rUtLFss0PG3Eh43ruzvC8sW7lhkCgxYMQXzLYCReyzYESGoePY0JXesPiYlBbCsbOjMyMu713L/uIedo5NiNm+8++vhkALSVlaRrqSEAtNKu1VNqWddKN4Rs3f6uLdxYnsTXsdNLg7Ze8y95WOhp6rM7RqyW6fGB+bnJ5Q0lNQRyo+u+fOGGIMFWOI2Rqq2ghgDUYtcSbH18fE4AuXLnHvr0PX30JvzEiRM+gG0nYFsE2IIgQARD0LINIY9vCNLla1R8ozu6PLc4brE6zH/iEcAxp90GD83mJ/dAqjU64TTGvNYodC0uX2SvJV1LNwSkFg3B1bWEWvhBhIdfeSjbwiHO9QeP34aFXQkPp9AitWgIgtfGYdcecBrrMJYJy1c0gLIHb62GrE96+/7aCmoyOwYnhvrnJo3otXz5Yl6roNCmptLTGHYtei1imy50rQ+lFrANC7v48ra86B6+HLs1/PDs2YsA7RWAlmLb0MkNgXStdENwv2WIUKzi2yrnoZo98ekkPpYQ7saQWtq1dPlCr+WGIO1axJZSe/Hs2bePHsiae4jSeOfxpdBQhJZULe/aSmIISG0cNwTetQoIMQTDbP9Qj818mP+Pejp7ByfGVmeF5WufIVw4wGuxahm1oaGh9S9vyqPYYYgnnL1O19cDtEAt7VrqtXCIdnWtsCEkYdfyWwaF4jMp174j9JtsvGvrGer/oMcNQXzL0CVZvhoaMii1iG2YQG19/enTp08+li33K/tm7JNGGIbxqiEht5m7iasMcBuwwOJk0tEFcn9BG7s1bizQGBycCCbXpUkTo9VFJVpRSeNkuBqnq6BRoiwOMtldm3Tr+37fJ9+dtFEUbNX3+RPIj+d+93zf/ctgyZb1YDAUAmx510J410JY1wqvbTeEma3vJ9uP9571yObu8rfTd6xrhSEIahFb2bXCayW1wWAwM2CvLf5XFvSMMr9fzw9kMhnEFqmVXSsMYQI3hFFJLUD7GixwZuvHU/kuoG+zsXx0CtS+Yl4runZ0fHzcvSEAtUPcEEKCWshhdYlezx44I0vV80J+AILQQgIuagdby9e1vXbm9Ogp3kmd2j45q7z1Ll/tXRuAoCEAtQzbQkE93qdLCw+Wsa9NXVXVfB6xdXftsOxauSHwszHgtfG0L6IufNkBdhm0vGul10Kk14quzQO1qqqDK7yg9Dwj682wrk+raqEgoGXUtrp2yGUI3Gt/we3T5/L+0Q+6W/mIXTvR7rWArTSEfJ5Rq+the40at5dBMTDDCC2jVhoCYjscAGo5tBC+fA0elXefhsF2dqTdOKtMyLlWvo2F3NQCtNPTiG04aZcI3B6lb6mqJZPhK2rVFrVBFATRtXKvrZw1Fh/RotXdoOzu/LwYhLj3Wo8hCGrDQG1y1jTIcbufvo2ilTJnZxFbnRsC81qxIYQ8XntRpy9SWOZ3y+dv2pevjPRaQa1pplIph87OuoxsXEulTJND6zWEjPRagDZwXqbredd94bgiqfUYgsq6Ngldi9hqmna8Qj9etw6/bMvSNIYt/MAtQ8i7DQG79rLeoPH8b75QPg8wbINyQ+CGILtW0yxLUZoE7v2RrRmKolgaUouGwL1W93gtdu1lnY57bj5IPBRei+GGAHF3rYKJ7608l82lF5lacSLxOFLLutaUXeuhNlOnOyEdkMu9VnYtJGnysrUshDYej6SLG9QBd5TZdASgjSsQhNZFrS69FoilF6+OsgDkIrauDWEWu1ZjhoDYRiKRdNohU+g4CyUnl2tRa1nSEKTXFmwi9s7kNg89G4IwBNG1SG0uZ9BVhQ4PEwzDyKWBWoEtUis2BNG19toSydc9bx81D91dC9hKaiNIrWE4dPxwy8yXnEQCoL3WtSnetQhtsklXRLv1YajNli8BraYgtozaXBqgTSQS2cl1+q1vytj6pD+bBWpF10qvFV2btIukBV2WhVRrQxDQCkNAbLNZv1N7zoeMt/jr13x+P1LLyhawlYLAX8fobl33g0ZWtE2+11oearFr/RDf3gEV7p/z6WDS5/MJaF1eqwhslSo9q3qW/vn9Y63Na7Fqs0gtpDZHhXs9/Z9r0ShC6+5aQS1C65ToEKHXmVqvRtoNwQ9BbKPR96vUGt5H1IcYg9YjCDkxIcSrNB32ONLRSg7H1msIEMA2GqNJoZWp1ckYQCuoRWwZtUYaFSFdpNHwYbOwsodde7UhtLAFaGOxl7U5eub9Ztfscd0Eoihsu8oSKJBoQpUYyYULmhSIBVjACuip0tFQpEaaagTSyLiI9GSMZArEKp4it95N7oXBk6dUr7Ln59sB4uNw7p0BTlfb3s7Wcm0fvfbbV7MveAYo7k9eEB5h+x0Aa22b9OlKb9bHP7kNgLZC2slawKTs80Bx/2sIW8DO7dy7VSt9SfuoyHMurei1k7bnt/3K8ExQXLT2Y0MA8qLwro2mRaG6hV5RTNpuuba81345v+n+E3oR6su7aAjc2hyt9cL4qN+fcNNcw9DzPJG1i7VG2dfidHlHaYW1qK0H2oauZvvz/ZEyhtLyrBUN4WzuxLweIO6HrOXWhiQa6pUupHeLkRCYrbWB2dqzZh+vPGxOl1xoW3iztYyxqNWj4KadG0URm7z9tyHoPZi+PnBJRExjk7WEEHiTrtMof9T7q7NctJYwcBaYGgJwVf/R5Sft3bkh8KydrXVV93aHzs7WklBMY2b+kob65uE0NhcEQhhaa1mWo+7VmqqzgElaRlDbKWtD0wykAg6GikJkretO2saKelt1cRyjtQAD5qw1zUBCdr2L1jIWAdzaWEVvq44+pI0AErLQs/rdyiAjm9ONiF5rATGlpaPWwUM1ZHSxFokIY9FNn12fivw4toQwkbWobemPynhbD1lW0hKtfYTtVa3PUk+q3pp7LYDW0jLL2lGJ+yP14DhZltFFW5DW6swApgbrpn1kbVmCtJnj+KP0GyFwFsCspZRLq1j70Z2qsxC0loK0qK0vd95Wne/7IC1Q8qgd9DgB1Il0dKx4CVsH8P1E3n3C7t76AM/aErKW3s1FAxVZNweUllvrIwc5V5r7MUiSWVoMW5C2NdVAXao7DmNL1vpJkgy1dH/V9W9wNmmTxdqy7OR7CMMnV2EiaxMgkGzoXjdDEKC2S0HwR3OeoD6beuBZ26K0QXC4y/Pa/7Jrx7gJw1AYx2su0iuYgYFI7sAJstiS2ZKpk8UeWfKWKyQLLJ5Qxyp34TZ9D15YOkBpm9ji/U/A8CP+HFj0styMapHtZ54bh/t5y2E9LoQNVppMXoN1uixLQns8rl9PeZ0T3O96O5T4rL2wBQkuZnCbWQ7OXdUe1zwNni8YCsh2VOuK1I/a2jtHapHtZp/J+cD9bf0HoiW1rpAp38TfYlEUgJaetWUOZwP3P3UnetYCW1ChU52JogoS1UKIlq9gT95yILSugKRKciq2VhLaEpIVm3366j2pxaSMyf07YRUlND5qTynvGG6aaDOSWgnptFiIKoQLWmQ7pLphuOkTB0lqoZDSTGiVBrXE1iT0wbgEEn0gtSEEncoNfRV1uKKN/KqL+/4C1xYSA7XapjATRGU1hGxT3NtcGnUK0UJgZf7TuDWEls1yt9wiWsja7awzYRWttYgWYrPcLbejWqua2WaCqBSixQLvWe5mi06RWqv8TDOhNUrZC9sdm+Xuu5cpUqvU+wwzYbVVkIXYLHd/olekVpmpZ8KiMYbU7ma/EXJZBW5JrZl2V9bRnNWyWe4xt8TWTPd3FdEYSEFslnsoURlAi01FqN56f2br2xeOe9ytgbyf4nErGg/hl6RN4Jc5Lt++2rVjHMdCIIqiqtoRQW2mMogIyZFjlsB2B9wjjaY1PXa3wcbSPaEDR1fP9fVt8aPai2+f2+TlWu2l0yweZHlEO5S9cyu1lDKj5T/fWCH4jLaUnXMbvFyrzby8xRrayqUM2+ZWqvuM1nmpgHWkl8F9z9yG7LNa5yEMa0n8yHb93Er1oXjnoMVyFn3KQVcP7VRpFn+sDiznaiuHNufsHnkIwy7aPA/r5jbFGW3m7S12ku55WPN7Li1PPIRhN6sjtBjTmm+KmYcwPEOKs9tHF1JDHCoHLZ5DQ8yPBic9DoHjAKvsb85qjbFxHOCprMZaf5qdhlorxwGeTsPIttvPlno0y3GAZfbHp6n3ynGAV0m99u/2J6H3znGAvW4n+J25tdY7xwFey1pvQe6/DlrjOMDLqfXW7N5pbs0YWhxgtpj0rl1uiaHFISzccSWohUCzOIemm0VKChwHOMutKCVwHOA4av/JVi0xtDiRpCRfRcvQ4lBfxSnG0OJc/+xTjKHFyVRMP38iDC0O92lZVRhanO+vcVWGFu9BRWkW7+Z3rapEizdy7ZVm8V5Gtr8AtSnix59eOmYAAAAASUVORK5CYII=";
@@ -0,0 +1 @@
1
+ export const COMPASS_IMAGE = 'data:image/png;base64,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';