@kimap/indoor-positioning-sdk-vue2 4.2.9 → 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 +459 -0
- package/src/core/KimapSDK.js +43 -0
- package/src/core/SceneCore.js +211 -0
- 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;
|
|
@@ -854,6 +968,183 @@
|
|
|
854
968
|
this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);
|
|
855
969
|
};
|
|
856
970
|
|
|
971
|
+
/**
|
|
972
|
+
* 创建文本精灵(用于3D场景中显示文本)
|
|
973
|
+
*/
|
|
974
|
+
SceneCore.prototype.createTextSprite = function(options) {
|
|
975
|
+
var content = options.content || '';
|
|
976
|
+
var fontSize = options.fontSize || 16;
|
|
977
|
+
var color = options.color || '#000000';
|
|
978
|
+
var fontFamily = options.fontFamily || 'Arial, sans-serif';
|
|
979
|
+
var bold = options.bold || false;
|
|
980
|
+
var italic = options.italic || false;
|
|
981
|
+
var borderColor = options.borderColor || '#87CEEB';
|
|
982
|
+
var backgroundColor = options.backgroundColor || '#FFFFFF';
|
|
983
|
+
var backgroundOpacity = options.backgroundOpacity !== undefined ? options.backgroundOpacity : 0.8;
|
|
984
|
+
var padding = options.padding || 8;
|
|
985
|
+
|
|
986
|
+
// 创建高分辨率canvas用于文本纹理
|
|
987
|
+
var canvas = document.createElement('canvas');
|
|
988
|
+
var context = canvas.getContext('2d');
|
|
989
|
+
|
|
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;
|
|
997
|
+
context.font = fontStyle;
|
|
998
|
+
context.textAlign = 'center';
|
|
999
|
+
context.textBaseline = 'middle';
|
|
1000
|
+
|
|
1001
|
+
// 测量文本
|
|
1002
|
+
var metrics = context.measureText(content);
|
|
1003
|
+
var textWidth = metrics.width;
|
|
1004
|
+
var textHeight = scaledFontSize * 1.2;
|
|
1005
|
+
|
|
1006
|
+
// 计算背景尺寸
|
|
1007
|
+
var scaledPadding = padding * 4;
|
|
1008
|
+
var bgWidth = textWidth + scaledPadding * 2;
|
|
1009
|
+
var bgHeight = textHeight + scaledPadding * 2;
|
|
1010
|
+
|
|
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;
|
|
1022
|
+
|
|
1023
|
+
// 绘制背景
|
|
1024
|
+
context.fillStyle = backgroundColor;
|
|
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;
|
|
1033
|
+
|
|
1034
|
+
// 绘制边框(加粗以提高可见性)
|
|
1035
|
+
context.strokeStyle = borderColor;
|
|
1036
|
+
context.lineWidth = 6;
|
|
1037
|
+
context.globalAlpha = 1;
|
|
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);
|
|
1044
|
+
|
|
1045
|
+
// 绘制文本
|
|
1046
|
+
context.fillStyle = color;
|
|
1047
|
+
context.fillText(content, centerX, centerY);
|
|
1048
|
+
|
|
1049
|
+
// 创建纹理和sprite
|
|
1050
|
+
var texture = new THREE.Texture(canvas);
|
|
1051
|
+
texture.needsUpdate = true;
|
|
1052
|
+
texture.minFilter = THREE.LinearFilter;
|
|
1053
|
+
texture.magFilter = THREE.LinearFilter;
|
|
1054
|
+
|
|
1055
|
+
var material = new THREE.SpriteMaterial({
|
|
1056
|
+
map: texture,
|
|
1057
|
+
transparent: true,
|
|
1058
|
+
depthTest: true,
|
|
1059
|
+
depthWrite: false,
|
|
1060
|
+
sizeAttenuation: true // 启用距离衰减,使文本大小随距离变化
|
|
1061
|
+
});
|
|
1062
|
+
|
|
1063
|
+
var sprite = new THREE.Sprite(material);
|
|
1064
|
+
|
|
1065
|
+
// 增大缩放以提高可读性(与编辑器一致)
|
|
1066
|
+
var scale = 3;
|
|
1067
|
+
sprite.scale.set(scale, scale / 2, 1);
|
|
1068
|
+
|
|
1069
|
+
// 设置渲染顺序为最大值,确保文本始终在最上层
|
|
1070
|
+
sprite.renderOrder = 999999;
|
|
1071
|
+
|
|
1072
|
+
return sprite;
|
|
1073
|
+
};
|
|
1074
|
+
|
|
1075
|
+
/**
|
|
1076
|
+
* 渲染文本元素到场景
|
|
1077
|
+
*/
|
|
1078
|
+
SceneCore.prototype.renderTextElements = function(textElements) {
|
|
1079
|
+
var self = this;
|
|
1080
|
+
|
|
1081
|
+
if (!textElements || textElements.length === 0) {
|
|
1082
|
+
return;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
// 创建文本组(如果不存在)
|
|
1086
|
+
if (!this.textGroup) {
|
|
1087
|
+
this.textGroup = new THREE.Group();
|
|
1088
|
+
this.textGroup.name = 'texts';
|
|
1089
|
+
this.scene.add(this.textGroup);
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
// 清空现有文本
|
|
1093
|
+
while (this.textGroup.children.length > 0) {
|
|
1094
|
+
var child = this.textGroup.children[0];
|
|
1095
|
+
this.textGroup.remove(child);
|
|
1096
|
+
|
|
1097
|
+
// 释放资源
|
|
1098
|
+
if (child.material && child.material.map) {
|
|
1099
|
+
child.material.map.dispose();
|
|
1100
|
+
}
|
|
1101
|
+
if (child.material) {
|
|
1102
|
+
child.material.dispose();
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
// 渲染每个文本元素
|
|
1107
|
+
textElements.forEach(function(textEl) {
|
|
1108
|
+
if (!textEl.points || textEl.points.length === 0) return;
|
|
1109
|
+
|
|
1110
|
+
var point = textEl.points[0];
|
|
1111
|
+
var x = point.x / 100; // Canvas pixels to 3D meters
|
|
1112
|
+
var z = point.y / 100;
|
|
1113
|
+
|
|
1114
|
+
// 文本elevation单位是mm,需要按照墙面比例缩放(与编辑器预览一致)
|
|
1115
|
+
var LOGICAL_WALL_HEIGHT = 3000; // 逻辑墙高3000mm(3米)
|
|
1116
|
+
var RENDER_WALL_HEIGHT = 0.375; // 渲染墙高0.375米
|
|
1117
|
+
var elevationMm = textEl.elevation || 8500; // 默认8500mm(850cm)
|
|
1118
|
+
var y = (elevationMm / LOGICAL_WALL_HEIGHT) * RENDER_WALL_HEIGHT;
|
|
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
|
+
|
|
1127
|
+
var sprite = self.createTextSprite({
|
|
1128
|
+
content: textEl.content || '',
|
|
1129
|
+
fontSize: textEl.fontSize || 16,
|
|
1130
|
+
color: textEl.color || '#000000',
|
|
1131
|
+
fontFamily: textEl.fontFamily || 'Arial, sans-serif',
|
|
1132
|
+
bold: textEl.bold || false,
|
|
1133
|
+
italic: textEl.italic || false,
|
|
1134
|
+
borderColor: textEl.borderColor || '#87CEEB',
|
|
1135
|
+
backgroundColor: textEl.backgroundColor || '#FFFFFF',
|
|
1136
|
+
backgroundOpacity: textEl.backgroundOpacity !== undefined ? textEl.backgroundOpacity : 0.8,
|
|
1137
|
+
padding: textEl.padding || 8
|
|
1138
|
+
});
|
|
1139
|
+
|
|
1140
|
+
sprite.position.set(x, y, z);
|
|
1141
|
+
sprite.userData = { type: 'text', elementId: textEl.id };
|
|
1142
|
+
self.textGroup.add(sprite);
|
|
1143
|
+
});
|
|
1144
|
+
|
|
1145
|
+
console.log('[SceneCore] ✅ 文本元素渲染完成:', textElements.length, '个');
|
|
1146
|
+
};
|
|
1147
|
+
|
|
857
1148
|
SceneCore.prototype.destroy = function() {
|
|
858
1149
|
if (this.scene) {
|
|
859
1150
|
this.scene.traverse(function(object) {
|
|
@@ -1575,6 +1866,7 @@
|
|
|
1575
1866
|
})
|
|
1576
1867
|
.then(function(result) {
|
|
1577
1868
|
console.log('✅ 3D家具模型加载完成:', result);
|
|
1869
|
+
|
|
1578
1870
|
return { success: true, loaded: result.loaded, failed: result.failed };
|
|
1579
1871
|
})
|
|
1580
1872
|
.catch(function(error) {
|
|
@@ -2127,6 +2419,173 @@
|
|
|
2127
2419
|
}
|
|
2128
2420
|
};
|
|
2129
2421
|
|
|
2422
|
+
/**
|
|
2423
|
+
* 解析并渲染文本元素
|
|
2424
|
+
* @private
|
|
2425
|
+
*/
|
|
2426
|
+
KimapSDK.prototype._parseAndRenderTextElements = function(kidataObj) {
|
|
2427
|
+
var textElements = [];
|
|
2428
|
+
|
|
2429
|
+
// 从floors中提取文本元素
|
|
2430
|
+
if (kidataObj.floors && Array.isArray(kidataObj.floors)) {
|
|
2431
|
+
kidataObj.floors.forEach(function(floor) {
|
|
2432
|
+
if (floor.layers && Array.isArray(floor.layers)) {
|
|
2433
|
+
floor.layers.forEach(function(layer) {
|
|
2434
|
+
if (layer.elements && Array.isArray(layer.elements)) {
|
|
2435
|
+
layer.elements.forEach(function(element) {
|
|
2436
|
+
if (element.type === 'text' && element.visible !== false) {
|
|
2437
|
+
textElements.push(element);
|
|
2438
|
+
}
|
|
2439
|
+
});
|
|
2440
|
+
}
|
|
2441
|
+
});
|
|
2442
|
+
}
|
|
2443
|
+
});
|
|
2444
|
+
}
|
|
2445
|
+
|
|
2446
|
+
console.log('[KimapSDK] 找到', textElements.length, '个文本元素');
|
|
2447
|
+
|
|
2448
|
+
// 调用SceneCore的渲染方法
|
|
2449
|
+
if (textElements.length > 0 && this.core) {
|
|
2450
|
+
this.core.renderTextElements(textElements);
|
|
2451
|
+
}
|
|
2452
|
+
};
|
|
2453
|
+
|
|
2454
|
+
/**
|
|
2455
|
+
* 应用墙面透明度
|
|
2456
|
+
* @private
|
|
2457
|
+
*/
|
|
2458
|
+
KimapSDK.prototype._applyWallOpacity = function(kidataObj) {
|
|
2459
|
+
if (!kidataObj || !kidataObj.floors || !this.core || !this.core.scene) {
|
|
2460
|
+
console.log('[KimapSDK] _applyWallOpacity: 缺少必要数据');
|
|
2461
|
+
return;
|
|
2462
|
+
}
|
|
2463
|
+
|
|
2464
|
+
console.log('[KimapSDK] 开始应用墙面透明度...');
|
|
2465
|
+
|
|
2466
|
+
var wallCount = 0;
|
|
2467
|
+
var wallsInKidata = [];
|
|
2468
|
+
var self = this;
|
|
2469
|
+
|
|
2470
|
+
// 收集kidata中的所有墙体数据
|
|
2471
|
+
kidataObj.floors.forEach(function(floor) {
|
|
2472
|
+
if (floor.layers) {
|
|
2473
|
+
floor.layers.forEach(function(layer) {
|
|
2474
|
+
if (layer.elements) {
|
|
2475
|
+
layer.elements.forEach(function(element) {
|
|
2476
|
+
if (element.type === 'wall') {
|
|
2477
|
+
wallsInKidata.push({
|
|
2478
|
+
id: element.id,
|
|
2479
|
+
opacity: element.opacity !== undefined ? element.opacity : 0.85,
|
|
2480
|
+
color: element.color
|
|
2481
|
+
});
|
|
2482
|
+
}
|
|
2483
|
+
});
|
|
2484
|
+
}
|
|
2485
|
+
});
|
|
2486
|
+
}
|
|
2487
|
+
});
|
|
2488
|
+
|
|
2489
|
+
console.log('[KimapSDK] Kidata中墙体数量:', wallsInKidata.length);
|
|
2490
|
+
|
|
2491
|
+
// 如果kidata中没有墙体数据,不需要继续
|
|
2492
|
+
if (wallsInKidata.length === 0) {
|
|
2493
|
+
console.log('[KimapSDK] Kidata中没有墙体数据');
|
|
2494
|
+
return;
|
|
2495
|
+
}
|
|
2496
|
+
|
|
2497
|
+
// 遍历场景中的所有对象,通过材质名称识别墙体
|
|
2498
|
+
this.core.scene.traverse(function(object) {
|
|
2499
|
+
if (object instanceof THREE.Mesh && object.material) {
|
|
2500
|
+
var materialName = object.material.name || '';
|
|
2501
|
+
|
|
2502
|
+
// 通过材质名称识别墙体(Wall_前缀)
|
|
2503
|
+
if (materialName.startsWith('Wall_')) {
|
|
2504
|
+
// 从材质名称中提取墙体ID(格式:Wall_<id>)
|
|
2505
|
+
var wallId = materialName.replace('Wall_', '');
|
|
2506
|
+
|
|
2507
|
+
// 在kidata中查找对应的墙体数据
|
|
2508
|
+
var wallData = wallsInKidata.find(function(w) {
|
|
2509
|
+
return w.id === wallId;
|
|
2510
|
+
});
|
|
2511
|
+
|
|
2512
|
+
if (wallData) {
|
|
2513
|
+
var opacity = wallData.opacity;
|
|
2514
|
+
|
|
2515
|
+
console.log('[KimapSDK] 应用墙体透明度:', {
|
|
2516
|
+
id: wallId,
|
|
2517
|
+
opacity: opacity,
|
|
2518
|
+
materialName: materialName
|
|
2519
|
+
});
|
|
2520
|
+
|
|
2521
|
+
// 应用透明度到材质
|
|
2522
|
+
if (Array.isArray(object.material)) {
|
|
2523
|
+
object.material.forEach(function(mat) {
|
|
2524
|
+
mat.transparent = opacity < 1;
|
|
2525
|
+
mat.opacity = opacity;
|
|
2526
|
+
mat.needsUpdate = true;
|
|
2527
|
+
});
|
|
2528
|
+
} else {
|
|
2529
|
+
object.material.transparent = opacity < 1;
|
|
2530
|
+
object.material.opacity = opacity;
|
|
2531
|
+
object.material.needsUpdate = true;
|
|
2532
|
+
}
|
|
2533
|
+
|
|
2534
|
+
wallCount++;
|
|
2535
|
+
}
|
|
2536
|
+
}
|
|
2537
|
+
}
|
|
2538
|
+
});
|
|
2539
|
+
|
|
2540
|
+
if (wallCount > 0) {
|
|
2541
|
+
console.log('[KimapSDK] ✅ 应用墙面透明度完成:', wallCount, '个墙体');
|
|
2542
|
+
} else {
|
|
2543
|
+
console.log('[KimapSDK] ⚠️ 未找到匹配的墙体对象');
|
|
2544
|
+
}
|
|
2545
|
+
};
|
|
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
|
+
|
|
2130
2589
|
KimapSDK.prototype.destroy = function() {
|
|
2131
2590
|
if (this.animationFrameId) {
|
|
2132
2591
|
cancelAnimationFrame(this.animationFrameId);
|
package/src/core/KimapSDK.js
CHANGED
|
@@ -723,6 +723,7 @@ KimapSDK.prototype.loadKMData = function() {
|
|
|
723
723
|
})
|
|
724
724
|
.then(function(result) {
|
|
725
725
|
console.log('✅ 3D家具模型加载完成:', result);
|
|
726
|
+
|
|
726
727
|
return { success: true, loaded: result.loaded, failed: result.failed };
|
|
727
728
|
})
|
|
728
729
|
.catch(function(error) {
|
|
@@ -967,6 +968,48 @@ KimapSDK.prototype._loadSingleFurniture = function(furniture, serverUrl) {
|
|
|
967
968
|
});
|
|
968
969
|
};
|
|
969
970
|
|
|
971
|
+
/**
|
|
972
|
+
* 从kidata内容加载家具模型(不处理文本和墙体透明度)
|
|
973
|
+
* @param {string} kidataContent - kidata文件内容(加密或未加密)
|
|
974
|
+
*/
|
|
975
|
+
KimapSDK.prototype.loadKidataForFurniture = function(kidataContent) {
|
|
976
|
+
var self = this;
|
|
977
|
+
|
|
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
|
+
}
|
|
993
|
+
}
|
|
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);
|
|
1010
|
+
}
|
|
1011
|
+
};
|
|
1012
|
+
|
|
970
1013
|
/**
|
|
971
1014
|
* 销毁 SDK
|
|
972
1015
|
*/
|
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;
|
|
@@ -611,6 +645,183 @@ SceneCore.prototype.onWindowResize = function() {
|
|
|
611
645
|
this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);
|
|
612
646
|
};
|
|
613
647
|
|
|
648
|
+
/**
|
|
649
|
+
* 创建文本精灵(用于3D场景中显示文本)
|
|
650
|
+
*/
|
|
651
|
+
SceneCore.prototype.createTextSprite = function(options) {
|
|
652
|
+
var content = options.content || '';
|
|
653
|
+
var fontSize = options.fontSize || 16;
|
|
654
|
+
var color = options.color || '#000000';
|
|
655
|
+
var fontFamily = options.fontFamily || 'Arial, sans-serif';
|
|
656
|
+
var bold = options.bold || false;
|
|
657
|
+
var italic = options.italic || false;
|
|
658
|
+
var borderColor = options.borderColor || '#87CEEB';
|
|
659
|
+
var backgroundColor = options.backgroundColor || '#FFFFFF';
|
|
660
|
+
var backgroundOpacity = options.backgroundOpacity !== undefined ? options.backgroundOpacity : 0.8;
|
|
661
|
+
var padding = options.padding || 8;
|
|
662
|
+
|
|
663
|
+
// 创建高分辨率canvas用于文本纹理
|
|
664
|
+
var canvas = document.createElement('canvas');
|
|
665
|
+
var context = canvas.getContext('2d');
|
|
666
|
+
|
|
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;
|
|
674
|
+
context.font = fontStyle;
|
|
675
|
+
context.textAlign = 'center';
|
|
676
|
+
context.textBaseline = 'middle';
|
|
677
|
+
|
|
678
|
+
// 测量文本
|
|
679
|
+
var metrics = context.measureText(content);
|
|
680
|
+
var textWidth = metrics.width;
|
|
681
|
+
var textHeight = scaledFontSize * 1.2;
|
|
682
|
+
|
|
683
|
+
// 计算背景尺寸
|
|
684
|
+
var scaledPadding = padding * 4;
|
|
685
|
+
var bgWidth = textWidth + scaledPadding * 2;
|
|
686
|
+
var bgHeight = textHeight + scaledPadding * 2;
|
|
687
|
+
|
|
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;
|
|
699
|
+
|
|
700
|
+
// 绘制背景
|
|
701
|
+
context.fillStyle = backgroundColor;
|
|
702
|
+
context.globalAlpha = Math.max(backgroundOpacity, 0.9); // 确保背景足够不透明
|
|
703
|
+
context.fillRect(bgX, bgY, bgWidth, bgHeight);
|
|
704
|
+
|
|
705
|
+
// 重置阴影
|
|
706
|
+
context.shadowColor = 'transparent';
|
|
707
|
+
context.shadowBlur = 0;
|
|
708
|
+
context.shadowOffsetX = 0;
|
|
709
|
+
context.shadowOffsetY = 0;
|
|
710
|
+
|
|
711
|
+
// 绘制边框(加粗以提高可见性)
|
|
712
|
+
context.strokeStyle = borderColor;
|
|
713
|
+
context.lineWidth = 6;
|
|
714
|
+
context.globalAlpha = 1;
|
|
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);
|
|
721
|
+
|
|
722
|
+
// 绘制文本
|
|
723
|
+
context.fillStyle = color;
|
|
724
|
+
context.fillText(content, centerX, centerY);
|
|
725
|
+
|
|
726
|
+
// 创建纹理和sprite
|
|
727
|
+
var texture = new THREE.Texture(canvas);
|
|
728
|
+
texture.needsUpdate = true;
|
|
729
|
+
texture.minFilter = THREE.LinearFilter;
|
|
730
|
+
texture.magFilter = THREE.LinearFilter;
|
|
731
|
+
|
|
732
|
+
var material = new THREE.SpriteMaterial({
|
|
733
|
+
map: texture,
|
|
734
|
+
transparent: true,
|
|
735
|
+
depthTest: true,
|
|
736
|
+
depthWrite: false,
|
|
737
|
+
sizeAttenuation: true // 启用距离衰减,使文本大小随距离变化
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
var sprite = new THREE.Sprite(material);
|
|
741
|
+
|
|
742
|
+
// 增大缩放以提高可读性(与编辑器一致)
|
|
743
|
+
var scale = 3;
|
|
744
|
+
sprite.scale.set(scale, scale / 2, 1);
|
|
745
|
+
|
|
746
|
+
// 设置渲染顺序为最大值,确保文本始终在最上层
|
|
747
|
+
sprite.renderOrder = 999999;
|
|
748
|
+
|
|
749
|
+
return sprite;
|
|
750
|
+
};
|
|
751
|
+
|
|
752
|
+
/**
|
|
753
|
+
* 渲染文本元素到场景
|
|
754
|
+
*/
|
|
755
|
+
SceneCore.prototype.renderTextElements = function(textElements) {
|
|
756
|
+
var self = this;
|
|
757
|
+
|
|
758
|
+
if (!textElements || textElements.length === 0) {
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
// 创建文本组(如果不存在)
|
|
763
|
+
if (!this.textGroup) {
|
|
764
|
+
this.textGroup = new THREE.Group();
|
|
765
|
+
this.textGroup.name = 'texts';
|
|
766
|
+
this.scene.add(this.textGroup);
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
// 清空现有文本
|
|
770
|
+
while (this.textGroup.children.length > 0) {
|
|
771
|
+
var child = this.textGroup.children[0];
|
|
772
|
+
this.textGroup.remove(child);
|
|
773
|
+
|
|
774
|
+
// 释放资源
|
|
775
|
+
if (child.material && child.material.map) {
|
|
776
|
+
child.material.map.dispose();
|
|
777
|
+
}
|
|
778
|
+
if (child.material) {
|
|
779
|
+
child.material.dispose();
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// 渲染每个文本元素
|
|
784
|
+
textElements.forEach(function(textEl) {
|
|
785
|
+
if (!textEl.points || textEl.points.length === 0) return;
|
|
786
|
+
|
|
787
|
+
var point = textEl.points[0];
|
|
788
|
+
var x = point.x / 100; // Canvas pixels to 3D meters
|
|
789
|
+
var z = point.y / 100;
|
|
790
|
+
|
|
791
|
+
// 文本elevation单位是mm,需要按照墙面比例缩放(与编辑器预览一致)
|
|
792
|
+
var LOGICAL_WALL_HEIGHT = 3000; // 逻辑墙高3000mm(3米)
|
|
793
|
+
var RENDER_WALL_HEIGHT = 0.375; // 渲染墙高0.375米
|
|
794
|
+
var elevationMm = textEl.elevation || 8500; // 默认8500mm(850cm)
|
|
795
|
+
var y = (elevationMm / LOGICAL_WALL_HEIGHT) * RENDER_WALL_HEIGHT;
|
|
796
|
+
|
|
797
|
+
console.log('[SceneCore] 文本元素属性:', {
|
|
798
|
+
content: textEl.content,
|
|
799
|
+
backgroundColor: textEl.backgroundColor,
|
|
800
|
+
borderColor: textEl.borderColor,
|
|
801
|
+
color: textEl.color
|
|
802
|
+
});
|
|
803
|
+
|
|
804
|
+
var sprite = self.createTextSprite({
|
|
805
|
+
content: textEl.content || '',
|
|
806
|
+
fontSize: textEl.fontSize || 16,
|
|
807
|
+
color: textEl.color || '#000000',
|
|
808
|
+
fontFamily: textEl.fontFamily || 'Arial, sans-serif',
|
|
809
|
+
bold: textEl.bold || false,
|
|
810
|
+
italic: textEl.italic || false,
|
|
811
|
+
borderColor: textEl.borderColor || '#ADD8E6',
|
|
812
|
+
backgroundColor: textEl.backgroundColor || '#FFFFFF',
|
|
813
|
+
backgroundOpacity: textEl.backgroundOpacity !== undefined ? textEl.backgroundOpacity : 0.8,
|
|
814
|
+
padding: textEl.padding || 8
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
sprite.position.set(x, y, z);
|
|
818
|
+
sprite.userData = { type: 'text', elementId: textEl.id };
|
|
819
|
+
self.textGroup.add(sprite);
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
console.log('[SceneCore] ✅ 文本元素渲染完成:', textElements.length, '个');
|
|
823
|
+
};
|
|
824
|
+
|
|
614
825
|
/**
|
|
615
826
|
* 销毁场景
|
|
616
827
|
*/
|
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
|
};
|