Skip to content

viewer.selection — SelectionManager

设计意图

viewer.selection 是「用户正在关注什么」的单一事实源:节点 / 面 / 边三种粒度的选中项共存于一个选中集,结构树、属性栏等 UI 与引擎内的视觉反馈(染色 + 描边)都从它派生,不需要各自维护一份选中状态。

域内刻意把拾取选中分离:pick / pickBox 是纯查询(命中什么返回什么,不改选中集),apply / clear 才是变更入口。默认的「点击即选中、移动即悬停」开箱即用,也可以分别关掉(setAutoSelectOnClick / setAutoHoverOnMove),改由「拾取 → 业务决策 → apply」组合出框选、双击、右键菜单等自定义手势。可拾取粒度(setPickableMask)与高亮样式(setHighlightMode / setStyle)是两组独立旋钮。

状态变化经 selection-changed / hover-changed 广播,且只在净变化时发(payload 带 added / removed / current)——UI 同步不需要自己 diff,也不会因重复设置同一集合而抖动。

何时用它:结构树 / 属性栏与画布的选中同步、框选 / 双击 / 右键菜单等自定义拾取手势、限定可选粒度与高亮风格的业务标注模式。「选中之后看哪」归 viewer.camera,「选中之后改外观」归 viewer.model

典型用法

结构树与画布双向同步(同一事实源,两个方向都不会成环):

ts
import { SelectionItem, SelectionMode } from "@modelcubes/viewer-core";

// 树 → 画布:点结构树行即替换选中
tree.onRowClick = (id) => viewer.selection.apply(SelectionItem.node(id), SelectionMode.Set);

// 画布 → 树:滚动并高亮对应行
viewer.on("selection-changed", (e) => tree.reveal(e.current.map((it) => it.nodeId)));

框选追加(拖出矩形后把框内零件加进选中集):

ts
const hits = viewer.selection.pickBox(x1, y1, x2, y2, { mustBeFullyInside: true });
if (hits.length > 0) viewer.selection.apply(hits, SelectionMode.Add);

「标记加工面」模式(只拾取面 + 自定义选中色):

ts
import { SelectionMask } from "@modelcubes/viewer-core";

viewer.selection.setPickableMask(SelectionMask.Face);
viewer.selection.setStyle({ selectColor: { r: 0.1, g: 0.5, b: 0.95 } });
// 退出模式时恢复引擎默认粒度(Node,零件级)
viewer.selection.setPickableMask(SelectionMask.Node);

注意事项

  • pickBox v1 仅支持 Node 粒度:mask 含 Face / Edge 位会降级并告警;模型未就绪时返回空数组。
  • apply 的 item 非法即抛 ViewerError(nodeId 不存在、Face 项缺 faceGroupId、Edge 项缺 edgeId、未加载模型);无净变化时不触发 selection-changed,别指望靠重复 apply 强制刷新 UI。
  • pick 坐标是画布局部 CSS 像素(相对画布左上角),从鼠标事件接线需用 getBoundingClientRect 换算,见选择与拾取
  • setAutoHoverOnMove(false) 会同时清除当前悬停,避免高亮残留。

完整签名与延伸