@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 +1 -1
- package/src/KimapCore.browser.js +214 -33
- package/src/core/KimapSDK.js +34 -103
- package/src/core/SceneCore.js +93 -28
- package/src/core/parsers.js +102 -1
package/package.json
CHANGED
package/src/KimapCore.browser.js
CHANGED
|
@@ -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
|
-
//
|
|
986
|
+
// 创建高分辨率canvas用于文本纹理
|
|
873
987
|
var canvas = document.createElement('canvas');
|
|
874
988
|
var context = canvas.getContext('2d');
|
|
875
989
|
|
|
876
|
-
//
|
|
877
|
-
|
|
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 =
|
|
1004
|
+
var textHeight = scaledFontSize * 1.2;
|
|
884
1005
|
|
|
885
|
-
//
|
|
886
|
-
var
|
|
887
|
-
var
|
|
888
|
-
|
|
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
|
-
//
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
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(
|
|
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 =
|
|
1036
|
+
context.lineWidth = 6;
|
|
904
1037
|
context.globalAlpha = 1;
|
|
905
|
-
context.strokeRect(
|
|
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,
|
|
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);
|
package/src/core/KimapSDK.js
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
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
|
-
|
|
1078
|
-
|
|
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
|
|
package/src/core/SceneCore.js
CHANGED
|
@@ -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
|
-
//
|
|
663
|
+
// 创建高分辨率canvas用于文本纹理
|
|
630
664
|
var canvas = document.createElement('canvas');
|
|
631
665
|
var context = canvas.getContext('2d');
|
|
632
666
|
|
|
633
|
-
//
|
|
634
|
-
|
|
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 =
|
|
681
|
+
var textHeight = scaledFontSize * 1.2;
|
|
641
682
|
|
|
642
|
-
//
|
|
643
|
-
var
|
|
644
|
-
var
|
|
645
|
-
|
|
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
|
-
//
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
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(
|
|
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 =
|
|
713
|
+
context.lineWidth = 6;
|
|
661
714
|
context.globalAlpha = 1;
|
|
662
|
-
context.strokeRect(
|
|
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,
|
|
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 || '#
|
|
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
|
package/src/core/parsers.js
CHANGED
|
@@ -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
|
};
|