- New "操作器" toggle: select a file in the tree and a 3-axis gizmo appears at its center -- XYZ arrows (TranslateManipulator) to move along an axis regardless of view angle, and rotation rings (RotateManipulator) to rotate any angle about an axis - Manipulators bind to a proxy ModelVisual3D; the proxy's transform is mirrored to all target BodyVisuals during drag (GPU-side, temp) - On release the accumulated transform is baked into B-rep via TransformRoot (deferred with Dispatcher.BeginInvoke to avoid reentrancy with the manipulator's own mouse-up handling); the gizmo re-centers on the new bounds - Temporary transforms are cleared with Transform3D.Identity, never null (HelixToolkit GetTransform crashes on null child.Transform) - Selecting another file retargets the gizmo; removing the target hides it - Docs updated (ARCHITECTURE Phase 10, CLAUDE.md, README) Known limitation: gizmo handles are occluded inside solid parts (WPF single depth buffer). Next: always-on-top overlay viewport. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
7.5 KiB
7.5 KiB
STPViewer — Claude 專案記憶
專案簡介
CAD 3D 檢視器(Windows 桌面 WPF, .NET 8):STEP/STL/DXF 匯入、STEP 裝配樹、 點/距離/邊/面/圓/角度/面距量測、兩點對齊(平移)、三點對齊(旋轉+平移)、軸向旋轉 90°、 拖曳模式(手形游標直接拖零件)、干涉檢查、剖面、mm⇄inch、CSV/截圖匯出。 詳細設計見 ARCHITECTURE.md。
技術棧
- .NET 8 WPF(
net8.0-windows)、MVVM(CommunityToolkit.Mvvm) - CADability(純 C# CAD kernel):STEP 匯入、B-rep 幾何、Face 三角化
- HelixToolkit.Wpf:3D viewport、相機、HitTest
常用指令
dotnet build STPViewer.sln
dotnet run --project src/STPViewer
dotnet publish src/STPViewer -c Release -o publish/STPViewer
測試模型:根目錄 test.stp(Amphenol RA PHD 座端,小檔)與 Amphenol PHD to PHD Cable 10201248 1.stp(39MB 大組件,效能測試用)
開發慣例
- MVVM:邏輯寫 ViewModel/Service,code-behind 只做純 View 事件(滑鼠拾取轉發)
- 一個 B-rep Face = 一個
GeometryModel3D,用Dictionary<Model3D, FaceInfo>反查(量測拾取靠這個,不可移除逐面結構) - 渲染雙模式(降 draw call):每個 leaf 同時持有
FacesContent(逐面,量測用)與MergedContent(整零件合併成 1 個GeometryModel3D,瀏覽用)。ApplyRenderMode()依狀態切BodyVisual.Content:瀏覽(None)且未剖面 → 合併網格;量測模式或剖面 → 逐面。 合併網格只代表「未剖切、目前位置」幾何;平移後呼叫RebuildMerged(leaf)同步。_faceMap/量測/剖面/干涉一律走FacesContent,與目前顯示哪種內容無關 - 合併網格的
BackMaterial:封閉實體(SolidCount≥1且HasBrep)不設(WPF 兩面渲染成本砍半);開放殼/STL 才設。逐面FacesContent一律保留 BackMaterial(剖切要看內部) - 量測值以 B-rep 為準(圓半徑、邊長、角度),面積/面距用網格近似
- 量測文字一律
Func<UnitSystem,string>延後產生(mm⇄inch 即時切換);內部數值永遠存 mm - 裝配樹節點(
ModelNodeViewModel)的可見性/邊線/顏色向下 cascade - 剖面只換
GeometryModel3D.Geometry(FaceInfo.Mesh保留原始 frozen mesh 供還原與量測) - 剛體變換(兩點對齊/旋轉 90°/三點對齊)統一走
TransformRoot(root, ModOp, Matrix3D):B-rep 用 ModOp 對 Solid/Shell 整體Modify(勿逐面位移,會重複位移共用邊),網格/合併網格/邊線/邊界同步重算;變換後量測已失效要ClearMeasurements()。 op 與 m 必須是同一個變換 — 數學在Services/RigidAlign.cs:WPFMatrix3D是「列向量」約定、CADabilityModOp是「行向量」約定,ToModOp負責轉置轉換,改動務必跑SmokeTest --align-test驗證兩種表示一致,否則 B-rep 與顯示網格會悄悄分家 - 拖曳模式(
MeasureMode.Drag):拖曳中只掛暫時TranslateTransform3D(GPU 免費),放開才一次性TranslateRoot烘進 B-rep — 不要改成拖曳中逐幀 TransformRoot(大檔每幀重建網格會卡死)。2D→3D 用 HelixUnProject(過錨點、法向=相機 LookDirection 的平面)。 合併網格的 hit-test 走_mergedMap(合併 Model → leaf);拖曳中邊線用_edgesSuspended暫停 - ⚠️
Visual3D.Transform永遠不要設成null,清除要用Transform3D.Identity。HelixToolkitViewport3DHelper.GetTransform對child.Transform沒做 null 檢查(GeneralTransform3DGroup.Children.Add(null)→ 拋「無法新增空值到集合中」), 之後任何FindHits都會 crash。v0.2.1 修的就是拖曳放開時把 BodyVisual.Transform 設 null(拖過一次後再點擊就炸)。 Gizmo 的暫時 Transform 清除同理一律用 Identity - Gizmo 操作器:Helix
TranslateManipulator/RotateManipulator×6Bind到代理ModelVisual3D, 代理 Transform 變更(DependencyPropertyDescriptor.AddValueChanged)即時套到目標 BodyVisual(暫時); 放開滑鼠才TransformRoot烘焙 — MouseUp 被 manipulator 標 handled,MainWindow 用AddHandler(..., handledEventsToo: true)才收得到。 烘焙用Dispatcher.BeginInvoke延後到 manipulator 自身事件處理完,避免 reentrancy;_gizmoBaking旗標防 Transform 歸零的回呼重入 - 干涉/面距/對齊等運算在背景執行緒;
Freeze()幾何後才跨執行緒 - 匯入在背景執行緒;
Freeze()幾何後才跨執行緒 - Commit 格式:Conventional Commits(
feat:/fix:/docs:…)
注意事項 / 已知限制
Path在 service 會與CADability.GeoObject.Path撞名 → 用IOPathalias- CADability 解析大 STEP 慢(39MB/64k 面實測:解析約 276 秒 + 幾何處理),不要改成同步呼叫。
解析(
ImportStep.Read)單執行緒無解;三角化/邊取樣已按 leaf 平行化(_leafWork收集 →Parallel.ForEach)。 平行粒度只能到 leaf:同 leaf 的面共用 Edge 物件,面級平行會 race。空 leaf 由Prune收掉(延後三角化可能全失敗)。 平行下GetTriangulation偶發失敗(實測 64k 面丟 ~8 面,跨 leaf 仍有共享狀態)→ 失敗面收進_retry,平行結束後循序重試補回, 該 leaf 的FinishLeaf也延到重試後才跑。不要移除重試機制,也不要把平行度開到面級 StepImportService.Progress回報匯入階段(解析/三角化耗時),UI 已接狀態列;訊息來自背景執行緒,要Dispatcher.BeginInvokeLinesVisual3D轉動視角逐幀重建,>30k 線段會卡 → 邊線自動關閉邏輯不要移除- 邊線採「一檔一條合併
LinesVisual3D」(掛在 rootEdgeVisual,由RefreshRootEdges收集各 leafOriginalEdgePoints重建)。 不要改回逐 leaf 一條 — 裝配樹零件多時,N 條線每幀重建會嚴重卡頓(實測主因)。leaf 只保留邊線「資料」,渲染統一在 root; 可見性/ShowEdges/剖面/平移變更時呼叫RefreshRootEdges(root)重組合併線 - 互動中暫停邊線:
Attach掛Camera.Changed→OnCameraMoved隱藏邊線、_interactionTimer(180ms) 停下後ResumeEdges顯示;_edgesSuspended為真時RefreshRootEdges不把線掛回。轉動/縮放/平移時不付邊線重建成本 - CADability
ImportStep對少數 AP242 檔案支援不完整;匯入失敗要 catch 顯示訊息,不可閃退 - IGES 無 reader;STL 無 B-rep(FaceInfo.BrepFace == null 的分支要保留)
- 不寫入原始檔(唯讀工具);WPF 限 Windows,不要嘗試移植 vbox/Linux
- 干涉檢查需剛好 2 個可見檔案(樹面板勾選);共面貼合(無穿透)不算干涉、gap≈0 視為配合(match)
- SmokeTest 工具:
--tree(裝配樹)、--clip-test(剖切數學)、--interference-test(干涉相交/分離/貼合)、--align-test(三點對齊剛體變換 + ModOp↔Matrix3D 一致性)、--make-dxf(產測試檔) - 絕不要用 PowerShell regex/Set-Content 改 .cs 檔 — Windows PowerShell 5.1 預設編碼會把 UTF-8 中文弄成亂碼(已踩過,靠反編譯 DLL 救回)。文字取代一律用 Edit 工具
secret/ 與 For_AI/
secret/:本機敏感資料集中地,secret/*gitignored(README.md、*.example除外)。 本專案無 runtime secret,僅有啟動腳本範本。For_AI/:AI 協作素材(截圖、草稿),整夾 gitignored。