- Don't ResetPermanentTransform (keeps path placement)
- Apply rotation around object center as incremental transform
- T(-center) * R * T(center) via Transform3D.Multiply
- Use existing OverridePermanentTransform pattern from PathAnimationManager
- Compute quaternion from face normal → ground direction
- Apply directly as Rotation3D with center-preserving translation
- Sync tracked state after application
- New button activates FaceInferToolPlugin to pick face on object
- Computes rotation from face normal to ground direction
- Decomposes to ZYX Euler angles, fills X/Y/Z fields
- User can preview and confirm as usual
- Remove ExecuteSelectAssemblyInstallationPointAsync
- Remove OnAssemblyInstallationMouseClicked
- Remove BeginAssemblyInstallationSelection
- Remove BuildAndRenderAssemblyInstallationReference
- Single-click '选安装面' is now the only way
- FaceInferToolPlugin now returns specific reasons: '未命中模型' or '表面过窄'
- ViewModel handlers show FailureReason instead of generic '未命中有效表面'
- Catch exceptions still pass through BuildFromFaceNormal error messages
- Replaces scattered UpdateMainStatus + ShowAutoClosingMessageBox calls
- Accepts Action<string> callback for ViewModel status bar update
- Applied to all end face and installation face failure handlers
- New Analyze(triangles, hitPoint, normal) bypasses 3-point seed plane construction
- New Analyze(modelItem, hitPoint, normal) for Navisworks types
- Same triangle extraction and centroid computation as 3-point version
- Enables single-click end face detection via FaceInferToolPlugin
- RowStyle TwoWay binding on IsSelected is unreliable with virtualizing DataGrid
- Switched to DataGrid.SelectionChanged event handler in code-behind
- Syncs RemovedItems/AddedItems to ViewModel.IsSelected reliably
- Single selection: SaveFileDialog with path name as default filename
- Multi selection: FolderBrowserDialog to pick output directory
- Export format: XML for multi (simpler UX), selectable for single
- DataGrid: SelectionMode Extended with RowStyle IsSelected binding
- PathRouteViewModel: add IsSelected property
- Export: iterates selected paths, saves each to separate file
- Ctrl/Shift+click to multi-select, then Export button batches them
- Also regenerate all PathPoint.Id to avoid UNIQUE constraint on PathPoints.Id
- Call RecalculateRoute() to rebuild edges with new point references
- Edge constructor auto-generates new Id, so edges are fresh too
- ParseRouteElement used route.AddPoint() which called RecalculateAndSaveRoute
- This saved the path to DB with original name before ImportPathCommand could rename it
- Changed to route.Points.Add() + route.RecalculateRoute() to skip DB save
- Only ImportPathCommand → AddRoute → SavePathToDatabase now persists renamed path
- Rename '导出选中路径' to '导出', move before '导出全部'
- Import dialog: enable MultiSelect, loop through all selected files
- Aggregate import results across files