按住Ctrl/Shift/Alt键时,预选子装配调用hitTest返回null的问题
解决按住修饰键时无法获取预选节点dbId的问题
当已选中存在父子关系的节点组,且按住Shift/Alt/Ctrl修饰键时,调用Viewer的hitTest会返回null——这是因为Viewer默认会优先处理修饰键对应的选择逻辑(如多选、反选),拦截了常规的命中检测。以下是两种可行的替代方案:
方案一:利用hitTestAll筛选选中节点
hitTestAll不受修饰键影响,会返回光标位置所有命中的节点,我们只需从中筛选出属于当前选中组的节点即可:
private getDbIdAtCoords(x: number, y: number): number { const viewer = this.viewerWrapper.viewer; const viewport = viewer.container.getBoundingClientRect(); // 获取所有命中结果 const hits = viewer.impl.hitTestAll( x - viewport.left, y - viewport.top, false ); // 获取当前已选中的节点ID列表 const selectedDbIds = viewer.getSelection(); // 筛选出属于选中组的第一个命中节点 for (const hit of hits) { if (selectedDbIds.includes(hit.dbId)) { return hit.dbId; } } return 0; }
方案二:基于Three.js射线检测直接关联节点
绕开Viewer的默认hitTest逻辑,直接使用Three.js的射线检测,遍历已选中节点对应的几何体,找到光标命中的目标:
private getDbIdAtCoords(x: number, y: number): number { const viewer = this.viewerWrapper.viewer; const viewport = viewer.container.getBoundingClientRect(); const canvas = viewer.canvas; // 将屏幕坐标转换为WebGL标准设备坐标 const mouseX = ((x - viewport.left) / canvas.width) * 2 - 1; const mouseY = -((y - viewport.top) / canvas.height) * 2 + 1; // 创建射线投射器 const raycaster = new THREE.Raycaster(); raycaster.setFromCamera({ x: mouseX, y: mouseY }, viewer.impl.camera); // 收集所有已选中节点对应的Three.js网格 const selectedDbIds = viewer.getSelection(); const selectedObjects: THREE.Object3D[] = []; const fragList = viewer.model.getFragmentList(); selectedDbIds.forEach(dbId => { const fragIds = fragList.getFragIds(dbId); fragIds.forEach(fragId => { const mesh = fragList.getVizmesh(fragId); if (mesh) { selectedObjects.push(mesh); } }); }); // 执行射线检测,获取命中结果 const intersects = raycaster.intersectObjects(selectedObjects, true); if (intersects.length > 0) { // 从命中的网格反向获取对应的节点dbId const mesh = intersects[0].object as any; return mesh.fragId ? fragList.getNodeId(mesh.fragId) : 0; } return 0; }
注意事项
- 方案一的
hitTestAll会返回场景中所有层级的命中节点,筛选时确保只保留已选中的节点,避免获取到无关对象。 - 方案二需要处理模型的碎片(fragId)与节点(dbId)的映射关系,因为单个dbId可能对应多个几何体碎片。
内容的提问来源于stack exchange,提问作者arkkuba
相关产品推荐
相关产品推荐

