- New FaceInferToolPlugin: pixel-neighborhood method with {2,5,10}px offsets
- New FaceInferResult data class
- Test button in SystemManagement page
- Cursor: MarkupAutoPoly (arrow + dashed box)
- Uses project-standard tools: FindNamedParentContainer, viewpoint.Position
- Design doc at doc/design/2026/face-infer-tool-design.md
112 lines
3.4 KiB
Markdown
112 lines
3.4 KiB
Markdown
# 快速取面工具设计
|
||
|
||
创建时间:2026-06-02
|
||
|
||
## 目标
|
||
|
||
新增一个独立 ToolPlugin,通过单点+像素邻域法快速推断鼠标点击位置所在面的法向量。
|
||
和取点工具 `PathClickToolPlugin` 并列,互斥激活。
|
||
|
||
## 核心算法
|
||
|
||
单点+像素邻域法 —— 一次点击内做三次 `PickItemFromPoint`:
|
||
|
||
```
|
||
中心点: PickItemFromPoint(x, y) → p0
|
||
右邻域: PickItemFromPoint(x+δ, y) → p1
|
||
下邻域: PickItemFromPoint(x, y+δ) → p2
|
||
|
||
v1 = p1 - p0
|
||
v2 = p2 - p0
|
||
normal = normalize(cross(v1, v2))
|
||
```
|
||
|
||
偏移量 δ 三级递进:2px → 5px → 10px,小偏移共线时自动退到更大偏移。
|
||
|
||
## 新增文件
|
||
|
||
| 文件 | 职责 |
|
||
|---|---|
|
||
| `src/Models/FaceInferResult.cs` | 结果数据类 |
|
||
| `src/Core/FaceInferToolPlugin.cs` | ToolPlugin 主体 |
|
||
|
||
不修改任何现有文件。
|
||
|
||
## 架构
|
||
|
||
```
|
||
FaceInferToolPlugin : ToolPlugin
|
||
│
|
||
├── Plugin("FaceInferTool", "NavisworksTransport")
|
||
│
|
||
├── Static 成员
|
||
│ ├── event FaceInferred # 外部订阅事件
|
||
│ ├── AssemblyPath # 用于动态加载
|
||
│ └── TriggerFaceInferred() # 允许外部手动触发
|
||
│
|
||
├── GetCursor → Cursor.MarkupQuickPick
|
||
│
|
||
├── MouseDown(x, y)
|
||
│ ├── centerPick = PickItemFromPoint(x, y)
|
||
│ ├── rightPick = PickItemFromPoint(x + δ, y)
|
||
│ ├── downPick = PickItemFromPoint(x, y + δ)
|
||
│ ├── 同构件校验 (ModelItem 比较)
|
||
│ ├── 共线校验 (cross.Length < 0.001 → 退到下一级 δ)
|
||
│ ├── 法向量归一化
|
||
│ ├── 朝向统一 (dot with viewDir, 始终指向相机)
|
||
│ └── 触发 FaceInferred 事件
|
||
│
|
||
└── MouseMove → 暂不处理(轻量原则)
|
||
```
|
||
|
||
## FaceInferResult 数据结构
|
||
|
||
```csharp
|
||
public class FaceInferResult
|
||
{
|
||
public Point3D HitPoint { get; } // 命中点(宿主坐标系)
|
||
public Vector3D Normal { get; } // 面法向量(已归一化,朝向相机)
|
||
public ModelItem ModelItem { get; } // 命中的 ModelItem
|
||
public bool IsValid { get; } // 推断是否成功
|
||
}
|
||
```
|
||
|
||
所有坐标均为**宿主坐标系**(`PickItemFromPoint` 原始输出)。
|
||
|
||
## 激活方式
|
||
|
||
与 `PathClickToolPlugin` 完全相同的流程,仅插件名不同:
|
||
|
||
```csharp
|
||
var assemblyPath = FaceInferToolPlugin.AssemblyPath;
|
||
Application.Plugins.AddPluginAssembly(assemblyPath);
|
||
var record = (ToolPluginRecord)Application.Plugins.FindPlugin("FaceInferTool.NavisworksTransport");
|
||
var plugin = record.LoadPlugin();
|
||
Application.MainDocument.Tool.SetCustomToolPlugin(plugin);
|
||
```
|
||
|
||
## 光标
|
||
|
||
使用 `Cursor.MarkupQuickPick`。Navisworks `Cursor` 枚举无平行四边形,此为最接近的选项。
|
||
|
||
## 与 PathClickToolPlugin 的关系
|
||
|
||
- 两者都是 `ToolPlugin`,同一时刻只有一个激活
|
||
- 互斥切换:取面时取点失活,取面结束切回取点
|
||
- 切换流程与项目中已有的「导航工具 ↔ ClickTool」切换模式一致
|
||
|
||
## 暂不实现
|
||
|
||
| 项目 | 原因 |
|
||
|---|---|
|
||
| 透视近大远小动态 δ 调整 | 先验证精度,不够再加 |
|
||
| 跨面距离检测 (maxDist) | 同构件判断已覆盖基本场景 |
|
||
| 曲面曲率分析 | 不同业务需求 |
|
||
| MouseMove 实时预览 | 保持轻量 |
|
||
|
||
## 下一步
|
||
|
||
1. 实现两个新文件
|
||
2. 在已知平面上验证法向量精度(误差应在 1° 以内)
|
||
3. 后续由调用方(ViewModel)按需集成激活/切换逻辑
|