Lesson12:吸附与约束系统
系列:MiniCAD第 14 / 17 篇
- MiniCAD 初级教程课程规划
- C++ 基础课:读懂 MiniCAD 所需的语言知识
- Lesson01:项目能跑起来
- Lesson02:整体架构地图
- Lesson03:程序入口与主循环
- Lesson04:输入系统与消息映射
- Lesson05:绘图工具如何工作
- Lesson06:Core 核心层:数学、几何、对象与实体
- Lesson07:命令模式与撤销重做
- Lesson08:选择、拾取与夹点编辑
- Lesson09:视口、相机与坐标转换
- Lesson10:渲染层入门
- Lesson11:文字与字体系统
- Lesson12:吸附与约束系统
- Lesson13:命令行系统
- Lesson14:多文档管理
- Lesson15:综合实战:扩展一个 MiniCAD 功能
学习目标
本课讲清楚 MiniCAD 中"鼠标落点不是鼠标点"的完整管线:输入坐标经过吸附修正和约束投影,最终变成工具收到的语义坐标。完成后学习者应能理解 SnapEngine、ConstraintEngine、InputResolver 三者的分工,并能新增一种吸附类型或修改约束角度。
相关源码
src/Editor/Snap/SnapEngine.hsrc/Editor/Snap/SnapResult.hsrc/Editor/Constraint/IConstraint.hsrc/Editor/Constraint/OrthoConstraint.hsrc/Editor/Constraint/PolarConstraint.hsrc/Editor/Constraint/ConstraintEngine.hsrc/Editor/Resolver/InputResolver.hsrc/Editor/Resolver/InputResult.hsrc/Editor/EditorContext.h
原理
输入管线分三步,依次叠加:
屏幕坐标
↓ Camera::ScreenToWorld()
原始世界坐标(rawPoint)
↓ SnapEngine::FindNearest()
吸附点(可选,snapPoint)
↓ ConstraintEngine::Apply()
约束点(可选,constrainedPoint)
↓
InputResult.point(工具最终使用的点)
吸附(Snap):SnapEngine 遍历场景实体,对每个实体计算候选吸附点(端点、中点、最近点、象限点),找到距离当前鼠标最近且在吸附半径内的点。结果保存为 SnapResult,包含点坐标和类型。
约束(Constraint):ConstraintEngine 持有 OrthoConstraint 和 PolarConstraint,在有锚点(来自工具的 GetAnchor() 或夹点的激活点)时生效。正交约束把候选点投影到最近的水平或垂直轴;极轴约束把候选点投影到距离最近的极轴射线。约束作用于吸附后的点,优先级高于吸附。
InputResolver:InputResolver::Resolve(EditorContext&) 负责按序调用上述步骤,把结果写入 ctx.resolved(即 InputResult)。
MiniCAD 中的应用
Editor::OnInput() 每帧构建 EditorContext 后立即调用 m_resolver.Resolve(ctx),工具在 OnInput(ctx) 中直接读 ctx.resolved.point。
快捷键绑定(在 Editor::HandleGlobal()):
F3:切换吸附开关ToggleSnap()F8:切换正交约束ToggleOrtho()- 极轴角度通过
SetPolarAngle()设置
渲染时,SnapResult 中的点类型和屏幕坐标由 Editor::BuildViewState() 写入 ViewState.Snap,Viewport 再用对应图标(方框/三角/十字等)绘制吸附标记。
课堂演示
- 画一条线,移动鼠标靠近另一条线的端点,观察自动吸附到端点。
- 按
F3关闭吸附,再靠近端点,观察不再吸附。 - 开启正交(
F8),画线时鼠标只能沿水平或垂直方向移动。 - 在代码中修改
SnapEngine的吸附半径,观察捕捉范围变化。
拓展练习
- 阅读
SnapEngine,找到中点吸附的计算方式,说明它如何得到线段中点。 - 在
OrthoConstraint中增加 45° 方向,使正交约束变成八方向约束。 - 在
InputResult中新增hasConstraint标记的使用,找到工具中用它做特殊处理的地方。