@kimap/indoor-positioning-sdk-vue2 5.3.9 → 5.4.1
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/core/SceneCore.js +192 -15
- package/src/core/parsers.js +25 -1
package/package.json
CHANGED
package/src/core/SceneCore.js
CHANGED
|
@@ -29,6 +29,7 @@ var extractColorsFromOBJ = parsers.extractColorsFromOBJ;
|
|
|
29
29
|
var extractTextElementsFromOBJ = parsers.extractTextElementsFromOBJ;
|
|
30
30
|
var extractWallOpacityFromOBJ = parsers.extractWallOpacityFromOBJ;
|
|
31
31
|
var extractShapeOpacityFromOBJ = parsers.extractShapeOpacityFromOBJ;
|
|
32
|
+
var extractCalibrationBoundaryAreaIdsFromOBJ = parsers.extractCalibrationBoundaryAreaIdsFromOBJ;
|
|
32
33
|
|
|
33
34
|
/**
|
|
34
35
|
* SceneCore 构造函数
|
|
@@ -64,6 +65,8 @@ function SceneCore(config) {
|
|
|
64
65
|
this.floorGroups = []; // 楼层Group数组
|
|
65
66
|
this.floorHeight = 3; // 每层高度(米)
|
|
66
67
|
this.isMultiFloor = Array.isArray(config.objUrl); // 是否多楼层模式
|
|
68
|
+
/** @type {string[]} 从 OBJ 注释解析的校准边界区域 ID(仅此类区域需隐藏) */
|
|
69
|
+
this._calibrationBoundaryAreaIds = [];
|
|
67
70
|
}
|
|
68
71
|
|
|
69
72
|
/**
|
|
@@ -426,6 +429,8 @@ SceneCore.prototype._loadKimapFile = function(kimapUrl, themeUrl, objLoader, mtl
|
|
|
426
429
|
if (mapConfig && mapConfig.isCalibrated) {
|
|
427
430
|
self._mapConfig = mapConfig;
|
|
428
431
|
}
|
|
432
|
+
|
|
433
|
+
self._calibrationBoundaryAreaIds = extractCalibrationBoundaryAreaIdsFromOBJ(objContent);
|
|
429
434
|
|
|
430
435
|
// 提取颜色信息(作为备用)
|
|
431
436
|
var colorMap = materials ? {} : extractColorsFromOBJ(objContent);
|
|
@@ -499,6 +504,7 @@ SceneCore.prototype._loadOBJFile = function(url, objLoader, resolve, reject) {
|
|
|
499
504
|
url,
|
|
500
505
|
function(object) {
|
|
501
506
|
console.log('OBJ文件下载完成,开始处理...');
|
|
507
|
+
self._calibrationBoundaryAreaIds = [];
|
|
502
508
|
self._processLoadedModel(object, null, {});
|
|
503
509
|
resolve();
|
|
504
510
|
},
|
|
@@ -593,6 +599,8 @@ SceneCore.prototype._loadKimapFileToGroup = function(kimapUrl, themeUrl, objLoad
|
|
|
593
599
|
if (mapConfig && mapConfig.isCalibrated) {
|
|
594
600
|
self._mapConfig = mapConfig;
|
|
595
601
|
}
|
|
602
|
+
|
|
603
|
+
self._calibrationBoundaryAreaIds = extractCalibrationBoundaryAreaIdsFromOBJ(objContent);
|
|
596
604
|
}
|
|
597
605
|
|
|
598
606
|
var colorMap = materials ? {} : extractColorsFromOBJ(objContent);
|
|
@@ -646,6 +654,9 @@ SceneCore.prototype._loadOBJFileToGroup = function(url, objLoader, floorGroup, f
|
|
|
646
654
|
url,
|
|
647
655
|
function(object) {
|
|
648
656
|
console.log('楼层OBJ文件下载完成,开始处理...');
|
|
657
|
+
if (floorIndex === 0) {
|
|
658
|
+
self._calibrationBoundaryAreaIds = [];
|
|
659
|
+
}
|
|
649
660
|
self._processLoadedModelToGroup(object, null, {}, floorGroup, floorIndex);
|
|
650
661
|
resolve();
|
|
651
662
|
},
|
|
@@ -760,6 +771,8 @@ SceneCore.prototype._processLoadedModel = function(object, materials, colorMap)
|
|
|
760
771
|
|
|
761
772
|
// 检测并隐藏大面积黑色底面mesh
|
|
762
773
|
self._hideLargeBlackGroundMesh(self.mapModel);
|
|
774
|
+
// 仅隐藏「校准边界」区域(真实世界尺寸检测用),普通 Area_ 地面保留
|
|
775
|
+
self._hideCalibrationBoundaryAreaMeshes(self.mapModel);
|
|
763
776
|
|
|
764
777
|
// 自动调整相机位置
|
|
765
778
|
self._adjustCameraToModel();
|
|
@@ -804,6 +817,7 @@ SceneCore.prototype._processLoadedModelToGroup = function(object, materials, col
|
|
|
804
817
|
|
|
805
818
|
// 检测并隐藏大面积黑色底面mesh
|
|
806
819
|
self._hideLargeBlackGroundMesh(object);
|
|
820
|
+
self._hideCalibrationBoundaryAreaMeshes(object);
|
|
807
821
|
};
|
|
808
822
|
|
|
809
823
|
/**
|
|
@@ -885,9 +899,90 @@ SceneCore.prototype._isHiddenGroundMesh = function(name) {
|
|
|
885
899
|
});
|
|
886
900
|
};
|
|
887
901
|
|
|
902
|
+
/**
|
|
903
|
+
* 遍历 mesh 上所有材质的 name(支持多材质)
|
|
904
|
+
* @private
|
|
905
|
+
*/
|
|
906
|
+
SceneCore.prototype._eachMeshMaterialName = function(mesh, callback) {
|
|
907
|
+
var m = mesh.material;
|
|
908
|
+
if (!m) return;
|
|
909
|
+
if (Array.isArray(m)) {
|
|
910
|
+
for (var i = 0; i < m.length; i++) {
|
|
911
|
+
if (m[i] && m[i].name) {
|
|
912
|
+
callback(m[i].name);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
} else if (m.name) {
|
|
916
|
+
callback(m.name);
|
|
917
|
+
}
|
|
918
|
+
};
|
|
919
|
+
|
|
920
|
+
/**
|
|
921
|
+
* 是否任一材质名以指定前缀开头
|
|
922
|
+
* @private
|
|
923
|
+
*/
|
|
924
|
+
SceneCore.prototype._meshMaterialStartsWith = function(mesh, prefix) {
|
|
925
|
+
var found = false;
|
|
926
|
+
this._eachMeshMaterialName(mesh, function(name) {
|
|
927
|
+
if (name.indexOf(prefix) === 0) {
|
|
928
|
+
found = true;
|
|
929
|
+
}
|
|
930
|
+
});
|
|
931
|
+
return found;
|
|
932
|
+
};
|
|
933
|
+
|
|
934
|
+
/**
|
|
935
|
+
* 隐藏「校准边界」区域 mesh(绘制端 isCalibrationBoundary / OBJ 中 Y=-0.02)
|
|
936
|
+
* 普通地面 Area_ 必须保留可见;仅此类检测用区域不显示。
|
|
937
|
+
* @private
|
|
938
|
+
*/
|
|
939
|
+
SceneCore.prototype._hideCalibrationBoundaryAreaMeshes = function(object) {
|
|
940
|
+
var self = this;
|
|
941
|
+
var explicitIds = self._calibrationBoundaryAreaIds || [];
|
|
942
|
+
var hasExplicit = explicitIds.length > 0;
|
|
943
|
+
var idSet = {};
|
|
944
|
+
for (var j = 0; j < explicitIds.length; j++) {
|
|
945
|
+
idSet[explicitIds[j]] = true;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
object.traverse(function(child) {
|
|
949
|
+
if (!(child instanceof THREE.Mesh)) return;
|
|
950
|
+
if (!child.material || !child.geometry) return;
|
|
951
|
+
|
|
952
|
+
var areaIdsOnMesh = [];
|
|
953
|
+
self._eachMeshMaterialName(child, function(name) {
|
|
954
|
+
if (name.indexOf('Area_') === 0) {
|
|
955
|
+
areaIdsOnMesh.push(name.replace(/^Area_/, ''));
|
|
956
|
+
}
|
|
957
|
+
});
|
|
958
|
+
if (areaIdsOnMesh.length === 0) return;
|
|
959
|
+
|
|
960
|
+
child.geometry.computeBoundingBox();
|
|
961
|
+
var bb = child.geometry.boundingBox;
|
|
962
|
+
if (!bb) return;
|
|
963
|
+
|
|
964
|
+
// objExporter:校准边界顶点 Y=-0.02,普通区域 Y=0(局部几何空间)
|
|
965
|
+
var geoLooksCalib = bb.max.y < -0.005;
|
|
966
|
+
|
|
967
|
+
var matchExplicit = false;
|
|
968
|
+
for (var k = 0; k < areaIdsOnMesh.length; k++) {
|
|
969
|
+
if (hasExplicit && idSet[areaIdsOnMesh[k]]) {
|
|
970
|
+
matchExplicit = true;
|
|
971
|
+
break;
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
if (matchExplicit || (!hasExplicit && geoLooksCalib)) {
|
|
976
|
+
child.visible = false;
|
|
977
|
+
console.log('[KimapSDK] 已隐藏校准边界区域:', areaIdsOnMesh.join(','),
|
|
978
|
+
'(explicit:', matchExplicit, 'geometryY:', geoLooksCalib, ')');
|
|
979
|
+
}
|
|
980
|
+
});
|
|
981
|
+
};
|
|
982
|
+
|
|
888
983
|
/**
|
|
889
984
|
* 检测并隐藏模型自带的大面积底垫mesh
|
|
890
|
-
*
|
|
985
|
+
* 基于世界空间包围盒的启发式判断(薄片+贴底+相对面积+颜色)
|
|
891
986
|
* @private
|
|
892
987
|
*/
|
|
893
988
|
SceneCore.prototype._hideLargeBlackGroundMesh = function(object) {
|
|
@@ -900,45 +995,127 @@ SceneCore.prototype._hideLargeBlackGroundMesh = function(object) {
|
|
|
900
995
|
modelBox.getSize(modelSize);
|
|
901
996
|
var maxDim = Math.max(modelSize.x, modelSize.z);
|
|
902
997
|
|
|
903
|
-
// 2.
|
|
904
|
-
|
|
905
|
-
var
|
|
906
|
-
|
|
998
|
+
// 2. 动态阈值(相对于模型尺度,调整为更宽松的值)
|
|
999
|
+
// slabMaxThickness: 厚度上限,降低以检测更薄的底板
|
|
1000
|
+
var slabMaxThickness = Math.max(0.02, 0.003 * maxDim);
|
|
1001
|
+
// minFootprint: 最小占地面积,降低以检测更小的底板
|
|
1002
|
+
var minFootprint = Math.max(0.5, 0.005 * modelSize.x * modelSize.z);
|
|
1003
|
+
// epsilon: 贴底判定容差,放宽以检测更多情况
|
|
1004
|
+
var epsilon = Math.max(0.05, 0.01 * modelSize.y);
|
|
907
1005
|
|
|
908
1006
|
var debugMode = this.config && this.config.debugBottomMeshes === true;
|
|
909
1007
|
|
|
1008
|
+
/**
|
|
1009
|
+
* 获取材质的亮度(检查是否为黑色/深色)
|
|
1010
|
+
* @param {THREE.Object3D} child - 3D对象
|
|
1011
|
+
* @returns {number} 亮度值(0-1),-1表示无颜色信息
|
|
1012
|
+
*/
|
|
1013
|
+
function getMaterialLuminance(child) {
|
|
1014
|
+
var mat = child.material;
|
|
1015
|
+
if (!mat) return -1;
|
|
1016
|
+
var color = mat.color;
|
|
1017
|
+
if (!color) return -1;
|
|
1018
|
+
var r = color.r, g = color.g, b = color.b;
|
|
1019
|
+
return 0.299 * r + 0.587 * g + 0.114 * b;
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
/**
|
|
1023
|
+
* 检查是否为需要跳过颜色检测的物体类型
|
|
1024
|
+
* 这些物体即使颜色较深也不应该被隐藏
|
|
1025
|
+
* @param {string} name - 物体名称
|
|
1026
|
+
* @returns {boolean} 是否跳过
|
|
1027
|
+
*/
|
|
1028
|
+
function shouldSkipColorCheck(name) {
|
|
1029
|
+
if (!name) return false;
|
|
1030
|
+
var lowerName = name.toLowerCase();
|
|
1031
|
+
// 跳过有明确类型的物体
|
|
1032
|
+
var skipPatterns = ['wall_', 'area_', 'shape_', 'stair_', 'door_', 'window_', 'furniture_', 'desk_', 'chair_', 'table_', 'cabinet_', 'bed_', 'sofa_', 'shelf_', 'rack_', 'plant_', 'tree_', 'lamp_', 'light_'];
|
|
1033
|
+
return skipPatterns.some(function(pattern) {
|
|
1034
|
+
return lowerName.startsWith(pattern);
|
|
1035
|
+
});
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
/**
|
|
1039
|
+
* 检查是否为底垫mesh
|
|
1040
|
+
* 条件1: 贴底 + 薄片 + 够大(原有逻辑)
|
|
1041
|
+
* 条件2: 贴底 + 超薄 + 深色(新增:检测无名称的黑色底板)
|
|
1042
|
+
* 条件3: 贴底 + 中等厚度 + 超大深色(新增:检测无名称的厚深色底板)
|
|
1043
|
+
* 条件4: 极薄 + 极大深色面(检测超大面积薄板)
|
|
1044
|
+
* @param {THREE.Mesh} child - 网格对象
|
|
1045
|
+
* @param {THREE.Vector3} sizeWorld - 世界空间尺寸
|
|
1046
|
+
* @param {number} worldMinY - 世界空间最小Y
|
|
1047
|
+
* @returns {boolean} 是否为底垫mesh
|
|
1048
|
+
*/
|
|
1049
|
+
function isBottomSlab(child, sizeWorld, worldMinY) {
|
|
1050
|
+
var luminance = getMaterialLuminance(child);
|
|
1051
|
+
var childName = child.name || '';
|
|
1052
|
+
var skipColor = shouldSkipColorCheck(childName);
|
|
1053
|
+
|
|
1054
|
+
// 条件1: 原有逻辑(贴底 + 薄片 + 够大)- 始终适用
|
|
1055
|
+
var cond1 = (
|
|
1056
|
+
worldMinY <= modelMinY + epsilon &&
|
|
1057
|
+
sizeWorld.y < slabMaxThickness &&
|
|
1058
|
+
sizeWorld.x * sizeWorld.z > minFootprint
|
|
1059
|
+
);
|
|
1060
|
+
|
|
1061
|
+
// 条件2: 贴底 + 超薄 + 深色(只对无明确名称的物体生效)
|
|
1062
|
+
var cond2 = !skipColor && (
|
|
1063
|
+
worldMinY <= modelMinY + epsilon * 2 &&
|
|
1064
|
+
sizeWorld.y < slabMaxThickness * 0.5 &&
|
|
1065
|
+
luminance >= 0 && luminance < 0.15
|
|
1066
|
+
);
|
|
1067
|
+
|
|
1068
|
+
// 条件3: 贴底 + 较大面积 + 深色(只对无明确名称的物体生效)
|
|
1069
|
+
var cond3 = !skipColor && (
|
|
1070
|
+
worldMinY <= modelMinY + epsilon &&
|
|
1071
|
+
sizeWorld.x * sizeWorld.z > minFootprint * 5 &&
|
|
1072
|
+
luminance >= 0 && luminance < 0.1
|
|
1073
|
+
);
|
|
1074
|
+
|
|
1075
|
+
// 条件4: 极薄 + 极大深色面(只对无明确名称的物体生效)
|
|
1076
|
+
var cond4 = !skipColor && (
|
|
1077
|
+
worldMinY <= modelMinY + epsilon &&
|
|
1078
|
+
sizeWorld.y < slabMaxThickness * 0.3 &&
|
|
1079
|
+
sizeWorld.x * sizeWorld.z > minFootprint * 2 &&
|
|
1080
|
+
luminance >= 0 && luminance < 0.2
|
|
1081
|
+
);
|
|
1082
|
+
|
|
1083
|
+
return cond1 || cond2 || cond3 || cond4;
|
|
1084
|
+
}
|
|
1085
|
+
|
|
910
1086
|
object.traverse(function(child) {
|
|
911
1087
|
if (!(child instanceof THREE.Mesh)) return;
|
|
912
1088
|
if (child.visible === false) return;
|
|
913
1089
|
|
|
914
|
-
//
|
|
1090
|
+
// 名称已明确匹配的,保持隐藏
|
|
915
1091
|
if (self._isHiddenGroundMesh(child.name)) return;
|
|
916
1092
|
|
|
1093
|
+
// Area_ 地面为薄片且贴模型底边,会被 cond1 误判为底垫;校准区由 _hideCalibrationBoundaryAreaMeshes 单独处理
|
|
1094
|
+
if (self._meshMaterialStartsWith(child, 'Area_')) return;
|
|
1095
|
+
|
|
917
1096
|
if (!child.geometry || !child.geometry.attributes.position) return;
|
|
918
1097
|
|
|
919
|
-
//
|
|
1098
|
+
// 计算世界空间包围盒(考虑旋转/缩放)
|
|
920
1099
|
child.geometry.computeBoundingBox();
|
|
921
1100
|
var worldBox = new THREE.Box3().setFromObject(child);
|
|
922
1101
|
var sizeWorld = new THREE.Vector3();
|
|
923
1102
|
worldBox.getSize(sizeWorld);
|
|
924
1103
|
var worldMinY = worldBox.min.y;
|
|
925
1104
|
|
|
926
|
-
//
|
|
927
|
-
var
|
|
928
|
-
worldMinY <= modelMinY + epsilon && // 贴底
|
|
929
|
-
sizeWorld.y < slabMaxThickness && // 薄片
|
|
930
|
-
sizeWorld.x * sizeWorld.z > minFootprint // 够大
|
|
931
|
-
);
|
|
1105
|
+
// 底垫判定
|
|
1106
|
+
var isSlab = isBottomSlab(child, sizeWorld, worldMinY);
|
|
932
1107
|
|
|
933
1108
|
if (debugMode) {
|
|
1109
|
+
var luminance = getMaterialLuminance(child);
|
|
934
1110
|
console.log('[debugBottomMeshes]', child.name || 'unnamed',
|
|
935
1111
|
'worldMinY:', worldMinY.toFixed(3),
|
|
936
1112
|
'size:', sizeWorld.x.toFixed(1), 'x', sizeWorld.z.toFixed(1), 'x', sizeWorld.y.toFixed(3),
|
|
937
|
-
'
|
|
1113
|
+
'luminance:', luminance >= 0 ? luminance.toFixed(2) : 'N/A',
|
|
1114
|
+
'isSlab:', isSlab
|
|
938
1115
|
);
|
|
939
1116
|
}
|
|
940
1117
|
|
|
941
|
-
if (
|
|
1118
|
+
if (isSlab) {
|
|
942
1119
|
child.visible = false;
|
|
943
1120
|
console.log('[KimapSDK] Hidden bottom slab mesh:', child.name || 'unnamed',
|
|
944
1121
|
'size:', sizeWorld.x.toFixed(1), 'x', sizeWorld.z.toFixed(1), 'x', sizeWorld.y.toFixed(3));
|
package/src/core/parsers.js
CHANGED
|
@@ -242,6 +242,29 @@ function extractShapeOpacityFromOBJ(objContent) {
|
|
|
242
242
|
return shapeOpacity;
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
+
/**
|
|
246
|
+
* 从 OBJ 中提取「真实世界尺寸校准边界」区域 ID(与绘制端 objExporter 中 # KIMAP_AREA_CALIBRATION_BOUNDARY 对应)
|
|
247
|
+
* 仅此类区域不应显示;普通 Area_ 地面应保留。
|
|
248
|
+
*/
|
|
249
|
+
function extractCalibrationBoundaryAreaIdsFromOBJ(objContent) {
|
|
250
|
+
var ids = [];
|
|
251
|
+
if (!objContent || typeof objContent !== 'string') {
|
|
252
|
+
return ids;
|
|
253
|
+
}
|
|
254
|
+
var lines = objContent.split('\n');
|
|
255
|
+
var prefix = '# KIMAP_AREA_CALIBRATION_BOUNDARY ';
|
|
256
|
+
for (var i = 0; i < lines.length; i++) {
|
|
257
|
+
var line = lines[i].trim();
|
|
258
|
+
if (line.indexOf(prefix) === 0) {
|
|
259
|
+
var id = line.substring(prefix.length).trim();
|
|
260
|
+
if (id) {
|
|
261
|
+
ids.push(id);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return ids;
|
|
266
|
+
}
|
|
267
|
+
|
|
245
268
|
module.exports = {
|
|
246
269
|
extractBorderFromOBJ: extractBorderFromOBJ,
|
|
247
270
|
extractMapConfigFromOBJ: extractMapConfigFromOBJ,
|
|
@@ -249,5 +272,6 @@ module.exports = {
|
|
|
249
272
|
extractCoordinateSystem: extractCoordinateSystem,
|
|
250
273
|
extractTextElementsFromOBJ: extractTextElementsFromOBJ,
|
|
251
274
|
extractWallOpacityFromOBJ: extractWallOpacityFromOBJ,
|
|
252
|
-
extractShapeOpacityFromOBJ: extractShapeOpacityFromOBJ
|
|
275
|
+
extractShapeOpacityFromOBJ: extractShapeOpacityFromOBJ,
|
|
276
|
+
extractCalibrationBoundaryAreaIdsFromOBJ: extractCalibrationBoundaryAreaIdsFromOBJ
|
|
253
277
|
};
|