@kimap/indoor-positioning-sdk-vue2 4.3.1 → 4.3.2

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": "4.3.1",
3
+ "version": "4.3.2",
4
4
  "description": "Vue2自包含室内定位SDK - 完全兼容Webpack3+Babel6 | Vue2 Self-Contained Indoor Positioning SDK",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -193,6 +193,90 @@
193
193
  return colorMap;
194
194
  }
195
195
 
196
+ // 从OBJ内容中提取文本元素
197
+ function extractTextElementsFromOBJ(objContent) {
198
+ var textElements = [];
199
+ var lines = objContent.split('\n');
200
+ var currentText = null;
201
+
202
+ for (var i = 0; i < lines.length; i++) {
203
+ var line = lines[i].trim();
204
+
205
+ if (line.indexOf('# ===== Text Element =====') === 0) {
206
+ currentText = {};
207
+ } else if (line.indexOf('# ===== End Text Element =====') === 0 && currentText) {
208
+ textElements.push(currentText);
209
+ currentText = null;
210
+ } else if (currentText) {
211
+ if (line.indexOf('# TEXT_ID') === 0) {
212
+ currentText.id = line.split(' ')[2];
213
+ } else if (line.indexOf('# TEXT_CONTENT') === 0) {
214
+ currentText.content = line.substring(line.indexOf('TEXT_CONTENT') + 13).trim();
215
+ } else if (line.indexOf('# TEXT_POSITION') === 0) {
216
+ var parts = line.split(' ');
217
+ currentText.points = [{ x: parseFloat(parts[2]), y: parseFloat(parts[3]) }];
218
+ } else if (line.indexOf('# TEXT_ELEVATION') === 0) {
219
+ currentText.elevation = parseFloat(line.split(' ')[2]);
220
+ } else if (line.indexOf('# TEXT_FONT_SIZE') === 0) {
221
+ currentText.fontSize = parseFloat(line.split(' ')[2]);
222
+ } else if (line.indexOf('# TEXT_COLOR') === 0) {
223
+ currentText.color = line.split(' ')[2];
224
+ } else if (line.indexOf('# TEXT_FONT_FAMILY') === 0) {
225
+ currentText.fontFamily = line.substring(line.indexOf('TEXT_FONT_FAMILY') + 17).trim();
226
+ } else if (line.indexOf('# TEXT_BOLD') === 0) {
227
+ currentText.bold = line.split(' ')[2] === 'true';
228
+ } else if (line.indexOf('# TEXT_ITALIC') === 0) {
229
+ currentText.italic = line.split(' ')[2] === 'true';
230
+ } else if (line.indexOf('# TEXT_BORDER_COLOR') === 0) {
231
+ currentText.borderColor = line.split(' ')[2];
232
+ } else if (line.indexOf('# TEXT_BG_COLOR') === 0) {
233
+ currentText.backgroundColor = line.split(' ')[2];
234
+ } else if (line.indexOf('# TEXT_BG_OPACITY') === 0) {
235
+ currentText.backgroundOpacity = parseFloat(line.split(' ')[2]);
236
+ } else if (line.indexOf('# TEXT_PADDING') === 0) {
237
+ currentText.padding = parseFloat(line.split(' ')[2]);
238
+ }
239
+ }
240
+ }
241
+
242
+ console.log('[Parsers] 提取到的文本元素:', textElements.length, '个');
243
+ return textElements;
244
+ }
245
+
246
+ // 从OBJ内容中提取墙体透明度信息
247
+ function extractWallOpacityFromOBJ(objContent) {
248
+ var wallOpacity = {};
249
+ var lines = objContent.split('\n');
250
+ var currentWallId = null;
251
+
252
+ for (var i = 0; i < lines.length; i++) {
253
+ var line = lines[i].trim();
254
+
255
+ if (line.indexOf('o Wall_') === 0) {
256
+ // 提取墙体ID(格式:Wall_<id>_seg<n>)
257
+ var match = line.match(/Wall_([^_]+)_seg/);
258
+ if (match) {
259
+ currentWallId = match[1];
260
+ }
261
+ } else if (currentWallId && line.indexOf('# WALL_OPACITY') === 0) {
262
+ var opacity = parseFloat(line.split(' ')[2]);
263
+ if (!wallOpacity[currentWallId]) {
264
+ wallOpacity[currentWallId] = {};
265
+ }
266
+ wallOpacity[currentWallId].opacity = opacity;
267
+ } else if (currentWallId && line.indexOf('# WALL_COLOR') === 0) {
268
+ var color = line.split(' ')[2];
269
+ if (!wallOpacity[currentWallId]) {
270
+ wallOpacity[currentWallId] = {};
271
+ }
272
+ wallOpacity[currentWallId].color = color;
273
+ }
274
+ }
275
+
276
+ console.log('[Parsers] 提取到的墙体透明度:', Object.keys(wallOpacity).length, '个墙体');
277
+ return wallOpacity;
278
+ }
279
+
196
280
  /**
197
281
  * 3D 场景核心
198
282
  */
@@ -548,9 +632,28 @@
548
632
  console.log('提取到颜色信息:', colorMap);
549
633
  }
550
634
 
635
+ // 提取文本元素
636
+ var textElements = extractTextElementsFromOBJ(objContent);
637
+ if (textElements && textElements.length > 0) {
638
+ console.log('✅ 从OBJ提取到文本元素:', textElements.length, '个');
639
+ self._textElements = textElements;
640
+ }
641
+
642
+ // 提取墙体透明度信息
643
+ var wallOpacity = extractWallOpacityFromOBJ(objContent);
644
+ if (wallOpacity && Object.keys(wallOpacity).length > 0) {
645
+ console.log('✅ 从OBJ提取到墙体透明度:', Object.keys(wallOpacity).length, '个墙体');
646
+ self._wallOpacity = wallOpacity;
647
+ }
648
+
551
649
  var object = objLoader.parse(objContent);
552
650
  self.processModel(object, colorMap);
553
651
 
652
+ // 渲染文本元素
653
+ if (self._textElements && self._textElements.length > 0) {
654
+ self.renderTextElements(self._textElements);
655
+ }
656
+
554
657
  console.log('=== Kimap模型加载成功 ===');
555
658
  self.logModelInfo();
556
659
  resolve();
@@ -595,6 +698,7 @@
595
698
  };
596
699
 
597
700
  SceneCore.prototype.processModel = function(object, colorMap) {
701
+ var self = this;
598
702
  this.mapModel = object;
599
703
  colorMap = colorMap || {};
600
704
 
@@ -632,6 +736,16 @@
632
736
  } else if (child.material.name.startsWith('Wall_')) {
633
737
  roughness = 0.25;
634
738
  metalness = 0;
739
+
740
+ // 从OBJ提取的墙体透明度数据中读取
741
+ if (self._wallOpacity) {
742
+ var wallId = child.material.name.replace('Wall_', '');
743
+ if (self._wallOpacity[wallId]) {
744
+ opacity = self._wallOpacity[wallId].opacity;
745
+ transparent = opacity < 1.0;
746
+ console.log('[SceneCore] 应用墙体透明度:', wallId, '=', opacity);
747
+ }
748
+ }
635
749
  } else if (child.material.name.startsWith('Shape_') ||
636
750
  child.material.name.startsWith('Stair_')) {
637
751
  roughness = 0.25;
@@ -869,62 +983,86 @@
869
983
  var backgroundOpacity = options.backgroundOpacity !== undefined ? options.backgroundOpacity : 0.8;
870
984
  var padding = options.padding || 8;
871
985
 
872
- // 创建canvas
986
+ // 创建高分辨率canvas用于文本纹理
873
987
  var canvas = document.createElement('canvas');
874
988
  var context = canvas.getContext('2d');
875
989
 
876
- // 设置字体
877
- var fontStyle = (italic ? 'italic ' : '') + (bold ? 'bold ' : '') + fontSize + 'px ' + fontFamily;
990
+ // 使用更大的canvas尺寸以提高清晰度
991
+ canvas.width = 1024;
992
+ canvas.height = 512;
993
+
994
+ // 配置字体(放大4倍以提高质量)
995
+ var scaledFontSize = fontSize * 4;
996
+ var fontStyle = (italic ? 'italic ' : '') + (bold ? 'bold ' : '') + scaledFontSize + 'px ' + fontFamily;
878
997
  context.font = fontStyle;
998
+ context.textAlign = 'center';
999
+ context.textBaseline = 'middle';
879
1000
 
880
- // 测量文本尺寸
1001
+ // 测量文本
881
1002
  var metrics = context.measureText(content);
882
1003
  var textWidth = metrics.width;
883
- var textHeight = fontSize * 1.2;
1004
+ var textHeight = scaledFontSize * 1.2;
884
1005
 
885
- // 设置canvas尺寸(需要是2的幂次方以获得更好的性能)
886
- var canvasWidth = Math.pow(2, Math.ceil(Math.log2(textWidth + padding * 2)));
887
- var canvasHeight = Math.pow(2, Math.ceil(Math.log2(textHeight + padding * 2)));
888
- canvas.width = canvasWidth;
889
- canvas.height = canvasHeight;
1006
+ // 计算背景尺寸
1007
+ var scaledPadding = padding * 4;
1008
+ var bgWidth = textWidth + scaledPadding * 2;
1009
+ var bgHeight = textHeight + scaledPadding * 2;
890
1010
 
891
- // 重新设置字体(canvas尺寸改变后需要重新设置)
892
- context.font = fontStyle;
893
- context.textAlign = 'center';
894
- context.textBaseline = 'middle';
1011
+ // 计算居中位置
1012
+ var centerX = canvas.width / 2;
1013
+ var centerY = canvas.height / 2;
1014
+ var bgX = centerX - bgWidth / 2;
1015
+ var bgY = centerY - bgHeight / 2;
1016
+
1017
+ // 绘制阴影效果以增强可读性
1018
+ context.shadowColor = 'rgba(0, 0, 0, 0.5)';
1019
+ context.shadowBlur = 8;
1020
+ context.shadowOffsetX = 2;
1021
+ context.shadowOffsetY = 2;
895
1022
 
896
1023
  // 绘制背景
897
1024
  context.fillStyle = backgroundColor;
898
- context.globalAlpha = backgroundOpacity;
899
- context.fillRect(0, 0, canvasWidth, canvasHeight);
1025
+ context.globalAlpha = Math.max(backgroundOpacity, 0.9); // 确保背景足够不透明
1026
+ context.fillRect(bgX, bgY, bgWidth, bgHeight);
1027
+
1028
+ // 重置阴影
1029
+ context.shadowColor = 'transparent';
1030
+ context.shadowBlur = 0;
1031
+ context.shadowOffsetX = 0;
1032
+ context.shadowOffsetY = 0;
900
1033
 
901
- // 绘制边框
1034
+ // 绘制边框(加粗以提高可见性)
902
1035
  context.strokeStyle = borderColor;
903
- context.lineWidth = 2;
1036
+ context.lineWidth = 6;
904
1037
  context.globalAlpha = 1;
905
- context.strokeRect(0, 0, canvasWidth, canvasHeight);
1038
+ context.strokeRect(bgX, bgY, bgWidth, bgHeight);
1039
+
1040
+ // 绘制文本轮廓以增强对比度
1041
+ context.strokeStyle = color === '#000000' ? '#FFFFFF' : '#000000';
1042
+ context.lineWidth = 2;
1043
+ context.strokeText(content, centerX, centerY);
906
1044
 
907
1045
  // 绘制文本
908
1046
  context.fillStyle = color;
909
- context.fillText(content, canvasWidth / 2, canvasHeight / 2);
1047
+ context.fillText(content, centerX, centerY);
910
1048
 
911
- // 创建纹理
1049
+ // 创建纹理和sprite
912
1050
  var texture = new THREE.Texture(canvas);
913
1051
  texture.needsUpdate = true;
1052
+ texture.minFilter = THREE.LinearFilter;
1053
+ texture.magFilter = THREE.LinearFilter;
914
1054
 
915
- // 创建精灵材质
916
- var spriteMaterial = new THREE.SpriteMaterial({
1055
+ var material = new THREE.SpriteMaterial({
917
1056
  map: texture,
918
1057
  transparent: true,
919
1058
  depthTest: true,
920
1059
  depthWrite: false,
921
- sizeAttenuation: true // 启用距离衰减
1060
+ sizeAttenuation: true // 启用距离衰减,使文本大小随距离变化
922
1061
  });
923
1062
 
924
- // 创建精灵
925
- var sprite = new THREE.Sprite(spriteMaterial);
1063
+ var sprite = new THREE.Sprite(material);
926
1064
 
927
- // 使用固定缩放(与编辑器预览一致)
1065
+ // 增大缩放以提高可读性(与编辑器一致)
928
1066
  var scale = 3;
929
1067
  sprite.scale.set(scale, scale / 2, 1);
930
1068
 
@@ -979,6 +1117,13 @@
979
1117
  var elevationMm = textEl.elevation || 8500; // 默认8500mm(850cm)
980
1118
  var y = (elevationMm / LOGICAL_WALL_HEIGHT) * RENDER_WALL_HEIGHT;
981
1119
 
1120
+ console.log('[SceneCore] 渲染文本元素:', {
1121
+ content: textEl.content,
1122
+ elevationMm: elevationMm,
1123
+ calculatedY: y,
1124
+ position: { x: x, y: y, z: z }
1125
+ });
1126
+
982
1127
  var sprite = self.createTextSprite({
983
1128
  content: textEl.content || '',
984
1129
  fontSize: textEl.fontSize || 16,
@@ -1716,18 +1861,12 @@
1716
1861
  floorCount: kidataObj.floors ? kidataObj.floors.length : 0
1717
1862
  });
1718
1863
 
1719
- // 解析并渲染文本元素
1720
- self._parseAndRenderTextElements(kidataObj);
1721
-
1722
1864
  // 加载3D家具模型
1723
1865
  return self._loadFurnitureModels(kidataObj);
1724
1866
  })
1725
1867
  .then(function(result) {
1726
1868
  console.log('✅ 3D家具模型加载完成:', result);
1727
1869
 
1728
- // 应用墙面透明度
1729
- self._applyWallOpacity(self.kidataContent);
1730
-
1731
1870
  return { success: true, loaded: result.loaded, failed: result.failed };
1732
1871
  })
1733
1872
  .catch(function(error) {
@@ -2405,6 +2544,48 @@
2405
2544
  }
2406
2545
  };
2407
2546
 
2547
+ /**
2548
+ * 从kidata内容加载家具模型(不处理文本和墙体透明度)
2549
+ * @param {string} kidataContent - kidata文件内容(加密或未加密)
2550
+ */
2551
+ KimapSDK.prototype.loadKidataForFurniture = function(kidataContent) {
2552
+ var self = this;
2553
+
2554
+ try {
2555
+ // 解密kidata文件
2556
+ var decrypted = self._decryptKidata(kidataContent);
2557
+ var kidataObj = JSON.parse(decrypted);
2558
+
2559
+ // 保存kidata数据供路径规划使用
2560
+ self.kidataContent = kidataObj;
2561
+
2562
+ // 如果kidata包含楼层数据,设置为projectLayers
2563
+ if (kidataObj.floors && Array.isArray(kidataObj.floors)) {
2564
+ console.log('[KimapSDK] kidata包含楼层数据,楼层数量:', kidataObj.floors.length);
2565
+ if (!self.projectLayers && kidataObj.floors.length > 0) {
2566
+ self.projectLayers = kidataObj.floors[0].layers;
2567
+ console.log('[KimapSDK] 从kidata设置楼层数据,图层数量:', self.projectLayers.length);
2568
+ }
2569
+ }
2570
+
2571
+ console.log('[KimapSDK] ✅ Kidata文件解密成功:', {
2572
+ version: kidataObj.version,
2573
+ furnitureCount: kidataObj.furnitures ? kidataObj.furnitures.length : 0,
2574
+ floorCount: kidataObj.floors ? kidataObj.floors.length : 0
2575
+ });
2576
+
2577
+ // 只加载3D家具模型
2578
+ return self._loadFurnitureModels(kidataObj)
2579
+ .then(function(result) {
2580
+ console.log('[KimapSDK] ✅ 3D家具模型加载完成:', result);
2581
+ return { success: true, loaded: result.loaded, failed: result.failed };
2582
+ });
2583
+ } catch (error) {
2584
+ console.error('[KimapSDK] ❌ Kidata加载失败:', error);
2585
+ return Promise.reject(error);
2586
+ }
2587
+ };
2588
+
2408
2589
  KimapSDK.prototype.destroy = function() {
2409
2590
  if (this.animationFrameId) {
2410
2591
  cancelAnimationFrame(this.animationFrameId);
@@ -718,18 +718,12 @@ KimapSDK.prototype.loadKMData = function() {
718
718
  floorCount: kidataObj.floors ? kidataObj.floors.length : 0
719
719
  });
720
720
 
721
- // 解析并渲染文本元素
722
- self._parseAndRenderTextElements(kidataObj);
723
-
724
721
  // 加载3D家具模型
725
722
  return self._loadFurnitureModels(kidataObj);
726
723
  })
727
724
  .then(function(result) {
728
725
  console.log('✅ 3D家具模型加载完成:', result);
729
726
 
730
- // 应用墙面透明度
731
- self._applyWallOpacity(self.kidataContent);
732
-
733
727
  return { success: true, loaded: result.loaded, failed: result.failed };
734
728
  })
735
729
  .catch(function(error) {
@@ -975,107 +969,44 @@ KimapSDK.prototype._loadSingleFurniture = function(furniture, serverUrl) {
975
969
  };
976
970
 
977
971
  /**
978
- * 解析并渲染文本元素
972
+ * 从kidata内容加载家具模型(不处理文本和墙体透明度)
973
+ * @param {string} kidataContent - kidata文件内容(加密或未加密)
979
974
  */
980
- KimapSDK.prototype._parseAndRenderTextElements = function(kidataObj) {
981
- var textElements = [];
982
-
983
- // 从floors中提取文本元素
984
- if (kidataObj.floors && Array.isArray(kidataObj.floors)) {
985
- kidataObj.floors.forEach(function(floor) {
986
- if (floor.layers && Array.isArray(floor.layers)) {
987
- floor.layers.forEach(function(layer) {
988
- if (layer.elements && Array.isArray(layer.elements)) {
989
- layer.elements.forEach(function(element) {
990
- if (element.type === 'text' && element.visible !== false) {
991
- textElements.push(element);
992
- }
993
- });
994
- }
995
- });
996
- }
997
- });
998
- }
999
-
1000
- console.log('[KimapSDK] 找到', textElements.length, '个文本元素');
1001
-
1002
- // 调用SceneCore的渲染方法
1003
- if (textElements.length > 0 && this.core) {
1004
- this.core.renderTextElements(textElements);
1005
- }
1006
- };
1007
-
1008
- /**
1009
- * 应用墙面透明度
1010
- */
1011
- KimapSDK.prototype._applyWallOpacity = function(kidataObj) {
1012
- if (!kidataObj || !kidataObj.floors || !this.core || !this.core.scene) {
1013
- return;
1014
- }
1015
-
1016
- var wallCount = 0;
975
+ KimapSDK.prototype.loadKidataForFurniture = function(kidataContent) {
1017
976
  var self = this;
1018
977
 
1019
- // 遍历场景中的所有墙体对象
1020
- this.core.scene.traverse(function(object) {
1021
- if (object.userData && object.userData.type === 'wall') {
1022
- var wallId = object.userData.elementId;
1023
-
1024
- // kidata中查找对应的墙体元素
1025
- kidataObj.floors.forEach(function(floor) {
1026
- if (floor.layers && Array.isArray(floor.layers)) {
1027
- floor.layers.forEach(function(layer) {
1028
- if (layer.elements && Array.isArray(layer.elements)) {
1029
- layer.elements.forEach(function(element) {
1030
- if (element.type === 'wall' && element.id === wallId) {
1031
- var opacity = element.opacity !== undefined ? element.opacity : 0.85;
1032
-
1033
- // 应用透明度到墙体材质
1034
- if (object instanceof THREE.Mesh) {
1035
- if (Array.isArray(object.material)) {
1036
- object.material.forEach(function(mat) {
1037
- mat.transparent = opacity < 1;
1038
- mat.opacity = opacity;
1039
- mat.needsUpdate = true;
1040
- });
1041
- } else if (object.material) {
1042
- object.material.transparent = opacity < 1;
1043
- object.material.opacity = opacity;
1044
- object.material.needsUpdate = true;
1045
- }
1046
- wallCount++;
1047
- }
1048
-
1049
- // 如果是Group,遍历其子对象
1050
- if (object instanceof THREE.Group) {
1051
- object.traverse(function(child) {
1052
- if (child instanceof THREE.Mesh) {
1053
- if (Array.isArray(child.material)) {
1054
- child.material.forEach(function(mat) {
1055
- mat.transparent = opacity < 1;
1056
- mat.opacity = opacity;
1057
- mat.needsUpdate = true;
1058
- });
1059
- } else if (child.material) {
1060
- child.material.transparent = opacity < 1;
1061
- child.material.opacity = opacity;
1062
- child.material.needsUpdate = true;
1063
- }
1064
- }
1065
- });
1066
- wallCount++;
1067
- }
1068
- }
1069
- });
1070
- }
1071
- });
1072
- }
1073
- });
978
+ try {
979
+ // 解密kidata文件
980
+ var decrypted = self._decryptKidata(kidataContent);
981
+ var kidataObj = JSON.parse(decrypted);
982
+
983
+ // 保存kidata数据供路径规划使用
984
+ self.kidataContent = kidataObj;
985
+
986
+ // 如果kidata包含楼层数据,设置为projectLayers
987
+ if (kidataObj.floors && Array.isArray(kidataObj.floors)) {
988
+ console.log('[KimapSDK] kidata包含楼层数据,楼层数量:', kidataObj.floors.length);
989
+ if (!self.projectLayers && kidataObj.floors.length > 0) {
990
+ self.projectLayers = kidataObj.floors[0].layers;
991
+ console.log('[KimapSDK] 从kidata设置楼层数据,图层数量:', self.projectLayers.length);
992
+ }
1074
993
  }
1075
- });
1076
-
1077
- if (wallCount > 0) {
1078
- console.log('[KimapSDK] 应用墙面透明度完成:', wallCount, '个墙体');
994
+
995
+ console.log('[KimapSDK] ✅ Kidata文件解密成功:', {
996
+ version: kidataObj.version,
997
+ furnitureCount: kidataObj.furnitures ? kidataObj.furnitures.length : 0,
998
+ floorCount: kidataObj.floors ? kidataObj.floors.length : 0
999
+ });
1000
+
1001
+ // 只加载3D家具模型
1002
+ return self._loadFurnitureModels(kidataObj)
1003
+ .then(function(result) {
1004
+ console.log('[KimapSDK] ✅ 3D家具模型加载完成:', result);
1005
+ return { success: true, loaded: result.loaded, failed: result.failed };
1006
+ });
1007
+ } catch (error) {
1008
+ console.error('[KimapSDK] ❌ Kidata加载失败:', error);
1009
+ return Promise.reject(error);
1079
1010
  }
1080
1011
  };
1081
1012
 
@@ -26,6 +26,8 @@ var decryptTheme = crypto.decryptTheme;
26
26
  var extractBorderFromOBJ = parsers.extractBorderFromOBJ;
27
27
  var extractMapConfigFromOBJ = parsers.extractMapConfigFromOBJ;
28
28
  var extractColorsFromOBJ = parsers.extractColorsFromOBJ;
29
+ var extractTextElementsFromOBJ = parsers.extractTextElementsFromOBJ;
30
+ var extractWallOpacityFromOBJ = parsers.extractWallOpacityFromOBJ;
29
31
 
30
32
  /**
31
33
  * SceneCore 构造函数
@@ -369,12 +371,32 @@ SceneCore.prototype._loadKimapFile = function(kimapUrl, themeUrl, objLoader, mtl
369
371
  if (!materials) {
370
372
  console.log('[SceneCore] 提取到颜色信息:', colorMap);
371
373
  }
374
+
375
+ // 提取文本元素
376
+ var textElements = extractTextElementsFromOBJ(objContent);
377
+ if (textElements && textElements.length > 0) {
378
+ console.log('[SceneCore] ✅ 从OBJ提取到文本元素:', textElements.length, '个');
379
+ self._textElements = textElements;
380
+ }
381
+
382
+ // 提取墙体透明度信息
383
+ var wallOpacity = extractWallOpacityFromOBJ(objContent);
384
+ if (wallOpacity && Object.keys(wallOpacity).length > 0) {
385
+ console.log('[SceneCore] ✅ 从OBJ提取到墙体透明度:', Object.keys(wallOpacity).length, '个墙体');
386
+ self._wallOpacity = wallOpacity;
387
+ }
372
388
 
373
389
  // 解析OBJ内容
374
390
  var object = objLoader.parse(objContent);
375
391
  console.log('[SceneCore] ✓ OBJ解析完成,处理模型...');
376
392
 
377
393
  self._processLoadedModel(object, materials, colorMap);
394
+
395
+ // 渲染文本元素
396
+ if (self._textElements && self._textElements.length > 0) {
397
+ self.renderTextElements(self._textElements);
398
+ }
399
+
378
400
  resolve();
379
401
  } catch (error) {
380
402
  console.error('[SceneCore] ✗ Kimap处理失败:', error.message);
@@ -512,6 +534,8 @@ SceneCore.prototype._processLoadedModel = function(object, materials, colorMap)
512
534
  * @private
513
535
  */
514
536
  SceneCore.prototype._processMaterial = function(child, materials, colorMap) {
537
+ var self = this;
538
+
515
539
  if (child.material && child.material.name) {
516
540
  // 已有材质,转换为PBR材质
517
541
  var mtlColor = child.material.color ? child.material.color.getHex() : 0xdddddd;
@@ -529,6 +553,16 @@ SceneCore.prototype._processMaterial = function(child, materials, colorMap) {
529
553
  } else if (child.material.name.startsWith('Wall_')) {
530
554
  roughness = 0.25;
531
555
  metalness = 0;
556
+
557
+ // 从OBJ提取的墙体透明度数据中读取
558
+ if (self._wallOpacity) {
559
+ var wallId = child.material.name.replace('Wall_', '');
560
+ if (self._wallOpacity[wallId]) {
561
+ opacity = self._wallOpacity[wallId].opacity;
562
+ transparent = opacity < 1.0;
563
+ console.log('[SceneCore] 应用墙体透明度:', wallId, '=', opacity);
564
+ }
565
+ }
532
566
  } else if (child.material.name.startsWith('Shape_') || child.material.name.startsWith('Stair_')) {
533
567
  roughness = 0.25;
534
568
  metalness = 0;
@@ -626,62 +660,86 @@ SceneCore.prototype.createTextSprite = function(options) {
626
660
  var backgroundOpacity = options.backgroundOpacity !== undefined ? options.backgroundOpacity : 0.8;
627
661
  var padding = options.padding || 8;
628
662
 
629
- // 创建canvas
663
+ // 创建高分辨率canvas用于文本纹理
630
664
  var canvas = document.createElement('canvas');
631
665
  var context = canvas.getContext('2d');
632
666
 
633
- // 设置字体
634
- var fontStyle = (italic ? 'italic ' : '') + (bold ? 'bold ' : '') + fontSize + 'px ' + fontFamily;
667
+ // 使用更大的canvas尺寸以提高清晰度
668
+ canvas.width = 1024;
669
+ canvas.height = 512;
670
+
671
+ // 配置字体(放大4倍以提高质量)
672
+ var scaledFontSize = fontSize * 4;
673
+ var fontStyle = (italic ? 'italic ' : '') + (bold ? 'bold ' : '') + scaledFontSize + 'px ' + fontFamily;
635
674
  context.font = fontStyle;
675
+ context.textAlign = 'center';
676
+ context.textBaseline = 'middle';
636
677
 
637
- // 测量文本尺寸
678
+ // 测量文本
638
679
  var metrics = context.measureText(content);
639
680
  var textWidth = metrics.width;
640
- var textHeight = fontSize * 1.2;
681
+ var textHeight = scaledFontSize * 1.2;
641
682
 
642
- // 设置canvas尺寸(需要是2的幂次方以获得更好的性能)
643
- var canvasWidth = Math.pow(2, Math.ceil(Math.log2(textWidth + padding * 2)));
644
- var canvasHeight = Math.pow(2, Math.ceil(Math.log2(textHeight + padding * 2)));
645
- canvas.width = canvasWidth;
646
- canvas.height = canvasHeight;
683
+ // 计算背景尺寸
684
+ var scaledPadding = padding * 4;
685
+ var bgWidth = textWidth + scaledPadding * 2;
686
+ var bgHeight = textHeight + scaledPadding * 2;
647
687
 
648
- // 重新设置字体(canvas尺寸改变后需要重新设置)
649
- context.font = fontStyle;
650
- context.textAlign = 'center';
651
- context.textBaseline = 'middle';
688
+ // 计算居中位置
689
+ var centerX = canvas.width / 2;
690
+ var centerY = canvas.height / 2;
691
+ var bgX = centerX - bgWidth / 2;
692
+ var bgY = centerY - bgHeight / 2;
693
+
694
+ // 绘制阴影效果以增强可读性
695
+ context.shadowColor = 'rgba(0, 0, 0, 0.5)';
696
+ context.shadowBlur = 8;
697
+ context.shadowOffsetX = 2;
698
+ context.shadowOffsetY = 2;
652
699
 
653
700
  // 绘制背景
654
701
  context.fillStyle = backgroundColor;
655
- context.globalAlpha = backgroundOpacity;
656
- context.fillRect(0, 0, canvasWidth, canvasHeight);
702
+ context.globalAlpha = Math.max(backgroundOpacity, 0.9); // 确保背景足够不透明
703
+ context.fillRect(bgX, bgY, bgWidth, bgHeight);
657
704
 
658
- // 绘制边框
705
+ // 重置阴影
706
+ context.shadowColor = 'transparent';
707
+ context.shadowBlur = 0;
708
+ context.shadowOffsetX = 0;
709
+ context.shadowOffsetY = 0;
710
+
711
+ // 绘制边框(加粗以提高可见性)
659
712
  context.strokeStyle = borderColor;
660
- context.lineWidth = 2;
713
+ context.lineWidth = 6;
661
714
  context.globalAlpha = 1;
662
- context.strokeRect(0, 0, canvasWidth, canvasHeight);
715
+ context.strokeRect(bgX, bgY, bgWidth, bgHeight);
716
+
717
+ // 绘制文本轮廓以增强对比度
718
+ context.strokeStyle = color === '#000000' ? '#FFFFFF' : '#000000';
719
+ context.lineWidth = 2;
720
+ context.strokeText(content, centerX, centerY);
663
721
 
664
722
  // 绘制文本
665
723
  context.fillStyle = color;
666
- context.fillText(content, canvasWidth / 2, canvasHeight / 2);
724
+ context.fillText(content, centerX, centerY);
667
725
 
668
- // 创建纹理
726
+ // 创建纹理和sprite
669
727
  var texture = new THREE.Texture(canvas);
670
728
  texture.needsUpdate = true;
729
+ texture.minFilter = THREE.LinearFilter;
730
+ texture.magFilter = THREE.LinearFilter;
671
731
 
672
- // 创建精灵材质
673
- var spriteMaterial = new THREE.SpriteMaterial({
732
+ var material = new THREE.SpriteMaterial({
674
733
  map: texture,
675
734
  transparent: true,
676
735
  depthTest: true,
677
736
  depthWrite: false,
678
- sizeAttenuation: true // 启用距离衰减
737
+ sizeAttenuation: true // 启用距离衰减,使文本大小随距离变化
679
738
  });
680
739
 
681
- // 创建精灵
682
- var sprite = new THREE.Sprite(spriteMaterial);
740
+ var sprite = new THREE.Sprite(material);
683
741
 
684
- // 使用固定缩放(与编辑器预览一致)
742
+ // 增大缩放以提高可读性(与编辑器一致)
685
743
  var scale = 3;
686
744
  sprite.scale.set(scale, scale / 2, 1);
687
745
 
@@ -736,6 +794,13 @@ SceneCore.prototype.renderTextElements = function(textElements) {
736
794
  var elevationMm = textEl.elevation || 8500; // 默认8500mm(850cm)
737
795
  var y = (elevationMm / LOGICAL_WALL_HEIGHT) * RENDER_WALL_HEIGHT;
738
796
 
797
+ console.log('[SceneCore] 文本元素属性:', {
798
+ content: textEl.content,
799
+ backgroundColor: textEl.backgroundColor,
800
+ borderColor: textEl.borderColor,
801
+ color: textEl.color
802
+ });
803
+
739
804
  var sprite = self.createTextSprite({
740
805
  content: textEl.content || '',
741
806
  fontSize: textEl.fontSize || 16,
@@ -743,7 +808,7 @@ SceneCore.prototype.renderTextElements = function(textElements) {
743
808
  fontFamily: textEl.fontFamily || 'Arial, sans-serif',
744
809
  bold: textEl.bold || false,
745
810
  italic: textEl.italic || false,
746
- borderColor: textEl.borderColor || '#87CEEB',
811
+ borderColor: textEl.borderColor || '#ADD8E6',
747
812
  backgroundColor: textEl.backgroundColor || '#FFFFFF',
748
813
  backgroundOpacity: textEl.backgroundOpacity !== undefined ? textEl.backgroundOpacity : 0.8,
749
814
  padding: textEl.padding || 8
@@ -114,9 +114,110 @@ function extractCoordinateSystem(config) {
114
114
  };
115
115
  }
116
116
 
117
+ /**
118
+ * 从OBJ内容中提取文本元素
119
+ * 格式示例:
120
+ * # ===== Text Element =====
121
+ * # TEXT_ID text_123
122
+ * # TEXT_CONTENT Hello World
123
+ * # TEXT_POSITION 1000 2000
124
+ * # TEXT_ELEVATION 8500
125
+ * # ===== End Text Element =====
126
+ */
127
+ function extractTextElementsFromOBJ(objContent) {
128
+ var textElements = [];
129
+ var lines = objContent.split('\n');
130
+ var currentText = null;
131
+
132
+ for (var i = 0; i < lines.length; i++) {
133
+ var line = lines[i].trim();
134
+
135
+ if (line.indexOf('# ===== Text Element =====') === 0) {
136
+ currentText = {};
137
+ } else if (line.indexOf('# ===== End Text Element =====') === 0 && currentText) {
138
+ textElements.push(currentText);
139
+ currentText = null;
140
+ } else if (currentText) {
141
+ if (line.indexOf('# TEXT_ID') === 0) {
142
+ currentText.id = line.split(' ')[2];
143
+ } else if (line.indexOf('# TEXT_CONTENT') === 0) {
144
+ currentText.content = line.substring(line.indexOf('TEXT_CONTENT') + 13).trim();
145
+ } else if (line.indexOf('# TEXT_POSITION') === 0) {
146
+ var parts = line.split(' ');
147
+ currentText.points = [{ x: parseFloat(parts[2]), y: parseFloat(parts[3]) }];
148
+ } else if (line.indexOf('# TEXT_ELEVATION') === 0) {
149
+ currentText.elevation = parseFloat(line.split(' ')[2]);
150
+ } else if (line.indexOf('# TEXT_FONT_SIZE') === 0) {
151
+ currentText.fontSize = parseFloat(line.split(' ')[2]);
152
+ } else if (line.indexOf('# TEXT_COLOR') === 0) {
153
+ currentText.color = line.split(' ')[2];
154
+ } else if (line.indexOf('# TEXT_FONT_FAMILY') === 0) {
155
+ currentText.fontFamily = line.substring(line.indexOf('TEXT_FONT_FAMILY') + 17).trim();
156
+ } else if (line.indexOf('# TEXT_BOLD') === 0) {
157
+ currentText.bold = line.split(' ')[2] === 'true';
158
+ } else if (line.indexOf('# TEXT_ITALIC') === 0) {
159
+ currentText.italic = line.split(' ')[2] === 'true';
160
+ } else if (line.indexOf('# TEXT_BORDER_COLOR') === 0) {
161
+ currentText.borderColor = line.split(' ')[2];
162
+ } else if (line.indexOf('# TEXT_BG_COLOR') === 0) {
163
+ currentText.backgroundColor = line.split(' ')[2];
164
+ } else if (line.indexOf('# TEXT_BG_OPACITY') === 0) {
165
+ currentText.backgroundOpacity = parseFloat(line.split(' ')[2]);
166
+ } else if (line.indexOf('# TEXT_PADDING') === 0) {
167
+ currentText.padding = parseFloat(line.split(' ')[2]);
168
+ }
169
+ }
170
+ }
171
+
172
+ console.log('[Parsers] 提取到的文本元素:', textElements.length, '个');
173
+ return textElements;
174
+ }
175
+
176
+ /**
177
+ * 从OBJ内容中提取墙体透明度信息
178
+ * 格式示例:
179
+ * o Wall_wall123_seg0
180
+ * # WALL_OPACITY 0.85
181
+ * # WALL_COLOR #d1d5db
182
+ */
183
+ function extractWallOpacityFromOBJ(objContent) {
184
+ var wallOpacity = {};
185
+ var lines = objContent.split('\n');
186
+ var currentWallId = null;
187
+
188
+ for (var i = 0; i < lines.length; i++) {
189
+ var line = lines[i].trim();
190
+
191
+ if (line.indexOf('o Wall_') === 0) {
192
+ // 提取墙体ID(格式:Wall_<id>_seg<n>)
193
+ var match = line.match(/Wall_([^_]+)_seg/);
194
+ if (match) {
195
+ currentWallId = match[1];
196
+ }
197
+ } else if (currentWallId && line.indexOf('# WALL_OPACITY') === 0) {
198
+ var opacity = parseFloat(line.split(' ')[2]);
199
+ if (!wallOpacity[currentWallId]) {
200
+ wallOpacity[currentWallId] = {};
201
+ }
202
+ wallOpacity[currentWallId].opacity = opacity;
203
+ } else if (currentWallId && line.indexOf('# WALL_COLOR') === 0) {
204
+ var color = line.split(' ')[2];
205
+ if (!wallOpacity[currentWallId]) {
206
+ wallOpacity[currentWallId] = {};
207
+ }
208
+ wallOpacity[currentWallId].color = color;
209
+ }
210
+ }
211
+
212
+ console.log('[Parsers] 提取到的墙体透明度:', Object.keys(wallOpacity).length, '个墙体');
213
+ return wallOpacity;
214
+ }
215
+
117
216
  module.exports = {
118
217
  extractBorderFromOBJ: extractBorderFromOBJ,
119
218
  extractMapConfigFromOBJ: extractMapConfigFromOBJ,
120
219
  extractColorsFromOBJ: extractColorsFromOBJ,
121
- extractCoordinateSystem: extractCoordinateSystem
220
+ extractCoordinateSystem: extractCoordinateSystem,
221
+ extractTextElementsFromOBJ: extractTextElementsFromOBJ,
222
+ extractWallOpacityFromOBJ: extractWallOpacityFromOBJ
122
223
  };