snap
本文回答:
SnapManager对象捕捉 —— 10 种模式、位标志组合、开关、querySnap。
一、10 种捕捉模式
SnapMode 是位标志枚举,成员名为缩写(END/MID/…不是 Endpoint/Midpoint):
| 成员 | 位标志 | SnapType | 说明 |
|---|---|---|---|
SnapMode.END |
1 |
'end' |
线 / 弧 端点 |
SnapMode.MID |
2 |
'mid' |
线段中点 |
SnapMode.CEN |
4 |
'cen' |
圆 / 弧 圆心 |
SnapMode.NOD |
8 |
'nod' |
点实体节点 |
SnapMode.QUA |
16 |
'qua' |
圆 / 椭圆象限点 |
SnapMode.INT |
32 |
'int' |
实体间交点 |
SnapMode.INS |
64 |
'ins' |
块引用插入点 |
SnapMode.PER |
128 |
'per' |
垂足 |
SnapMode.TAN |
256 |
'tan' |
切点 |
SnapMode.NEA |
512 |
'nea' |
实体上最近点 |
SnapMode.ALL |
1023 |
— | 全部 10 种(1|2|…|512) |
SnapMode.NONE |
16384 |
— | 特殊值,不与其他位组合 |
二、启用 / 模式
const { SnapMode } = vjmapext;
mapcadLayer.setSnapEnabled(true);
mapcadLayer.setSnapMode(
SnapMode.END | SnapMode.MID | SnapMode.CEN | SnapMode.INT
);
// 全启 / 全关
mapcadLayer.setSnapMode(SnapMode.ALL); // 1023
mapcadLayer.setSnapMode(0); // 全关
// 关开关但保留模式
mapcadLayer.setSnapEnabled(false);
等价直读 / 直写:
mapcadLayer.snapManager.enabled; // bool
mapcadLayer.snapManager.snapMode; // 当前位掩码
mapcadLayer.snapManager.setSnapMode(SnapMode.END | SnapMode.INT);
mapcadLayer.snapManager.setOnChange(() => refreshSnapUI()); // 模式变化回调
三、阈值(像素)
mapcadLayer.snapManager.snapPixelRadius; // 当前像素半径
mapcadLayer.snapManager.setSnapRadius(15); // 像素,不是 CAD 单位
querySnap 内部把像素半径换算成 CAD 距离 snapRadiusCad,zoom 变化不影响命中手感。snapPixelRadius 默认 10 像素。
补充:
snapMode会持久化到localStorage['mapcad-snap-mode'](setSnapMode自动写入、构造时读回)——刷新页面后捕捉模式仍在;enabled开关不持久化。
四、querySnap:手动查捕捉点
const hit = mapcadLayer.snapManager.querySnap(
[120, 88], // 光标 CAD 坐标(Point2DLike = Point2D | [x,y])
snapRadiusCad, // CAD 单位半径
referencePoint // 可选:参考点(PER/TAN 需要),参数名 referencePoint
);
// hit = SnapPoint | null
SnapPoint 结构(字段是 position 不是 point):
interface SnapPoint {
type: SnapType; // 'end'|'mid'|'cen'|'nod'|'qua'|'int'|'ins'|'per'|'tan'|'nea'
position: Point2D;
entityId: string; // 命中的实体
primary?: boolean; // 距离最近的候选点
}
五、getSnapModeList(用于 UI)
const { getSnapModeList } = vjmapext;
getSnapModeList();
// [{ mode: 1, type: 'end', label: '端点' },
// { mode: 2, type: 'mid', label: '中点' },
// ... 共 10 项,字段名是 label 不是 name]
可直接拿来渲染"对象捕捉"对话框。
六、和 getPoint 的关系
getPoint 默认走 SnapManager.querySnap:鼠标靠近实体特征点时橡皮筋终点会"吸"过去,result.value 就是捕捉点的精确坐标。
const r = await im.getPoint({ message: '指定终点', useBasePoint: true, basePoint: p1 });
某一步需要禁用捕捉:
mapcadLayer.setSnapEnabled(false);
const r = await im.getPoint({ message: '指定任意位置' });
mapcadLayer.setSnapEnabled(true);
七、bindDraw:把捕捉挂到 UI 控件
const btn = document.getElementById('btn-snap-endpoint');
btn.onclick = () => {
const cur = mapcadLayer.snapManager.snapMode;
mapcadLayer.setSnapMode(cur ^ SnapMode.END); // 按位异或切换
btn.classList.toggle('active');
};
八、临时点捕捉
setTemporaryPoints 注入临时捕捉源(如绘制中的辅助点):
mapcadLayer.snapManager.setTemporaryPoints([[0, 0], [50, 0]]);
mapcadLayer.snapManager.clearTemporaryPoints();
⚠️ 在 getPoint 等待期间,每次鼠标移动 InputManager 都会执行 snapManager.setTemporaryPoints(previewManager.polylineVertices)——手动注入的点下一次 mousemove 就被覆盖;且临时点只在 SnapMode.END 开启时参与(按 end 类型)。要在 getPoint 中捕捉自定义辅助点,改喂 PreviewManager:
ctx.previewManager.setPolylineVertices(pts); // pts: Point2D[];会被当作临时捕捉点
// 注意:useBasePoint 橡皮筋会把这些点连成折线预览;previewManager.clear() 会清掉它们
九、内部机制
用户鼠标移动 → SnapManager.querySnap:
1. SpatialIndex.search(bbox) 取候选实体
2. 每个实体算各模式候选点(NEA 单独计算,与其他点去重)
3. 合并 → 过滤距离 > snapRadiusCad 的
4. 按距离排序,最近的标 primary
十、关于正交 / F 键
1.0.1 没有内置正交模式(PointInputOptions.ignoreOrthoMode 是预留字段,引擎侧没有 ortho 实现),也没有 F8/F3 快捷键——ShortcutMap 的 19 个动作里不含 ortho/snap 切换,要自己监听 keydown 再调 setSnapEnabled / useForceAngle。
十一、常见坑
- 枚举名是缩写:写
SnapMode.Endpoint会得到undefined,位或后等于没开——用END/MID/CEN/NOD/QUA/INT/INS/PER/TAN/NEA。 SnapPoint.point不存在:字段叫position,取坐标写hit.position。snapPixelRadius是像素:不是 CAD 单位;setSnapRadius同样按像素。- PER/TAN 需要参考点:
querySnap第三参referencePoint缺省时这两个模式算不出。 - 复杂实体多候选:多段线每个顶点都算 END,命中后
primary标记最近的那个。 - 栅格底图实体不参与捕捉:捕捉只作用于 MapCadLayer 内部实体(
addEntity进去的);DWG 底图原实体不在 SpatialIndex 里。 - 模式变化要刷新 UI:捕捉对话框勾选态不会自动同步,用
snapManager.setOnChange监听。
相关
- 点输入 → input-manager
- 夹点编辑(拖拽不走捕捉) → grip
- 橡皮筋预览 → preview
snapManager.snap(...)是内部方法:实例上存在snap但snap(0.1,0.1)静默返undefined——它不吃(x,y)坐标参;公开查询入口只有querySnap(cursorCadPt, snapRadiusCad, referencePoint?)。snapManager宿主是mapcadLayer.snapManager(已实例化),setTemporaryPoints/setSnapMode/setSnapRadius/setOnChange均可程序化直调。