Files
STPViewer/ARCHITECTURE.md
etwenandClaude Fable 5 45b27d51cb feat(ui): Add About dialog with user-facing changelog
- Help menu (Help > About STPViewer) opens a dialog styled after
  ETTerms AboutView: app card (name/version/description), developer
  card, tech stack card on the left; scrollable version history on
  the right, written for end users in plain language (v0.1.0-v0.7.0)
- Version read from assembly (single source: csproj <Version>)
- Note in CLAUDE.md: top-level WPF MenuItem with a Click handler does
  not fire (verified with real mouse input) - dialog entries must
  live in a submenu

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 09:17:06 +08:00

506 lines
33 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# STPViewer — Architecture
> STP/STEP 3D 檢視器:多檔匯入、圖層管理、點/線/面/圓量測(C# .NET 8 WPF 桌面程式)
---
## Overview
STPViewer 是一套 Windows 桌面 3D CAD 檢視工具,給硬體 / SI 工程師快速開啟機構件的
STEP`.stp` / `.step`)檔案做確認與量測,不需要安裝 SolidWorks / Creo 等重量級 CAD。
核心價值:
- **多檔匯入**:一次載入多個 CAD 檔(STEP / STL / DXF),每個檔案自成一棵樹
- **裝配樹**STEP product structure 還原成樹狀節點(組件→零件),逐節點 顯示/隱藏、換色、Zoom-to;
名稱搜尋過濾、隔離顯示(只顯示此節點/反轉/全顯)、tooltip 外形尺寸
- **量測**:點座標、兩點距離、邊長、面(面積/類型/法向量)、圓(心/半徑/直徑/周長)、
兩面/兩邊夾角、面到面最短距離、體積/質心;吸附含**圓心**(量孔對孔 pitch);
快速鍵 P/D/E/F/C/A/M + Esc;單位 mm ⇄ inch 即時切換
- **剖面**X/Y/Z 軸向 + **3點任意平面**,位置滑桿/數值輸入 + 反向,CPU 網格裁切(量測不受影響)
- **視圖**:標準視圖(等角/前/上/右)+ 正交⇄透視投影
- **匯出**:量測結果 CSV、3D 視圖 PNG 截圖(2x)、**目前對齊位置寫成新 STEP 檔**、
**STL 網格檔**(參數對話框:合併/每檔、binary/ASCII、mm/inch/自訂縮放、精度,確認再匯出)
- 單機離線執行,無網路相依;視窗/單位/最近檔案自動保存(settings.json
使用者:單一桌面使用者(無登入 / 角色系統)。
---
## Tech Stack
| Layer | Technology | 說明 |
|---|---|---|
| Runtime | .NET 8`net8.0-windows` | 桌面 WPF |
| UI Framework | WPF + MVVMCommunityToolkit.Mvvm | |
| 3D 渲染 / 拾取 | **HelixToolkit.Wpf** | Viewport3D 封裝、相機操作、HitTest |
| STEP 解析 / 幾何核心 | **CADability**(純 C#netstandard2.0 | STEP B-rep 匯入、Face 三角化、邊/面幾何查詢 |
| 量測幾何運算 | CADability(B-rep 精確值)+ 網格近似(面積) | |
| 打包 | `dotnet publish -c Release` | 免安裝、單資料夾 |
> 技術選型備註:OpenCASCADE 的 .NET wrapperMacad.Occt 等)不在 NuGet 上,
> 自建 C++/CLI wrapper 成本過高;CADability 是純 C# 的 CAD kernelMIT),
> 內建 STEP reader 與三角化,與 .NET 8 相容(netstandard2.0),故採用。
---
## Architecture Diagram
```
┌────────────────────────── MainWindow (WPF) ──────────────────────────┐
│ Toolbar(匯入/量測模式) LayerPanel HelixViewport3D 量測結果面板 │
└──────────────┬───────────────┬───────────────┬───────────────────────┘
│ ICommand │ binding │ MouseDown(HitTest)
▼ ▼ ▼
┌──────────────────────── MainViewModel (MVVM) ────────────────────────┐
│ Layers: ObservableCollection<LayerItemViewModel> │
│ Measurements: ObservableCollection<MeasurementResult> │
│ CurrentMode: None/Point/Distance/Edge/Face/Circle │
└───────┬──────────────────────────────┬───────────────────────────────┘
▼ ▼
┌─ StepImportService ─────────┐ ┌─ MeasurementService ───────────────┐
│ CADability ImportStep.Read │ │ Hit → GeometryModel3D → FaceInfo │
│ Solid→Shell→Face 三角化 │ │ 點: 頂點吸附 / 表面點 │
│ Edge 取樣折線(輪廓線) │ │ 邊: 最近 Edge → Line/Ellipse 判型 │
│ → MeshGeometry3D + FaceInfo │ │ 面: 面積(網格Σ)+Surface 類型 │
└─────────────────────────────┘ │ 圓: 圓形 Edge → 心/半徑/直徑/周長 │
└────────────────────────────────────┘
```
資料流:`*.stp → CADability B-rep → 三角網格(渲染) + B-rep 參照(量測) → Helix Viewport`
每個 Face 對應一個 `GeometryModel3D``FacesContent`,剖面模式渲染用),並登錄到 `_faceMap` 反查 B-rep Face / Edge。
效能:非剖面(瀏覽+量測)渲染**整零件合併網格**(`MergedContent`,每檔 1 個 model),量測 HitTest 改打合併網格、
用命中三角形的頂點 index 經 `_mergedFaceRanges` 二分搜尋(`ResolveMergedFace`)反查回是哪個面 —
量測模式不再掛數萬個逐面 model,大組件量測下轉動視角與瀏覽同樣流暢(v0.3.2)。
---
## Project Structure
```
STPViewer/
├── CLAUDE.md # 專案記憶 & 給 Claude 的指令
├── README.md # 快速上手
├── ARCHITECTURE.md # 本文件
├── .gitignore # For_AI/ + *.stp/*.step(客戶料號)規則
├── docs/
│ ├── decisions/ # 技術決策紀錄(ADR)
│ └── runbooks/ # 操作手冊
├── For_AI/ # 🚫 gitignored — AI 協作素材 + 測試模型 *.stp(客戶料號不入 git)
├── STPViewer.sln
└── src/
└── STPViewer/
├── STPViewer.csproj # net8.0-windows, UseWPF
├── App.xaml / App.xaml.cs # 全域例外處理(log + 訊息框存活;%LOCALAPPDATA%\STPViewer\error.log
├── MainWindow.xaml / .cs # 版面 + 滑鼠拾取事件
├── Models/
│ ├── FaceInfo.cs # GeometryModel3D ↔ B-rep Face 對照(STL 為 null
│ ├── MeasureMode.cs # enum: None/Point/Distance/Edge/Face/Circle/Angle/FaceDistance/Align/Align3/Drag/Interference/Volume
│ ├── MeasurementResult.cs # 量測結果(雙單位 lambda、3D 標籤同步)
│ └── UnitSystem.cs # mm/inch + Units 格式化
├── StlExportDialog.xaml / .cs # STL 匯出參數對話框(範圍/格式/單位/精度 + 預估,確認再匯出)
├── AboutDialog.xaml / .cs # 關於視窗(程式/開發者/技術棧 + 使用者版 changelog
├── Services/
│ ├── StepImportService.cs # STEP/STL/DXF 讀檔 + 三角化 + 裝配樹
│ ├── MeasurementService.cs # 點/線/面/圓/角度/面距/體積質心 幾何計算(吸附含圓心)
│ ├── InterferenceService.cs # 干涉檢查:三角形-三角形相交(區間法)+均勻網格加速;無干涉時近似最小間隙
│ ├── RigidAlign.cs # 三點對齊/旋轉的剛體變換數學(Matrix3D列向量 ↔ ModOp行向量 轉換)
│ ├── SectionService.cs # 剖面:網格/線段半空間裁切
│ ├── SettingsService.cs # 使用者設定 settings.json(視窗/單位/MRU%LOCALAPPDATA%\STPViewer
│ └── StlExportService.cs # STL 匯出:binary/ASCII 寫檔 + B-rep 重新三角化(精細)
└── ViewModels/ # MainViewModel 為 partial class,依職責分檔
├── MainViewModel.cs # 核心:匯入、裝配樹、量測、剛體變換、邊線、單位
├── MainViewModel.Drag.cs # 拖曳模式
├── MainViewModel.Gizmo.cs # 三軸操作器 + 疊圖層相機
├── MainViewModel.Section.cs # 剖面(v0.4.0 起背景平行裁切)
├── MainViewModel.Interference.cs # 干涉檢查指令
├── MainViewModel.Export.cs # CSV / 截圖 / STEP / STL 匯出
├── StlExportViewModel.cs # STL 匯出對話框 VM(選項 + 即時摘要)
└── ModelNodeViewModel.cs # 裝配樹節點(可見性/顏色 cascade)
```
---
## Data Models
桌面程式無資料庫;核心為記憶體內模型:
```csharp
// 裝配樹節點:root = 匯入檔、group = STEP 裝配、leaf = 零件幾何(可見性/顏色向下 cascade)
class ModelNodeViewModel
{
string Name; // root = 檔名;group/leaf = STEP product 名
string? FilePath; // root 才有值
bool IsVisible; // 切換 viewport 中的 ModelVisual3Dcascade
bool IsFilterVisible; // 樹搜尋過濾結果(只影響樹面板顯示)
Color Color; // 節點色(換色只改共用 Brush)
int SolidCount, FaceCount, TriangleCount;
ModelVisual3D BodyVisual; // leaf:面網格容器(合併/逐面兩種內容切換)
LinesVisual3D EdgeVisual; // root:整檔合併輪廓線
Rect3D Bounds; // Zoom-to / 外形尺寸 / 旋轉中心
}
// HitTest 反查:渲染物件 → B-rep
class FaceInfo
{
Face? BrepFace; // CADability Face(量測用 B-repSTL 為 null
ModelNodeViewModel Owner;
MeshGeometry3D Mesh; // 面積近似 / 頂點吸附
}
enum MeasureMode { None, Point, Distance, Edge, Face, Circle,
Angle, FaceDistance, Align, Align3, Drag, Interference, Volume }
class MeasurementResult
{
MeasureMode Kind;
string Title; // "P1 (12.30, 4.50, 0.00)"
string Detail; // 多行明細(Δ、半徑、面積…)
List<Visual3D> Overlays; // 視圖中的標記(刪除量測時一併移除)
}
```
單位:STEP 內部以 mm 為準(CADability 匯入時依檔內單位換算),UI 顯示 mm。
---
## Key Features
| 功能 | 操作 | 輸出 |
|---|---|---|
| 匯入 STP | 工具列「匯入」(可複選) / 拖放檔案 | 新圖層 + 自動 ZoomExtents |
| 圖層 | 面板勾選顯示、換色、Zoom-to、移除 | 即時反映於 3D 視圖 |
| 量測-點 | 模式「點」+ 點擊模型 | 座標(優先吸附頂點/邊端點) |
| 量測-距離 | 模式「距離」+ 點兩下 | 直線距離 + ΔX/ΔY/ΔZ + 視圖連線 |
| 量測-邊 | 模式「邊」+ 點擊邊附近 | 線段長/曲線長;圓弧附半徑 |
| 量測-面 | 模式「面」+ 點擊面 | 面積、曲面類型、平面法向量/圓柱半徑 |
| 量測-圓 | 模式「圓」+ 點擊圓孔邊緣 | 圓心、半徑、直徑、周長 + 視圖圓心標記 |
| 量測-角度 | 模式「∠」+ 點兩個面(或靠近直線邊) | 夾角 + 補角(面取法向量、邊取方向) |
| 量測-面距 | 模式「⇔」+ 點兩個面 | 面到面最短距離(網格近似)+ 最近點對連線 |
| 對齊 | 模式「對齊」+ 點「要移動零件」上一點、再點目標點 | 純平移該檔案使點1貼到點2(B-rep 用 `ModOp` 整體位移,量測清空) |
| 三點對齊 | 模式「三點」+ 來源檔 3 點、目標檔 3 對應點 | 旋轉+平移剛體變換(點1精確貼合、1→2 方向對齊、三點平面對齊;`RigidAlign` |
| 旋轉 | 樹面板選檔案 + 工具列 ↻X/↻Y/↻Z | 繞檔案中心 +90°(連按累加;方向不合時先轉正再對齊) |
| 拖曳 | 模式「🖐 拖曳」(手形游標)+ 左鍵按住零件拖 | 沿螢幕平面移動該檔案,放開烘進 B-rep(拖曳中僅暫時 Transform,不卡) |
| 操作器 | 「⊹ 操作器」+ 樹面板選檔案 | XYZ 箭頭沿軸移動、旋轉環繞軸轉任意角度(與視角無關);每次放開烘進 B-rep |
| 干涉 | 工具列「🧩 干涉」(≥2 個可見檔案,兩兩配對檢查) | 每組配對:相交→紅色交線+相交三角形對數;無相交→最小間隙 gap(≈0 即配合 match |
| 剖面 | 工具列「✂ 剖面」+ 軸向(X/Y/Z/**3點任意平面**)/位置(滑桿+數值輸入)/反向 | CPU 裁切渲染網格(原始幾何保留,量測仍精確);3點=在模型上點 3 點定義平面 |
| 單位 | 工具列 mm ⇄ in(隨設定保存) | 既有量測(清單+3D 標籤)即時換算 |
| 匯出 | 💾 CSV / 📷 截圖 / 📤 STEP / 📐 STL | UTF-8 BOM CSV2x PNG;可見檔案目前位置寫成**新** STEP 檔(對齊結果交接 CAD);STL 先開參數對話框(範圍/格式/單位/精度 + 三角形數與大小預估)確認再匯出 |
| 視圖 | 滑鼠右鍵旋轉/滾輪縮放/中鍵平移(Helix 預設)、ViewCube、工具列 等角/前/上/右、正交⇄透視 | |
| 快速鍵 | P 點 / D 距離 / E 邊 / F 面 / C 圓 / A 角度 / M 面距;同鍵再按=退出;Esc=取消進行中量測→退出模式 | |
| 樹操作 | 搜尋框過濾節點名稱;右鍵:只顯示此節點 / 反轉顯示 / 全部顯示 / 量體積質心 | tooltip 含外形尺寸 L×W×H |
| 體積/質心 | 樹節點右鍵「⚖ 量體積/質心」 | signed volume(封閉實體)+ 質心標記 + AABB 外形尺寸 |
| 設定/MRU | 自動記住視窗位置大小、單位;「▾」最近 10 個檔案 | %LOCALAPPDATA%\STPViewer\settings.json |
支援格式:`.stp` / `.step`B-rep + 裝配樹)、`.stl`(純網格,僅點/距離/角度/面距量測)、
`.dxf`(線架構檢視)。**IGES 不支援**CADability 無 IGES reader)。
---
## Key Constraints & Business Rules
1. 每個匯入檔案 = 一個圖層;同檔重複匯入產生新圖層(後綴 `(2)`
2. 量測一律以 **B-rep 幾何** 為準(邊長、圓半徑);僅「面積」用網格加總近似(三角化精度內)
3. 隱藏圖層不參與 HitTest(量測不會打到看不見的東西)
4. 移除圖層時,其上的量測 overlay 一併清除
5. 三角化精度依物件尺寸自適應(對角線 × 0.0015,限 0.02–0.5 mm);大型組件匯入走背景執行緒,UI 不凍結
6. 不寫入/修改原始 STP 檔(唯讀檢視器)
---
## Security Considerations
- 純離線桌面工具,無網路、無帳號、無資料庫、無任何 runtime / compile-time secret(故未建 `secret/` 資料夾)
- `For_AI/` 收 AI 協作素材(截圖、筆記)+ 測試模型 `*.stp`(客戶料號),整夾 gitignore
- `*.stp` / `*.step` 全域 gitignore:客戶 CAD 料號檔不入 git,避免外流
---
## Build & Setup Steps
```bash
cd E:/10_AI/STPViewer
# 還原 + 建置
dotnet build STPViewer.sln -c Debug
# 執行
dotnet run --project src/STPViewer
# 發佈(免安裝資料夾)
dotnet publish src/STPViewer -c Release -o publish/STPViewer
```
NuGet 相依(自動還原):`CADability``HelixToolkit.Wpf``CommunityToolkit.Mvvm`
---
## Development Phases
### Phase 1 — 專案骨架 + 3D 視窗(工作量:S)
**目標:** 專案能跑起來,出現含 Helix 3D viewport 的主視窗
**包含:**
- [x] `STPViewer.sln` + `src/STPViewer/STPViewer.csproj`net8.0-windows、UseWPF、NuGet 三件套)
- [x] `MainWindow.xaml`:工具列 / 左側圖層面板 / 中央 `HelixViewport3D`(含 ViewCube、預設光源)/ 右側量測面板 / 底部狀態列
- [x] `MainViewModel.cs` 空殼 + DataContext 接線
**驗收條件:** `dotnet run` 開出主視窗,3D 區可旋轉縮放(空場景 + 格線)
### Phase 2 — STEP 匯入與渲染(工作量:L)
**目標:** 可開啟單一 STP 並看到實體模型
**包含:**
- [x] `StepImportService.cs`CADability `ImportStep` 讀檔 → Solid/Shell/Face 三角化 → `MeshGeometry3D`
- [x] Face→`GeometryModel3D` 一對一、建 `FaceInfo` 對照字典
- [x] Edge 取樣折線 → `LinesVisual3D` 輪廓線(CAD 外觀)
- [x] 匯入走 `Task.Run`,完成後 UI 執行緒組 Visual + `ZoomExtents`
- [x] 用根目錄 Amphenol STP 驗證
**驗收條件:** 匯入 Amphenol STP 顯示正確 3D 模型(含輪廓線),視角操作流暢
### Phase 3 — 圖層系統(工作量:M)
**目標:** 多檔匯入、各自成層、可管理
**包含:**
- [x] 「匯入」支援複選 + 檔案拖放
- [x] `LayerItemViewModel.cs`:名稱、可見性 checkbox、色塊(調色盤換色)、統計(Solid/Face/三角形數)
- [x] 圖層操作:顯示/隱藏(含 HitTest 排除)、Zoom-to、移除(連帶清 overlay
**驗收條件:** 匯入 2+ 個 STP,逐層開關/換色/移除皆即時生效
### Phase 4 — 量測功能(工作量:L)
**目標:** 點 / 距離 / 邊 / 面 / 圓 五種量測可用
**包含:**
- [x] `MeasurementService.cs` + `MeasureMode` 工具列切換(互斥 toggle
- [x] 點:HitTest 命中點 + 頂點/邊端點吸附;距離:兩點 + ΔXYZ + 視圖連線
- [x] 邊:命中面最近 Edge`Line`→長度、`Ellipse(IsCircle)`→弧長+半徑、其他→曲線長
- [x] 面:網格面積加總 + Surface 類型(平面法向量 / 圓柱半徑)
- [x] 圓:搜尋最近圓形 Edge → 圓心/半徑/直徑/周長 + 圓心標記
- [x] 量測結果面板:清單 + 單筆刪除 + 全部清除(overlay 同步移除)
**驗收條件:** 對 Amphenol STP 可量出 pin 孔圓徑、殼體面積、兩點距離,數值合理
### Phase 5 — 整合收尾(工作量:S)
**目標:** 穩定可交付
**包含:**
- [x] 錯誤處理(壞檔/非 STEP → 訊息列提示不閃退)、匯入進度提示
- [x] 狀態列模式提示(「點選第 2 點…」)
- [x] `README.md``CLAUDE.md` 完稿
- [x] `dotnet publish -c Release` 驗證 + smoke test(無 UI 載檔驗證管線)
**驗收條件:** Release 發佈資料夾雙擊可用;載入壞檔不閃退
---
## Development Phases — 第二輪(Future Extensions 實作,全部完成)
### Phase 6 — Future Extensions(工作量:L
- [x] 剖面(Section plane)檢視 — `SectionService` CPU 網格/線段裁切 + 軸向/位置/反向控制 + 半透明剖面指示
- [x] 角度量測(兩面/兩邊夾角,含補角)、面到面最短距離(頂點→三角形雙向,網格近似)
- [x] 量測結果匯出 CSVUTF-8 BOM/ 視圖 PNG 截圖(2x
- [x] 裝配樹(STEP `HierarchyToBlocks` product structure)取代「一檔一層」,節點層級 顯示/換色/Zoom
- [x] STL / DXF 格式支援(IGES 落空:CADability 無 IGES reader,誠實不支援)
- [x] 量測單位切換 mm ⇄ inch(清單與 3D 標籤即時換算,內部一律存 mm)
**驗收:** ClipTest 裁切數學 5 項全過;STEP/STL/DXF 三格式 smoke test 通過;UI 端到端存活
---
## Development Phases — 第三輪(裝配驗證,全部完成)
### Phase 7 — 配合 / 干涉驗證(工作量:M)
- [x] 兩點對齊(`Align`)— 點「要移動零件」一點 + 目標點 → 純平移整個檔案使其貼合;
B-rep 用 `CADability.ModOp.Translate` 對 Solid/Shell 整體 `Modify`,網格/邊線/邊界同步重建,量測清空
- [x] 干涉檢查(`InterferenceService`)— 三角形-三角形相交(區間法回傳交線段)+ 均勻網格空間加速;
相交→紅色交線 overlay;無相交→近似最小間隙 gap(共面貼合不算穿透)
- [x] SmokeTest `--interference-test`:相交 / 分離(gap≈20) / 貼合(gap≈0) 三情境數學驗證
**驗收:** InterferenceTest 3 情境全過;兩件 STEP 勾選後可判定干涉或回報配合間隙
### Phase 8 — 旋轉對齊(工作量:M)
- [x] 軸向旋轉 — 樹面板選檔案 + 工具列 ↻X/↻Y/↻Z,繞檔案 Bounds 中心 +90°(方向不合先轉正)
- [x] 三點對齊(`Align3`)— 來源檔 3 特徵點 → 目標檔 3 對應點,解旋轉+平移剛體變換一次貼合
- [x] `RigidAlign` 數學服務 — `TryRigidTransform`(座標架法)、`ToModOp`WPF Matrix3D 列向量 ↔ CADability ModOp 行向量轉置)
- [x] 通用 `TransformRoot`(取代平移專用路徑):B-rep ModOp Modify + 網格/合併網格/邊線重算 + `RecomputeBounds`
- [x] SmokeTest `--align-test`:已知變換還原(誤差 ~1e-15)、ModOp↔Matrix3D 一致、共線拒絕
**驗收:** AlignTest 8 項全過;公母連接器可旋轉擺正後三點對齊插合,再用干涉檢查驗證配合
### Phase 9 — 拖曳模式(工作量:M)
- [x] 🖐 拖曳模式(`MeasureMode.Drag`)— 手形游標,左鍵按住零件沿螢幕平面拖動(Helix `UnProject` 投影到過錨點、面向相機的平面)
- [x] 拖曳中僅掛暫時 `TranslateTransform3D`(GPU 端,大組件不卡),放開一次性 `TranslateRoot` 烘進 B-rep(量測精度不受影響)
- [x] `_mergedMap`(合併網格 → leaf 反查)支援瀏覽渲染下的 hit-test;拖曳中邊線暫停
- [x] 滑鼠捕捉(拖出視窗持續)、意外中斷以當前位置定格
**驗收:** 大組件手形拖曳流暢;放開後干涉/量測結果與拖曳位置一致;右鍵視角操作不受影響
> v0.2.1 修正:拖曳放開時清除暫時位移誤用 `Transform = null`,導致下次 `FindHits` 命中該檔案時
> HelixToolkit `GetTransform` 對 null transform `Add` 而 crash。改用 `Transform3D.Identity`。
### Phase 10 — Gizmo 三軸操作器(工作量:M)
- [x] 「⊹ 操作器」toggle — 對樹面板選取的檔案顯示 Helix `TranslateManipulator` ×3XYZ 紅綠藍箭頭)+ `RotateManipulator` ×3(旋轉環),尺寸隨檔案 Bounds 自適應
- [x] 操作器綁定代理 `ModelVisual3D`,拖動中代理 Transform 即時套到目標所有 BodyVisual(暫時、GPU 端);邊線暫停
- [x] 放開滑鼠(`handledEventsToo` 捕捉)→ `Dispatcher.BeginInvoke` 延後 `TransformRoot` 烘進 B-rep、操作器歸零並移到新中心
- [x] 換樹選取自動換目標;目標被移除自動收掉;暫時 Transform 清除用 `Identity`(非 null
**驗收:** 拖 X 箭頭零件僅沿世界 X 移動(與視角無關);拖環任意角度旋轉;放開後量測/干涉與顯示位置一致
### Phase 11 — Gizmo always-on-top 疊圖層(工作量:M
- [x] 操作器移到獨立透明 `Viewport3D``gizmoOverlay`)疊在主視窗上 → 不在主場景,**永不被實體遮擋、永遠可抓**
- [x] `_overlayCamera` 在主相機 `Changed` 時同步(Position/方向/FOV/near-far);同尺寸 → 投影一致、操作器精準貼合
- [x] raw Viewport3D 空白處不吃滑鼠 → 穿透回主視窗(orbit/縮放/量測不受影響);`IsHitTestVisible``GizmoEnabled`
- [x] 放開事件改掛 overlay`handledEventsToo`);`_gizmoBakePending` 防同次重複烘焙
**驗收:** 操作器從任何視角都浮在零件上可見可抓;開啟操作器時右鍵 orbit / 量測仍正常(穿透)
---
## Development Phases — 第四輪(效能,全部完成)
### Phase 12 — 大組件量測流暢度(工作量:M)
**問題:** 大檔(39MB / 64k 面)瀏覽轉動順,但一進量測模式轉動就嚴重卡頓。
**根因:** 量測模式渲染逐面(每個 B-rep 面 1 個 `GeometryModel3D`),64k 面 = 64k draw call
WPF retained-mode 每幀重走 visual tree → frame rate 崩。瀏覽模式因渲染合併網格(每檔 1 個 model)所以順。
- [x] 量測拾取改打**合併網格** + 三角形頂點 index 反查面(`_mergedFaceRanges` + `ResolveMergedFace` 二分搜尋)
- [x] `ApplyRenderMode()` 改為:非剖面(瀏覽+量測)→ 合併網格;剖面 → 逐面。量測模式不再掛數萬個逐面 model
- [x] `BuildMergedMesh` 依面序串接(無焊接共用頂點),匯入時同步記錄每面頂點起始邊界 + `FaceInfo`
- [x] 相機暫停事件改訂 `HelixViewport3D.CameraChanged`(控制項層級)+ `Camera.Changed` 保底,避免相機實例被換掉時訂閱孤兒化
- [x] 驗證:WPF 3D hit-test 純幾何不剔背面 → 打單面合併網格仍命中孔內壁;圓/面/邊/角度/面距/剖面量測數值與逐面一致
**驗收:** 大組件量測模式下轉動視角與瀏覽同樣流暢;圓孔/面/邊/角度/面距量測精度不變;剖面模式量測仍正常
---
## Development Phases — 第五輪(效能 + 穩定性 v0.4.0,全部完成)
### Phase 13 — 效能 + 穩定性(工作量:M)
**效能:**
- [x] 剖面裁切背景化 + 平行化 — `ApplySection` 改 asyncUI 執行緒快照 (model, frozen mesh)、
`Task.Run` + `Parallel.For` 全場景裁切、回 UI 一次換上。`_sectionApplying`/`_sectionReapply` guard
裁切期間的新變更(拉滑桿/換軸/零件平移)→ 完成後用最新參數重跑一輪,最終狀態必為最新。
大檔(64k 面)拉剖面滑桿不再凍結 UI
- [x] `TransformRoot` 網格變換面級平行化 — 來源/輸出 mesh 皆 frozen、各面獨立所以安全;
B-rep `Modify` 與視覺樹賦值維持循序(CADability 非執行緒安全 / WPF 執行緒親和)。
大組件拖曳/操作器放開的烘焙時間大減
- [x] 干涉間隙精修加速 — `ApproxMinDistance` 精修改為 AABB 平方距離下界快速拒絕 + `Parallel.For`
分段掃描(thread-local bestlock 只在合併時),大檔全三角形掃描成本大減
- [x] 合併網格邏輯去重複 — `BuildMergedMesh`/`RebuildMerged` 共用 `MergeMeshes`(面序不變,
`_mergedFaceRanges` 頂點邊界維持有效);`MeasurementService.ClosestPointOnTriangle`
改用 `InterferenceService` 的同一實作
**穩定性:**
- [x] 全域例外處理(App.xaml.cs)— `DispatcherUnhandledException`(記 log + 訊息框 + Handled 讓程式存活)、
`TaskScheduler.UnobservedTaskException`SetObserved)、`AppDomain.UnhandledException`(致命前留記錄);
log 位於 `%LOCALAPPDATA%\STPViewer\error.log`
- [x] `IsBusy` 指令防護 — 匯入 / 旋轉 90° / 干涉檢查在背景運算期間停用(`CanExecute`),
避免干涉檢查中零件被轉走造成結果錯位;`ImportFilesAsync` 整批維持 busy、
重入(拖放/命令列)直接擋掉(StepImportService 有共享狀態、非重入安全)
- [x] `MainViewModel`(原 1,476 行)拆 partial class — Drag / Gizmo / Section / Interference / Export 各自成檔
**驗收:** ClipTest 5 項、AlignTest 8 項、InterferenceTest 3 情境全過;test.stp 匯入 smoke test 通過;建置 0 警告
---
## Development Phases — 第六輪(Future Extensions 實作 v0.5.0,全部完成)
### Phase 14 — 功能擴充(工作量:L)
**量測/操作:**
- [x] 圓心吸附 — `Snap` 點到圓形邊(容差內)吸附圓心,與頂點比誰離命中點近;距離模式可直接量兩孔 pitch
- [x] Esc 取消 — 先清進行中的多段量測(保留模式)→ 退回瀏覽 → 關操作器;量測快速鍵 P/D/E/F/C/A/M
(同鍵再按=退出;焦點在輸入框時不攔截)
- [x] 體積/質心 — `MeasurementService.MeshVolume` signed volume + 加權質心;有向體積抵銷比例過高
(開放殼)判為不可靠並拒算;樹右鍵「⚖ 量體積/質心」→ 量測結果 + 質心標記
**視圖/UI**
- [x] 標準視圖(等角/前/上/右,Z 向上機構慣例)+ 正交⇄透視(`HelixViewport3D.Orthographic`);
gizmo 疊圖層相機「型別」跟隨主相機切換(透視/正交投影一致,操作器不錯位)
- [x] GridSplitter ×2 — 樹面板/量測面板可調寬(MinWidth 防拖到消失)
- [x] 節點外形尺寸 — `Bounds` setter 通知 `Stats`/`ToolTipText`tooltip 顯示 L×W×H(變換後自動更新)
- [x] 樹搜尋/過濾 — 名稱過濾(符合者+祖先+子樹顯示、自動展開);只影響樹面板,不影響 3D;新匯入檔套用現行過濾
- [x] 隔離顯示 — 樹右鍵:只顯示此節點/反轉顯示/全部顯示;`SetVisibleRecursive` 顯式遞迴
(不能只設 rootsetter 同值不觸發 cascade,會留下混合狀態)
- [x] 記住設定 + MRU — `SettingsService`settings.json):視窗位置大小(含虛擬桌面範圍檢查)、
mm/inch、最近 10 檔;工具列「▾」下拉
**檔案/剖面:**
- [x] 干涉 ≥2 可見檔 — 兩兩配對逐組檢查(網格先快照),每組一筆結果,狀態列總結相交組數
- [x] 匯出 STEP — `CADability.ExportStep.WriteToFile` + `Project.CreateSimpleProject`;可見檔案的
Solid/Shell(目前對齊位置)寫成新檔;SmokeTest `--export-test` 往返驗證(8 實體出→回讀 8 實體一致)
- [x] 3點任意剖面 — 軸向下拉加「3點」:模型上點 3 點定義平面(共線防呆、Esc 取消、換軸重置);
平面位置改為「場景 AABB 8 角投影到法向」內插(軸向=舊行為、任意法向也成立);位置加數值輸入框(0–100 防呆)
**驗收:** ClipTest / AlignTest / InterferenceTest / ExportTest 全過;test.stp 匯入正常;建置 0 警告
---
## Development Phases — 第七輪(STL 匯出 v0.6.0,全部完成)
### Phase 15 — STL 匯出(工作量:M)
- [x] `StlExportService` — binary80B header + placeholder 補三角形數)/ ASCII 寫檔,
退化(零面積)三角形濾除、逐三角形自算法向量;`Tessellate` 由 B-rep 重新三角化
- [x] 參數對話框(`StlExportDialog` + `StlExportViewModel`)— 範圍(合併單檔/每檔一個)、
格式(binary/ASCII)、單位縮放(mm / inch ÷25.4 / 自訂)、網格精度(目前網格/精細)、
即時摘要(檔案數/三角形數/預估大小),**確認才選路徑匯出**
- [x] 背景執行緒寫檔 — UI 先快照 frozen mesh`GeometryModel3D` 是 DispatcherObject
不可跨執行緒,v0.6.0 修正),背景只碰 frozen `MeshGeometry3D` + CADability B-rep
- [x] SmokeTest `--stl-export-test [file.stp]`binary 往返(含 inch 縮放)、ASCII 結構、
退化三角形濾除、真檔精細重算三角形數 > 匯入預設
**CADability 三角化兩個實測特性(設計依據):**
1. `GetTriangulation` 對「比快取粗」的精度要求直接回傳既有較細快取 → 「較粗」選項無效,誠實不提供
2. 三角形數對精度**非單調**(0.4× 重算反而比 1× 少 — 重算網格較有效率),
要明顯更細需 ≤0.15×(test.stp1× = 30,784 → 0.15× = 35,228)→ 精細係數定 0.15
**驗收:** StlExportTest 5 項全過;既有 ClipTest/AlignTest/InterferenceTest 回歸全過;建置 0 警告
---
## Development Phases — 第八輪(UI 重整 v0.7.0
### Phase 16 — 選單列 + 可自訂快速列(工作量:M)
**問題:** 功能累積後單一工具列 30+ 顆按鈕擠在一排,視窗不夠寬就溢出到「▾」,難找難按。
- [x] **選單列**(Menu):全部功能分類下拉 — 檔案(匯入/MRU/四種匯出)、視圖(全覽/標準視圖/正交/單位)、
量測(7 種模式 checkable + 快速鍵提示 + 清除)、變換(對齊/三點/拖曳/操作器/旋轉XYZ)、
工具(干涉/剖面)、快速列(自訂按鈕勾選 + 恢復預設)
- [x] **快速工具列**`QuickBarViewModel`):只顯示使用者勾選的常用按鈕;
註冊表定義全部按鈕(key/選單名/預設值),XAML 以索引子綁定
`Visibility="{Binding QuickBar[Key].IsChecked, Converter=BoolToVis}"`(按鈕本體與綁定保持靜態)
- [x] 預設常用:匯入、最近、全覽、等角、正交、點、距離、圓、角度、干涉、剖面、清除、mm⇄in;
其餘(邊/面/面距/對齊/三點/旋轉/拖曳/操作器/四種匯出/前上右視圖)預設收進選單
- [x] 勾選清單隨 settings.json`QuickBarKeys`)保存;null/新按鈕用註冊表預設值
- [x] **剖面參數列**獨立成第三列,只在 `SectionEnabled` 時顯示(含「✕ 關閉」);
平常整條隱藏,不再常駐佔位
- [x] **關於視窗**`AboutDialog`,選單「說明 → 關於 STPViewer」)— 程式資訊/開發者/技術棧 +
給一般使用者看的版本紀錄(樣式參考 ETTerms AboutView);發新版在 Changelog 陣列頂端加一筆
**驗收:** 選單所有功能可用(checkable 與工具列 toggle 同步);快速列勾選即時增減、重開保留;
既有快速鍵 P/D/E/F/C/A/M + Esc 不受影響
---
## Future Extensions(下一輪)
- **大檔重開快取** — 匯入成功後把三角網格 + 裝配樹序列化成 sidecar 快取檔(以來源檔 hash 驗證),
重開同檔免等 CADability 解析(39MB 實測 276 秒)。取捨:B-rep 無法快取 → 量測退化成網格模式
或背景補載;是否值得取決於「重複開同一大檔」的頻率(v0.5.0 決議暫緩)
### 既有清單(維持)
- 剖切面封口(cap)填實(目前剖開處可見內部背面材質;需對裁切輪廓三角化補面,難度最高,建議最後做)
- 樹節點三態 checkbox(部分子節點隱藏時顯示中間態)
- IGES 支援(需引入其他幾何核心或自寫 reader)
- 量測結果匯出含截圖的 PDF 報告
- 兩邊最短距離、邊到面距離
- 視圖狀態(相機、圖層、量測)存檔/還原