@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kimap/indoor-positioning-sdk-vue2",
3
- "version": "5.3.9",
3
+ "version": "5.4.1",
4
4
  "description": "Vue2自包含室内定位SDK - 完全兼容Webpack3+Babel6 | Vue2 Self-Contained Indoor Positioning SDK",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -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
- var slabMaxThickness = Math.max(0.05, 0.01 * maxDim); // 厚度上限
905
- var minFootprint = Math.max(1, 0.05 * modelSize.x * modelSize.z); // 最小占地面积(比固定 1000 更灵活)
906
- var epsilon = Math.max(0.01, 0.002 * modelSize.y); // 贴底判定容差
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
- // 2. 计算世界空间包围盒(考虑旋转/缩放)
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
- // 3. 底垫判定:必须同时满足「贴底」+「薄片」+「够大」
927
- var isBottomSlab = (
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
- 'isBottomSlab:', isBottomSlab
1113
+ 'luminance:', luminance >= 0 ? luminance.toFixed(2) : 'N/A',
1114
+ 'isSlab:', isSlab
938
1115
  );
939
1116
  }
940
1117
 
941
- if (isBottomSlab) {
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));
@@ -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
  };