@kimap/indoor-positioning-sdk-vue2 5.4.6 → 5.4.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kimap/indoor-positioning-sdk-vue2",
3
- "version": "5.4.6",
3
+ "version": "5.4.8",
4
4
  "description": "Vue2自包含室内定位SDK - 完全兼容Webpack3+Babel6 | Vue2 Self-Contained Indoor Positioning SDK",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -313,6 +313,34 @@
313
313
  return shapeOpacity;
314
314
  }
315
315
 
316
+ // 从OBJ内容中提取区域透明度信息
317
+ function extractAreaOpacityFromOBJ(objContent) {
318
+ var areaOpacity = {};
319
+ var lines = objContent.split('\n');
320
+ var currentAreaId = null;
321
+
322
+ for (var i = 0; i < lines.length; i++) {
323
+ var line = lines[i].trim();
324
+
325
+ if (line.indexOf('o Area_') === 0) {
326
+ // 提取区域ID(格式:Area_<id>)
327
+ var match = line.match(/Area_(.+)/);
328
+ if (match) {
329
+ currentAreaId = match[1];
330
+ }
331
+ } else if (currentAreaId && line.indexOf('# AREA_OPACITY') === 0) {
332
+ var opacity = parseFloat(line.split(' ')[2]);
333
+ if (!areaOpacity[currentAreaId]) {
334
+ areaOpacity[currentAreaId] = {};
335
+ }
336
+ areaOpacity[currentAreaId].opacity = opacity;
337
+ }
338
+ }
339
+
340
+ console.log('[Parsers] 提取到的区域透明度:', Object.keys(areaOpacity).length, '个区域');
341
+ return areaOpacity;
342
+ }
343
+
316
344
  /**
317
345
  * 3D 场景核心
318
346
  */
@@ -753,6 +781,11 @@
753
781
  if (shapeOpacity && Object.keys(shapeOpacity).length > 0) {
754
782
  self._shapeOpacity = shapeOpacity;
755
783
  }
784
+
785
+ var areaOpacity = extractAreaOpacityFromOBJ(objContent);
786
+ if (areaOpacity && Object.keys(areaOpacity).length > 0) {
787
+ self._areaOpacity = areaOpacity;
788
+ }
756
789
  }
757
790
 
758
791
  var object = objLoader.parse(objContent);
@@ -860,6 +893,11 @@
860
893
  self._shapeOpacity = shapeOpacity;
861
894
  }
862
895
 
896
+ var areaOpacity = extractAreaOpacityFromOBJ(objContent);
897
+ if (areaOpacity && Object.keys(areaOpacity).length > 0) {
898
+ self._areaOpacity = areaOpacity;
899
+ }
900
+
863
901
  var object = objLoader.parse(objContent);
864
902
  self.processModel(object, colorMap);
865
903
 
@@ -1087,7 +1125,14 @@
1087
1125
  console.log('✅ 从OBJ提取到形状透明度:', Object.keys(shapeOpacity).length, '个形状');
1088
1126
  self._shapeOpacity = shapeOpacity;
1089
1127
  }
1090
-
1128
+
1129
+ // 提取区域透明度信息
1130
+ var areaOpacity = extractAreaOpacityFromOBJ(objContent);
1131
+ if (areaOpacity && Object.keys(areaOpacity).length > 0) {
1132
+ console.log('✅ 从OBJ提取到区域透明度:', Object.keys(areaOpacity).length, '个区域');
1133
+ self._areaOpacity = areaOpacity;
1134
+ }
1135
+
1091
1136
  var object = objLoader.parse(objContent);
1092
1137
  self.processModel(object, colorMap);
1093
1138
 
@@ -1191,6 +1236,15 @@
1191
1236
  roughness = 0.95; // 与three-helper.ts一致
1192
1237
  metalness = 0; // 与three-helper.ts一致,非金属材质
1193
1238
  transparent = true; // 区域支持透明
1239
+ // 从OBJ提取的区域透明度数据中读取
1240
+ if (self._areaOpacity) {
1241
+ var areaId = child.material.name.replace('Area_', '');
1242
+ if (self._areaOpacity[areaId]) {
1243
+ opacity = self._areaOpacity[areaId].opacity;
1244
+ transparent = opacity < 1.0;
1245
+ console.log('[SceneCore] 应用区域透明度:', areaId, '=', opacity);
1246
+ }
1247
+ }
1194
1248
  // 区域通常是高透明度的,关闭depthWrite避免与3D家具的深度冲突
1195
1249
  depthWrite = false;
1196
1250
  // 添加轻微emissive提亮区域颜色
@@ -916,10 +916,11 @@ KimapSDK.prototype._zoomToShowAllFloors = function() {
916
916
  var maxSize = Math.max(size.x, size.z);
917
917
  var distance = maxSize * 2;
918
918
 
919
- // 计算目标位置
919
+ // 计算目标位置 - 从侧面斜45度角看过去
920
+ // X方向偏移distance,Z方向也偏移distance,形成45度角
920
921
  var targetPosition = new THREE.Vector3(
921
- center.x,
922
- center.y + distance * 1.5,
922
+ center.x + distance,
923
+ center.y + distance * 0.8,
923
924
  center.z + distance
924
925
  );
925
926
 
@@ -29,6 +29,7 @@ var extractColorsFromOBJ = parsers.extractColorsFromOBJ;
29
29
  var extractTextElementsFromOBJ = parsers.extractTextElementsFromOBJ;
30
30
  var extractWallOpacityFromOBJ = parsers.extractWallOpacityFromOBJ;
31
31
  var extractShapeOpacityFromOBJ = parsers.extractShapeOpacityFromOBJ;
32
+ var extractAreaOpacityFromOBJ = parsers.extractAreaOpacityFromOBJ;
32
33
  var extractCalibrationBoundaryAreaIdsFromOBJ = parsers.extractCalibrationBoundaryAreaIdsFromOBJ;
33
34
 
34
35
  /**
@@ -452,7 +453,13 @@ SceneCore.prototype._loadKimapFile = function(kimapUrl, themeUrl, objLoader, mtl
452
453
  if (shapeOpacity && Object.keys(shapeOpacity).length > 0) {
453
454
  self._shapeOpacity = shapeOpacity;
454
455
  }
455
-
456
+
457
+ // 提取区域透明度信息
458
+ var areaOpacity = extractAreaOpacityFromOBJ(objContent);
459
+ if (areaOpacity && Object.keys(areaOpacity).length > 0) {
460
+ self._areaOpacity = areaOpacity;
461
+ }
462
+
456
463
  // 解析OBJ内容
457
464
  var object = objLoader.parse(objContent);
458
465
 
@@ -621,6 +628,11 @@ SceneCore.prototype._loadKimapFileToGroup = function(kimapUrl, themeUrl, objLoad
621
628
  if (shapeOpacity && Object.keys(shapeOpacity).length > 0) {
622
629
  self._shapeOpacity = shapeOpacity;
623
630
  }
631
+
632
+ var areaOpacity = extractAreaOpacityFromOBJ(objContent);
633
+ if (areaOpacity && Object.keys(areaOpacity).length > 0) {
634
+ self._areaOpacity = areaOpacity;
635
+ }
624
636
  }
625
637
 
626
638
  var object = objLoader.parse(objContent);
@@ -1146,11 +1158,26 @@ SceneCore.prototype._processMaterial = function(child, materials, colorMap) {
1146
1158
  // 与主应用 three-helper.ts 保持一致
1147
1159
  // =====================================================
1148
1160
  var depthWrite = true;
1149
-
1161
+ var emissiveEnabled = false;
1162
+ var emissiveColor = 0x000000;
1163
+ var emissiveIntensity = 0;
1164
+
1150
1165
  if (child.material.name.startsWith('Area_')) {
1151
1166
  roughness = 0.5;
1152
1167
  metalness = 0.2;
1153
1168
  transparent = true;
1169
+ // 从OBJ提取的区域透明度数据中读取
1170
+ if (self._areaOpacity) {
1171
+ var areaId = child.material.name.replace('Area_', '');
1172
+ if (self._areaOpacity[areaId]) {
1173
+ opacity = self._areaOpacity[areaId].opacity;
1174
+ transparent = opacity < 1.0;
1175
+ }
1176
+ }
1177
+ // 添加轻微emissive提亮区域颜色
1178
+ emissiveEnabled = true;
1179
+ emissiveColor = mtlColor;
1180
+ emissiveIntensity = 0.15;
1154
1181
  // 区域通常是高透明度的,关闭depthWrite避免与3D家具的深度冲突
1155
1182
  depthWrite = false;
1156
1183
  } else if (child.material.name.startsWith('Wall_')) {
@@ -1196,6 +1223,8 @@ SceneCore.prototype._processMaterial = function(child, materials, colorMap) {
1196
1223
  transparent: transparent,
1197
1224
  roughness: roughness,
1198
1225
  metalness: metalness,
1226
+ emissive: emissiveEnabled ? new THREE.Color(emissiveColor) : new THREE.Color(0x000000),
1227
+ emissiveIntensity: emissiveEnabled ? emissiveIntensity : 0,
1199
1228
  side: THREE.DoubleSide,
1200
1229
  depthWrite: depthWrite // 高透明度时关闭depthWrite避免频闪
1201
1230
  });
@@ -1,277 +1,308 @@
1
- /**
2
- * OBJ 文件解析器模块
3
- */
4
-
5
- /**
6
- * 从OBJ内容中提取边界信息
7
- * 格式示例:
8
- * # KIMAP_BORDER_MIN_X 0.000000
9
- * # KIMAP_BORDER_MAX_Z 8.200000
10
- */
11
- function extractBorderFromOBJ(objContent) {
12
- var border = {
13
- min: { x: null, y: null, z: null },
14
- max: { x: null, y: null, z: null }
15
- };
16
-
17
- var lines = objContent.split('\n');
18
-
19
- for (var i = 0; i < lines.length; i++) {
20
- var line = lines[i].trim();
21
-
22
- if (line.indexOf('# KIMAP_BORDER_MIN_X') === 0) {
23
- border.min.x = parseFloat(line.split(' ')[2]);
24
- } else if (line.indexOf('# KIMAP_BORDER_MIN_Y') === 0) {
25
- border.min.y = parseFloat(line.split(' ')[2]);
26
- } else if (line.indexOf('# KIMAP_BORDER_MIN_Z') === 0) {
27
- border.min.z = parseFloat(line.split(' ')[2]);
28
- } else if (line.indexOf('# KIMAP_BORDER_MAX_X') === 0) {
29
- border.max.x = parseFloat(line.split(' ')[2]);
30
- } else if (line.indexOf('# KIMAP_BORDER_MAX_Y') === 0) {
31
- border.max.y = parseFloat(line.split(' ')[2]);
32
- } else if (line.indexOf('# KIMAP_BORDER_MAX_Z') === 0) {
33
- border.max.z = parseFloat(line.split(' ')[2]);
34
- }
35
- }
36
-
37
- return border;
38
- }
39
-
40
- /**
41
- * 从OBJ内容中提取地图配置信息
42
- * 格式示例:
43
- * # KIMAP_CONFIG_IS_CALIBRATED true
44
- * # KIMAP_CONFIG_CALIBRATION_SCALE 1.0
45
- * # KIMAP_CONFIG_REAL_WIDTH 1000.0
46
- * # KIMAP_CONFIG_REAL_HEIGHT 800.0
47
- * # KIMAP_CONFIG_MODEL_TO_REAL_SCALE 2.0000
48
- */
49
- function extractMapConfigFromOBJ(objContent) {
50
- var config = {
51
- isCalibrated: false,
52
- calibrationScale: null,
53
- realWidth: null,
54
- realHeight: null,
55
- modelToRealScale: null
56
- };
57
-
58
- var lines = objContent.split('\n');
59
- var foundConfig = false;
60
-
61
- for (var i = 0; i < lines.length; i++) {
62
- var line = lines[i].trim();
63
-
64
- if (line.indexOf('KIMAP_CONFIG_CALIBRATED') !== -1) {
65
- config.isCalibrated = line.split(' ')[2] === 'true';
66
- foundConfig = true;
67
- } else if (line.indexOf('KIMAP_CONFIG_CALIBRATION_SCALE') !== -1) {
68
- config.calibrationScale = parseFloat(line.split(' ')[2]);
69
- } else if (line.indexOf('KIMAP_CONFIG_REAL_WIDTH') !== -1) {
70
- config.realWidth = parseFloat(line.split(' ')[2]);
71
- } else if (line.indexOf('KIMAP_CONFIG_REAL_HEIGHT') !== -1) {
72
- config.realHeight = parseFloat(line.split(' ')[2]);
73
- } else if (line.indexOf('KIMAP_CONFIG_MODEL_TO_REAL_SCALE') !== -1) {
74
- config.modelToRealScale = parseFloat(line.split(' ')[2]);
75
- }
76
-
77
- // 只在遇到 End Config Info 时停止
78
- if (line.indexOf('End Config Info') !== -1) {
79
- break;
80
- }
81
- }
82
-
83
- return foundConfig ? config : null;
84
- }
85
-
86
- /**
87
- * 从OBJ内容中提取颜色信息
88
- */
89
- function extractColorsFromOBJ(objContent) {
90
- var colorMap = {};
91
- var lines = objContent.split('\n');
92
-
93
- for (var i = 0; i < lines.length; i++) {
94
- var line = lines[i].trim();
95
- if (line.startsWith('usemtl ')) {
96
- var colorName = line.substring(7).trim();
97
- colorMap[colorName] = true;
98
- }
99
- }
100
-
101
- return colorMap;
102
- }
103
-
104
- /**
105
- * 从配置中提取坐标系统信息
106
- */
107
- function extractCoordinateSystem(config) {
108
- return {
109
- origin: config.origin || { x: 0, y: 0, z: 0 },
110
- maxX: config.maxX,
111
- maxY: config.maxY
112
- };
113
- }
114
-
115
- /**
116
- * 从OBJ内容中提取文本元素
117
- * 格式示例:
118
- * # ===== Text Element =====
119
- * # TEXT_ID text_123
120
- * # TEXT_CONTENT Hello World
121
- * # TEXT_POSITION 1000 2000
122
- * # TEXT_ELEVATION 8500
123
- * # ===== End Text Element =====
124
- */
125
- function extractTextElementsFromOBJ(objContent) {
126
- var textElements = [];
127
- var lines = objContent.split('\n');
128
- var currentText = null;
129
-
130
- for (var i = 0; i < lines.length; i++) {
131
- var line = lines[i].trim();
132
-
133
- if (line.indexOf('# ===== Text Element =====') === 0) {
134
- currentText = {};
135
- } else if (line.indexOf('# ===== End Text Element =====') === 0 && currentText) {
136
- textElements.push(currentText);
137
- currentText = null;
138
- } else if (currentText) {
139
- if (line.indexOf('# TEXT_ID') === 0) {
140
- currentText.id = line.split(' ')[2];
141
- } else if (line.indexOf('# TEXT_CONTENT') === 0) {
142
- currentText.content = line.substring(line.indexOf('TEXT_CONTENT') + 13).trim();
143
- } else if (line.indexOf('# TEXT_POSITION') === 0) {
144
- var parts = line.split(' ');
145
- currentText.points = [{ x: parseFloat(parts[2]), y: parseFloat(parts[3]) }];
146
- } else if (line.indexOf('# TEXT_ELEVATION') === 0) {
147
- currentText.elevation = parseFloat(line.split(' ')[2]);
148
- } else if (line.indexOf('# TEXT_FONT_SIZE') === 0) {
149
- currentText.fontSize = parseFloat(line.split(' ')[2]);
150
- } else if (line.indexOf('# TEXT_COLOR') === 0) {
151
- currentText.color = line.split(' ')[2];
152
- } else if (line.indexOf('# TEXT_FONT_FAMILY') === 0) {
153
- currentText.fontFamily = line.substring(line.indexOf('TEXT_FONT_FAMILY') + 17).trim();
154
- } else if (line.indexOf('# TEXT_BOLD') === 0) {
155
- currentText.bold = line.split(' ')[2] === 'true';
156
- } else if (line.indexOf('# TEXT_ITALIC') === 0) {
157
- currentText.italic = line.split(' ')[2] === 'true';
158
- } else if (line.indexOf('# TEXT_BORDER_COLOR') === 0) {
159
- currentText.borderColor = line.split(' ')[2];
160
- } else if (line.indexOf('# TEXT_BG_COLOR') === 0) {
161
- currentText.backgroundColor = line.split(' ')[2];
162
- } else if (line.indexOf('# TEXT_BG_OPACITY') === 0) {
163
- currentText.backgroundOpacity = parseFloat(line.split(' ')[2]);
164
- } else if (line.indexOf('# TEXT_PADDING') === 0) {
165
- currentText.padding = parseFloat(line.split(' ')[2]);
166
- }
167
- }
168
- }
169
-
170
- return textElements;
171
- }
172
-
173
- /**
174
- * 从OBJ内容中提取墙体透明度信息
175
- * 格式示例:
176
- * o Wall_wall123_seg0
177
- * # WALL_OPACITY 0.85
178
- * # WALL_COLOR #d1d5db
179
- */
180
- function extractWallOpacityFromOBJ(objContent) {
181
- var wallOpacity = {};
182
- var lines = objContent.split('\n');
183
- var currentWallId = null;
184
-
185
- for (var i = 0; i < lines.length; i++) {
186
- var line = lines[i].trim();
187
-
188
- if (line.indexOf('o Wall_') === 0) {
189
- // 提取墙体ID(格式:Wall_<id>_seg<n>)
190
- var match = line.match(/Wall_([^_]+)_seg/);
191
- if (match) {
192
- currentWallId = match[1];
193
- }
194
- } else if (currentWallId && line.indexOf('# WALL_OPACITY') === 0) {
195
- var opacity = parseFloat(line.split(' ')[2]);
196
- if (!wallOpacity[currentWallId]) {
197
- wallOpacity[currentWallId] = {};
198
- }
199
- wallOpacity[currentWallId].opacity = opacity;
200
- } else if (currentWallId && line.indexOf('# WALL_COLOR') === 0) {
201
- var color = line.split(' ')[2];
202
- if (!wallOpacity[currentWallId]) {
203
- wallOpacity[currentWallId] = {};
204
- }
205
- wallOpacity[currentWallId].color = color;
206
- }
207
- }
208
-
209
- return wallOpacity;
210
- }
211
-
212
- /**
213
- * 从OBJ内容中提取形状透明度信息(矩形、圆形、多边形)
214
- * 格式: # SHAPE_OPACITY 0.9
215
- */
216
- function extractShapeOpacityFromOBJ(objContent) {
217
- var shapeOpacity = {};
218
- var lines = objContent.split('\n');
219
- var currentShapeId = null;
220
- var currentShapeType = null;
221
-
222
- for (var i = 0; i < lines.length; i++) {
223
- var line = lines[i].trim();
224
-
225
- if (line.indexOf('o Shape_') === 0) {
226
- // 提取形状ID和类型(格式:Shape_<type>_<id>)
227
- var match = line.match(/Shape_([^_]+)_(.+)/);
228
- if (match) {
229
- currentShapeType = match[1];
230
- currentShapeId = match[2];
231
- }
232
- } else if (currentShapeId && line.indexOf('# SHAPE_OPACITY') === 0) {
233
- var opacity = parseFloat(line.split(' ')[2]);
234
- if (!shapeOpacity[currentShapeId]) {
235
- shapeOpacity[currentShapeId] = {};
236
- }
237
- shapeOpacity[currentShapeId].opacity = opacity;
238
- shapeOpacity[currentShapeId].type = currentShapeType;
239
- }
240
- }
241
-
242
- return shapeOpacity;
243
- }
244
-
245
- /**
246
- * 从 OBJ 中提取「真实世界尺寸校准边界」区域 ID(与绘制端 objExporter 中 # KIMAP_AREA_CALIBRATION_BOUNDARY 对应)
247
- * 仅此类区域不应显示;普通 Area_ 地面应保留。
248
- */
249
- function extractCalibrationBoundaryAreaIdsFromOBJ(objContent) {
250
- var ids = [];
251
- if (!objContent || typeof objContent !== 'string') {
252
- return ids;
253
- }
254
- var lines = objContent.split('\n');
255
- var prefix = '# KIMAP_AREA_CALIBRATION_BOUNDARY ';
256
- for (var i = 0; i < lines.length; i++) {
257
- var line = lines[i].trim();
258
- if (line.indexOf(prefix) === 0) {
259
- var id = line.substring(prefix.length).trim();
260
- if (id) {
261
- ids.push(id);
262
- }
263
- }
264
- }
265
- return ids;
266
- }
267
-
268
- module.exports = {
269
- extractBorderFromOBJ: extractBorderFromOBJ,
270
- extractMapConfigFromOBJ: extractMapConfigFromOBJ,
271
- extractColorsFromOBJ: extractColorsFromOBJ,
272
- extractCoordinateSystem: extractCoordinateSystem,
273
- extractTextElementsFromOBJ: extractTextElementsFromOBJ,
274
- extractWallOpacityFromOBJ: extractWallOpacityFromOBJ,
275
- extractShapeOpacityFromOBJ: extractShapeOpacityFromOBJ,
276
- extractCalibrationBoundaryAreaIdsFromOBJ: extractCalibrationBoundaryAreaIdsFromOBJ
277
- };
1
+ /**
2
+ * OBJ 文件解析器模块
3
+ */
4
+
5
+ /**
6
+ * 从OBJ内容中提取边界信息
7
+ * 格式示例:
8
+ * # KIMAP_BORDER_MIN_X 0.000000
9
+ * # KIMAP_BORDER_MAX_Z 8.200000
10
+ */
11
+ function extractBorderFromOBJ(objContent) {
12
+ var border = {
13
+ min: { x: null, y: null, z: null },
14
+ max: { x: null, y: null, z: null }
15
+ };
16
+
17
+ var lines = objContent.split('\n');
18
+
19
+ for (var i = 0; i < lines.length; i++) {
20
+ var line = lines[i].trim();
21
+
22
+ if (line.indexOf('# KIMAP_BORDER_MIN_X') === 0) {
23
+ border.min.x = parseFloat(line.split(' ')[2]);
24
+ } else if (line.indexOf('# KIMAP_BORDER_MIN_Y') === 0) {
25
+ border.min.y = parseFloat(line.split(' ')[2]);
26
+ } else if (line.indexOf('# KIMAP_BORDER_MIN_Z') === 0) {
27
+ border.min.z = parseFloat(line.split(' ')[2]);
28
+ } else if (line.indexOf('# KIMAP_BORDER_MAX_X') === 0) {
29
+ border.max.x = parseFloat(line.split(' ')[2]);
30
+ } else if (line.indexOf('# KIMAP_BORDER_MAX_Y') === 0) {
31
+ border.max.y = parseFloat(line.split(' ')[2]);
32
+ } else if (line.indexOf('# KIMAP_BORDER_MAX_Z') === 0) {
33
+ border.max.z = parseFloat(line.split(' ')[2]);
34
+ }
35
+ }
36
+
37
+ return border;
38
+ }
39
+
40
+ /**
41
+ * 从OBJ内容中提取地图配置信息
42
+ * 格式示例:
43
+ * # KIMAP_CONFIG_IS_CALIBRATED true
44
+ * # KIMAP_CONFIG_CALIBRATION_SCALE 1.0
45
+ * # KIMAP_CONFIG_REAL_WIDTH 1000.0
46
+ * # KIMAP_CONFIG_REAL_HEIGHT 800.0
47
+ * # KIMAP_CONFIG_MODEL_TO_REAL_SCALE 2.0000
48
+ */
49
+ function extractMapConfigFromOBJ(objContent) {
50
+ var config = {
51
+ isCalibrated: false,
52
+ calibrationScale: null,
53
+ realWidth: null,
54
+ realHeight: null,
55
+ modelToRealScale: null
56
+ };
57
+
58
+ var lines = objContent.split('\n');
59
+ var foundConfig = false;
60
+
61
+ for (var i = 0; i < lines.length; i++) {
62
+ var line = lines[i].trim();
63
+
64
+ if (line.indexOf('KIMAP_CONFIG_CALIBRATED') !== -1) {
65
+ config.isCalibrated = line.split(' ')[2] === 'true';
66
+ foundConfig = true;
67
+ } else if (line.indexOf('KIMAP_CONFIG_CALIBRATION_SCALE') !== -1) {
68
+ config.calibrationScale = parseFloat(line.split(' ')[2]);
69
+ } else if (line.indexOf('KIMAP_CONFIG_REAL_WIDTH') !== -1) {
70
+ config.realWidth = parseFloat(line.split(' ')[2]);
71
+ } else if (line.indexOf('KIMAP_CONFIG_REAL_HEIGHT') !== -1) {
72
+ config.realHeight = parseFloat(line.split(' ')[2]);
73
+ } else if (line.indexOf('KIMAP_CONFIG_MODEL_TO_REAL_SCALE') !== -1) {
74
+ config.modelToRealScale = parseFloat(line.split(' ')[2]);
75
+ }
76
+
77
+ // 只在遇到 End Config Info 时停止
78
+ if (line.indexOf('End Config Info') !== -1) {
79
+ break;
80
+ }
81
+ }
82
+
83
+ return foundConfig ? config : null;
84
+ }
85
+
86
+ /**
87
+ * 从OBJ内容中提取颜色信息
88
+ */
89
+ function extractColorsFromOBJ(objContent) {
90
+ var colorMap = {};
91
+ var lines = objContent.split('\n');
92
+
93
+ for (var i = 0; i < lines.length; i++) {
94
+ var line = lines[i].trim();
95
+ if (line.startsWith('usemtl ')) {
96
+ var colorName = line.substring(7).trim();
97
+ colorMap[colorName] = true;
98
+ }
99
+ }
100
+
101
+ return colorMap;
102
+ }
103
+
104
+ /**
105
+ * 从配置中提取坐标系统信息
106
+ */
107
+ function extractCoordinateSystem(config) {
108
+ return {
109
+ origin: config.origin || { x: 0, y: 0, z: 0 },
110
+ maxX: config.maxX,
111
+ maxY: config.maxY
112
+ };
113
+ }
114
+
115
+ /**
116
+ * 从OBJ内容中提取文本元素
117
+ * 格式示例:
118
+ * # ===== Text Element =====
119
+ * # TEXT_ID text_123
120
+ * # TEXT_CONTENT Hello World
121
+ * # TEXT_POSITION 1000 2000
122
+ * # TEXT_ELEVATION 8500
123
+ * # ===== End Text Element =====
124
+ */
125
+ function extractTextElementsFromOBJ(objContent) {
126
+ var textElements = [];
127
+ var lines = objContent.split('\n');
128
+ var currentText = null;
129
+
130
+ for (var i = 0; i < lines.length; i++) {
131
+ var line = lines[i].trim();
132
+
133
+ if (line.indexOf('# ===== Text Element =====') === 0) {
134
+ currentText = {};
135
+ } else if (line.indexOf('# ===== End Text Element =====') === 0 && currentText) {
136
+ textElements.push(currentText);
137
+ currentText = null;
138
+ } else if (currentText) {
139
+ if (line.indexOf('# TEXT_ID') === 0) {
140
+ currentText.id = line.split(' ')[2];
141
+ } else if (line.indexOf('# TEXT_CONTENT') === 0) {
142
+ currentText.content = line.substring(line.indexOf('TEXT_CONTENT') + 13).trim();
143
+ } else if (line.indexOf('# TEXT_POSITION') === 0) {
144
+ var parts = line.split(' ');
145
+ currentText.points = [{ x: parseFloat(parts[2]), y: parseFloat(parts[3]) }];
146
+ } else if (line.indexOf('# TEXT_ELEVATION') === 0) {
147
+ currentText.elevation = parseFloat(line.split(' ')[2]);
148
+ } else if (line.indexOf('# TEXT_FONT_SIZE') === 0) {
149
+ currentText.fontSize = parseFloat(line.split(' ')[2]);
150
+ } else if (line.indexOf('# TEXT_COLOR') === 0) {
151
+ currentText.color = line.split(' ')[2];
152
+ } else if (line.indexOf('# TEXT_FONT_FAMILY') === 0) {
153
+ currentText.fontFamily = line.substring(line.indexOf('TEXT_FONT_FAMILY') + 17).trim();
154
+ } else if (line.indexOf('# TEXT_BOLD') === 0) {
155
+ currentText.bold = line.split(' ')[2] === 'true';
156
+ } else if (line.indexOf('# TEXT_ITALIC') === 0) {
157
+ currentText.italic = line.split(' ')[2] === 'true';
158
+ } else if (line.indexOf('# TEXT_BORDER_COLOR') === 0) {
159
+ currentText.borderColor = line.split(' ')[2];
160
+ } else if (line.indexOf('# TEXT_BG_COLOR') === 0) {
161
+ currentText.backgroundColor = line.split(' ')[2];
162
+ } else if (line.indexOf('# TEXT_BG_OPACITY') === 0) {
163
+ currentText.backgroundOpacity = parseFloat(line.split(' ')[2]);
164
+ } else if (line.indexOf('# TEXT_PADDING') === 0) {
165
+ currentText.padding = parseFloat(line.split(' ')[2]);
166
+ }
167
+ }
168
+ }
169
+
170
+ return textElements;
171
+ }
172
+
173
+ /**
174
+ * 从OBJ内容中提取墙体透明度信息
175
+ * 格式示例:
176
+ * o Wall_wall123_seg0
177
+ * # WALL_OPACITY 0.85
178
+ * # WALL_COLOR #d1d5db
179
+ */
180
+ function extractWallOpacityFromOBJ(objContent) {
181
+ var wallOpacity = {};
182
+ var lines = objContent.split('\n');
183
+ var currentWallId = null;
184
+
185
+ for (var i = 0; i < lines.length; i++) {
186
+ var line = lines[i].trim();
187
+
188
+ if (line.indexOf('o Wall_') === 0) {
189
+ // 提取墙体ID(格式:Wall_<id>_seg<n>)
190
+ var match = line.match(/Wall_([^_]+)_seg/);
191
+ if (match) {
192
+ currentWallId = match[1];
193
+ }
194
+ } else if (currentWallId && line.indexOf('# WALL_OPACITY') === 0) {
195
+ var opacity = parseFloat(line.split(' ')[2]);
196
+ if (!wallOpacity[currentWallId]) {
197
+ wallOpacity[currentWallId] = {};
198
+ }
199
+ wallOpacity[currentWallId].opacity = opacity;
200
+ } else if (currentWallId && line.indexOf('# WALL_COLOR') === 0) {
201
+ var color = line.split(' ')[2];
202
+ if (!wallOpacity[currentWallId]) {
203
+ wallOpacity[currentWallId] = {};
204
+ }
205
+ wallOpacity[currentWallId].color = color;
206
+ }
207
+ }
208
+
209
+ return wallOpacity;
210
+ }
211
+
212
+ /**
213
+ * 从OBJ内容中提取形状透明度信息(矩形、圆形、多边形)
214
+ * 格式: # SHAPE_OPACITY 0.9
215
+ */
216
+ function extractShapeOpacityFromOBJ(objContent) {
217
+ var shapeOpacity = {};
218
+ var lines = objContent.split('\n');
219
+ var currentShapeId = null;
220
+ var currentShapeType = null;
221
+
222
+ for (var i = 0; i < lines.length; i++) {
223
+ var line = lines[i].trim();
224
+
225
+ if (line.indexOf('o Shape_') === 0) {
226
+ // 提取形状ID和类型(格式:Shape_<type>_<id>)
227
+ var match = line.match(/Shape_([^_]+)_(.+)/);
228
+ if (match) {
229
+ currentShapeType = match[1];
230
+ currentShapeId = match[2];
231
+ }
232
+ } else if (currentShapeId && line.indexOf('# SHAPE_OPACITY') === 0) {
233
+ var opacity = parseFloat(line.split(' ')[2]);
234
+ if (!shapeOpacity[currentShapeId]) {
235
+ shapeOpacity[currentShapeId] = {};
236
+ }
237
+ shapeOpacity[currentShapeId].opacity = opacity;
238
+ shapeOpacity[currentShapeId].type = currentShapeType;
239
+ }
240
+ }
241
+
242
+ return shapeOpacity;
243
+ }
244
+
245
+ /**
246
+ * 从OBJ内容中提取区域透明度信息
247
+ * 格式: # AREA_OPACITY 0.9
248
+ */
249
+ function extractAreaOpacityFromOBJ(objContent) {
250
+ var areaOpacity = {};
251
+ var lines = objContent.split('\n');
252
+ var currentAreaId = null;
253
+
254
+ for (var i = 0; i < lines.length; i++) {
255
+ var line = lines[i].trim();
256
+
257
+ if (line.indexOf('o Area_') === 0) {
258
+ // 提取区域ID(格式:Area_<id>)
259
+ var match = line.match(/Area_(.+)/);
260
+ if (match) {
261
+ currentAreaId = match[1];
262
+ }
263
+ } else if (currentAreaId && line.indexOf('# AREA_OPACITY') === 0) {
264
+ var opacity = parseFloat(line.split(' ')[2]);
265
+ if (!areaOpacity[currentAreaId]) {
266
+ areaOpacity[currentAreaId] = {};
267
+ }
268
+ areaOpacity[currentAreaId].opacity = opacity;
269
+ }
270
+ }
271
+
272
+ return areaOpacity;
273
+ }
274
+
275
+ /**
276
+ * 从 OBJ 中提取「真实世界尺寸校准边界」区域 ID(与绘制端 objExporter 中 # KIMAP_AREA_CALIBRATION_BOUNDARY 对应)
277
+ * 仅此类区域不应显示;普通 Area_ 地面应保留。
278
+ */
279
+ function extractCalibrationBoundaryAreaIdsFromOBJ(objContent) {
280
+ var ids = [];
281
+ if (!objContent || typeof objContent !== 'string') {
282
+ return ids;
283
+ }
284
+ var lines = objContent.split('\n');
285
+ var prefix = '# KIMAP_AREA_CALIBRATION_BOUNDARY ';
286
+ for (var i = 0; i < lines.length; i++) {
287
+ var line = lines[i].trim();
288
+ if (line.indexOf(prefix) === 0) {
289
+ var id = line.substring(prefix.length).trim();
290
+ if (id) {
291
+ ids.push(id);
292
+ }
293
+ }
294
+ }
295
+ return ids;
296
+ }
297
+
298
+ module.exports = {
299
+ extractBorderFromOBJ: extractBorderFromOBJ,
300
+ extractMapConfigFromOBJ: extractMapConfigFromOBJ,
301
+ extractColorsFromOBJ: extractColorsFromOBJ,
302
+ extractCoordinateSystem: extractCoordinateSystem,
303
+ extractTextElementsFromOBJ: extractTextElementsFromOBJ,
304
+ extractWallOpacityFromOBJ: extractWallOpacityFromOBJ,
305
+ extractShapeOpacityFromOBJ: extractShapeOpacityFromOBJ,
306
+ extractAreaOpacityFromOBJ: extractAreaOpacityFromOBJ,
307
+ extractCalibrationBoundaryAreaIdsFromOBJ: extractCalibrationBoundaryAreaIdsFromOBJ
308
+ };