Initial commit
This commit is contained in:
959
docs/MapEditor/V2-DangNV/LAYOUTEDITOR_IMPLEMENTATION.md
Normal file
959
docs/MapEditor/V2-DangNV/LAYOUTEDITOR_IMPLEMENTATION.md
Normal file
@@ -0,0 +1,959 @@
|
||||
# LayoutEditor Implementation - Development Log
|
||||
_Last Updated: 2024-12-03_
|
||||
|
||||
## 📋 Overview
|
||||
|
||||
Tài liệu này ghi lại quá trình xây dựng **LayoutEditor** - trang chỉnh sửa bản đồ cho robot AGV/AMR. LayoutEditor cho phép người dùng vẽ và chỉnh sửa nodes, edges, stations trên canvas SVG với background image.
|
||||
|
||||
**Note:** Quá trình xây dựng LayoutManager đã được lưu riêng trước đó.
|
||||
|
||||
---
|
||||
|
||||
## 📐 Design Specifications (From Initial Discussion)
|
||||
|
||||
### **1. Coordinate System**
|
||||
| Item | Value |
|
||||
|------|-------|
|
||||
| Web Origin | Top-Left (0,0) |
|
||||
| Layout Origin | Bottom-Left (0,0) |
|
||||
| Transform | `WebY = ImageHeight - LayoutY` |
|
||||
| Mouse Display | World Coordinates (meters) - góc trên trái |
|
||||
|
||||
### **2. SVG Layers (Bottom → Top)**
|
||||
1. Background Image (SLAM Map)
|
||||
2. Grid
|
||||
3. Edges + Trajectories
|
||||
4. Trajectory Control Points (khi edit)
|
||||
5. Nodes
|
||||
6. Station Overlays
|
||||
7. Selection Highlights
|
||||
8. Temporary Drawing (create edge preview)
|
||||
|
||||
### **3. Node Properties**
|
||||
| Property | Editable | Type |
|
||||
|----------|----------|------|
|
||||
| NodeId | ✅ | string |
|
||||
| NodeName | ✅ | string |
|
||||
| NodeDescription | ✅ | string |
|
||||
| X, Y | ✅ | double (meters) |
|
||||
| MapId | ✅ | string? |
|
||||
| Station Info | ❌ (read-only) | display |
|
||||
| VehicleProperties | ✅ | per VehicleType |
|
||||
|
||||
### **4. Edge Properties**
|
||||
| Property | Editable | Type |
|
||||
|----------|----------|------|
|
||||
| EdgeId | ✅ | string |
|
||||
| EdgeName | ✅ | string |
|
||||
| EdgeDescription | ✅ | string |
|
||||
| StartNodeId | ❌ | Guid (read-only) |
|
||||
| EndNodeId | ❌ | Guid (read-only) |
|
||||
| Length | ❌ | double (auto-calc) |
|
||||
| VehicleProperties | ✅ | per VehicleType |
|
||||
|
||||
### **5. Edge Vehicle Properties**
|
||||
| Property | Type |
|
||||
|----------|------|
|
||||
| VehicleOrientation | double? |
|
||||
| OrientationType | enum (GLOBAL, TANGENTIAL) |
|
||||
| RotationAllowed | bool? |
|
||||
| RotationAtStartNodeAllowed | enum (NONE, CCW, CW, BOTH) |
|
||||
| RotationAtEndNodeAllowed | enum (NONE, CCW, CW, BOTH) |
|
||||
| MaxSpeed, MaxRotationSpeed | double? |
|
||||
| MinHeight, MaxHeight | double? |
|
||||
| LoadRestriction_* | bool?, string? |
|
||||
| Trajectory | JSON (NURBS) |
|
||||
|
||||
### **6. Node Vehicle Properties**
|
||||
| Property | Type |
|
||||
|----------|------|
|
||||
| Theta | double? (radians) |
|
||||
| Actions | JSON (Form Builder) |
|
||||
|
||||
### **7. Display Settings**
|
||||
| Setting | Default | Persist |
|
||||
|---------|---------|---------|
|
||||
| Show Edge Names | ✅ | Session |
|
||||
| Show Node Names | ✅ | Session |
|
||||
| Show Grid | ✅ | Session |
|
||||
| Show Background | ✅ | Session |
|
||||
| Grid Spacing | 1.0m | Session |
|
||||
| Selected VehicleType | First | Session |
|
||||
|
||||
### **8. Visual Representations**
|
||||
| Element | Style |
|
||||
|---------|-------|
|
||||
| Node (normal) | Circle, scale with zoom |
|
||||
| Node (with Station) | Circle, different color (green) |
|
||||
| Edge | Line with arrow at end |
|
||||
| Edge Direction | Arrow at EndNode |
|
||||
| Edge Name | Text above, center of edge |
|
||||
| Node Name | Text below, center of node |
|
||||
| Selection | Highlight ring/glow |
|
||||
| Trajectory | NURBS curve (selected VehicleType only) |
|
||||
| Control Points | Small circles (when editing) |
|
||||
| Create Edge Preview | Dashed line from node1 to mouse (Option B) |
|
||||
|
||||
### **9. Operations**
|
||||
| Operation | Behavior |
|
||||
|-----------|----------|
|
||||
| Select | Click = select single, Ctrl+Click = add to selection |
|
||||
| Scanner | Drag rectangle to multi-select (objects **completely** within rectangle - Option C) |
|
||||
| CreateEdge 1-Way | Click node1 → node2, creates 1 edge |
|
||||
| CreateEdge 2-Way | Click node1 → node2, creates 2 edges (A→B, B→A) |
|
||||
| Copy | Duplicate nodes with new IDs, duplicate edges, NO stations |
|
||||
| Move | Drag selected nodes (no snap to grid) |
|
||||
| Merge | Combine selected nodes at center position |
|
||||
| Split | Split 1 node into N nodes (N = edge count), auto offset |
|
||||
| Align H-Left | Align selected nodes to leftmost X |
|
||||
| Align H-Right | Align selected nodes to rightmost X |
|
||||
| Align H-Center | Align selected nodes to average X |
|
||||
| Align V-Top | Align selected nodes to topmost Y |
|
||||
| Align V-Bottom | Align selected nodes to bottommost Y |
|
||||
| Align V-Center | Align selected nodes to average Y |
|
||||
|
||||
### **10. Undo/Redo**
|
||||
| Supported | Not Supported |
|
||||
|-----------|---------------|
|
||||
| Move operations | Create/Delete |
|
||||
| Alignment operations | Property changes |
|
||||
|
||||
### **11. Keyboard Shortcuts**
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| Ctrl+Z | Undo |
|
||||
| Ctrl+Y | Redo |
|
||||
| Ctrl+S | Save |
|
||||
| Ctrl+C | Copy selected nodes/edges |
|
||||
| Ctrl+M | Move mode (if nodes selected) |
|
||||
| Delete | Delete selected |
|
||||
| Escape | Cancel current operation (CreateEdge, Copy) |
|
||||
|
||||
### **12. Viewport**
|
||||
| Setting | Value |
|
||||
|---------|-------|
|
||||
| Initial | Zoom to fit image bounds |
|
||||
| Pan | Middle mouse drag OR toolbar button |
|
||||
| Zoom | Mouse wheel OR toolbar buttons (zoom at cursor position) |
|
||||
| Fit | Toolbar button |
|
||||
|
||||
### **13. Trajectory Editor**
|
||||
| Setting | Value |
|
||||
|---------|-------|
|
||||
| Display | Only when edge selected (single select mode, not multi-select) |
|
||||
| VehicleType | Show trajectory of selected VehicleType |
|
||||
| Degree | User selectable (1, 2, 3) |
|
||||
| Control Points | Managed via Right Panel buttons |
|
||||
| Edit | Drag control points on canvas |
|
||||
| Default Creation | Auto-generate: 2 control points tại StartNode và EndNode (straight line) - Option A |
|
||||
|
||||
### **14. Trajectory Degree Change Logic**
|
||||
**Khi TĂNG Degree (thêm control points):**
|
||||
- **Degree 1 → Degree 2:** Thêm 1 point tại giữa curve (t = 0.5)
|
||||
- **Degree 2 → Degree 3:** Thêm 1 point tại t = 0.33 hoặc t = 0.67
|
||||
- Chèn điểm mới ở giữa 2 điểm có khoảng cách lớn nhất để giữ biên dạng ít thay đổi
|
||||
|
||||
**Khi GIẢM Degree (xóa control points):**
|
||||
- **Degree 3 → Degree 2:** Giữ P0 và P_last, tính P_middle = weighted average của các control points cũ
|
||||
- **Degree 2 → Degree 1:** Chỉ giữ P0 và P_last, curve thành đường thẳng
|
||||
- Giảm từ n points xuống n-1 points bằng cách tính trung bình các cặp điểm liền kề
|
||||
|
||||
### **15. Actions Form Builder**
|
||||
| Feature | Behavior |
|
||||
|---------|----------|
|
||||
| ActionType | Dropdown (VehicleType ActionDefaults) + Custom text input |
|
||||
| Parameters | Key-Value pairs, add/remove individual parameters |
|
||||
| Defaults | Pre-filled from VehicleType ActionDefaults, có thể thêm/xóa tiếp |
|
||||
| Validation | JSON syntax + structure |
|
||||
|
||||
### **16. CheckLayout Validations**
|
||||
| Check | Description |
|
||||
|-------|-------------|
|
||||
| Edge Min Length | Edge length < EdgeMinLengthCreate in settings |
|
||||
| (More to be added) | ... |
|
||||
|
||||
### **17. Toolbar Order (Final)**
|
||||
```
|
||||
[Scanner][CreateEdge 1-Way▼][Select] │ [Zoom][Fit] │
|
||||
[H-Left][H-Right][H-Center][V-Top][V-Bottom][V-Center] [Copy][Move] │
|
||||
[Merge][Split] │
|
||||
VehicleType: [AMR-T800 ▼] │
|
||||
[Undo][Redo] [Save] [Delete] [Check] [Exit]
|
||||
```
|
||||
|
||||
**Note:** Display Options và Grid Spacing đã được chuyển vào Settings Tab (không còn trên Toolbar).
|
||||
|
||||
### **18. Exit Button**
|
||||
- **Behavior:** Quay về LayoutManager page (Option A)
|
||||
|
||||
### **19. Component Structure**
|
||||
```
|
||||
RobotNet10.MapEditor/
|
||||
├─ Components/
|
||||
│ └─ LayoutEditor/
|
||||
│ ├─ LayoutEditorComponent.razor ← Main container
|
||||
│ ├─ LayoutEditorComponent.razor.css
|
||||
│ ├─ EditorToolbar.razor ← Toolbar with all buttons
|
||||
│ ├─ EditorToolbar.razor.css
|
||||
│ ├─ MousePositionDisplay.razor ← World coordinates display
|
||||
│ ├─ SvgEditorCanvas.razor ← Main SVG canvas
|
||||
│ ├─ SvgEditorCanvas.razor.css
|
||||
│ ├─ RightPanel/
|
||||
│ │ ├─ EditorRightPanel.razor ← Container for tabs
|
||||
│ │ ├─ PropertiesTab.razor ← Selected object properties
|
||||
│ │ ├─ NodePropertiesEditor.razor
|
||||
│ │ ├─ EdgePropertiesEditor.razor
|
||||
│ │ ├─ VehiclePropertiesEditor.razor
|
||||
│ │ ├─ TrajectoryEditor.razor ← NURBS control points
|
||||
│ │ └─ SettingsTab.razor ← Layout level settings
|
||||
│ └─ Dialogs/
|
||||
│ ├─ ActionsFormDialog.razor ← Actions form builder
|
||||
│ ├─ CheckLayoutResultDialog.razor ← Validation results
|
||||
│ └─ UnsavedChangesDialog.razor ← Confirm leave
|
||||
│
|
||||
├─ Services/
|
||||
│ └─ State/
|
||||
│ ├─ LayoutEditorState.cs ← Centralized state
|
||||
│ ├─ EditorCommand.cs ← For Undo/Redo
|
||||
│ └─ EditorMode.cs ← Enum: Select, CreateEdge, etc.
|
||||
│
|
||||
└─ wwwroot/
|
||||
└─ js/
|
||||
└─ svgEditor.js ← JavaScript for SVG interactions
|
||||
```
|
||||
|
||||
### **20. Host Page**
|
||||
```
|
||||
RobotNet10.RobotApp.Client/
|
||||
└─ Pages/
|
||||
└─ LayoutEditor.razor ← Host page with route
|
||||
@page "/layout-editor/{LevelId:guid}"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ❓ Key Design Decisions (Q&A Summary)
|
||||
|
||||
### **Q8: Trajectory Display**
|
||||
- **Q8.1:** Hiển thị khi edge được select ở chế độ edit (không phải multi-select ở scanner)
|
||||
- **Q8.2:** Option B - Trajectory chỉ hiển thị cho VehicleType được chọn
|
||||
- **Q8.3:** Degree có thể thay đổi (1, 2, 3), số lượng control points tương ứng với degree
|
||||
|
||||
### **Q9: Node Properties**
|
||||
- **Q9.1:** Option B - Station info hiển thị trong Node Properties Tab
|
||||
|
||||
### **Q10: Edge/Node Names**
|
||||
- **Q10.1:** Option D - Edge names hiển thị ở trên, center của edge
|
||||
- **Q10.2:** Scale với zoom level
|
||||
|
||||
### **Q11: Station Info Display**
|
||||
- **Q11.1:** Option B - Station info trong Node Properties Tab
|
||||
- **Q11.2:** Tab Node Properties, trong đó sẽ có cả thông tin của Station
|
||||
|
||||
### **Q12: Actions Form Builder**
|
||||
- **Q12.1:** Option C - VehicleType có ActionDefaults, dropdown hiển thị defaults + cho phép nhập custom text
|
||||
- **Q12.2:** Có thể thêm/xóa từng parameter. Nếu ActionDefault đã có parameters thì sẽ thêm/xóa tiếp vào những parameters default đó
|
||||
|
||||
### **Q13: Keyboard Shortcuts**
|
||||
- **Q13.1:** Có các shortcuts: Ctrl+Z (Undo), Ctrl+Y (Redo), Ctrl+S (Save), Delete (Delete selected)
|
||||
|
||||
### **Q14: Selection Behavior**
|
||||
- **Q14.1:** Option A - Nếu chỉ Click thì sẽ select sang đối tượng mới và unselect đối tượng cũ
|
||||
|
||||
### **Q15: Box Select**
|
||||
- **Q15.1:** Option A - Box select trong Scanner mode
|
||||
|
||||
### **Q16: NURBS Degree Change**
|
||||
- **Q16.1:** Khi thay đổi Degree, control points được thêm/xóa tự động với logic giữ biên dạng ít thay đổi nhất (xem section 14 ở trên)
|
||||
|
||||
### **Q17: Default Trajectory**
|
||||
- **Q17.1:** Option A - Auto-generate: 2 control points tại StartNode và EndNode (straight line)
|
||||
|
||||
### **Q18: Box Select Behavior**
|
||||
- **Q18.1:** Option C - Objects **hoàn toàn** nằm trong rectangle
|
||||
|
||||
### **Q19: Exit Button**
|
||||
- **Q19:** Option A - Quay về LayoutManager page
|
||||
|
||||
### **Q20: Create Edge Preview**
|
||||
- **Q20:** Option B - Đường nét liền màu khác (e.g., gray) từ node đầu tiên đến vị trí mouse
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Implementation Phases
|
||||
|
||||
### **Phase 1: Foundation (Core Structure)** ✅ COMPLETE
|
||||
**Status:** Hoàn thành trong cuộc hội thoại ban đầu
|
||||
|
||||
**Deliverables:**
|
||||
- `LayoutEditorComponent.razor` - Main container với left canvas, right panel
|
||||
- `EditorToolbar.razor` - Full toolbar với tất cả buttons
|
||||
- `LayoutEditorState.cs` - Centralized state management
|
||||
- Host page: `RobotApp.Client/Pages/LayoutEditor.razor` với route `/layout-editor/{LevelId:guid}`
|
||||
- Navigation từ LayoutManager page
|
||||
|
||||
**Key Features:**
|
||||
- State management với data loading (Level, Nodes, Edges, Stations, VehicleTypes)
|
||||
- Selection management (single/multi-select)
|
||||
- Viewport control structure (`ViewportState` class)
|
||||
- Coordinate transforms (SVG ↔ World)
|
||||
- Undo/Redo stack (Command pattern với `EditorCommand` abstract class)
|
||||
- Editor modes enum (`Select`, `Scanner`, `CreateEdge1Way`, `CreateEdge2Way`, `Pan`, `Move`, `Copy`)
|
||||
|
||||
**State Management Structure:**
|
||||
- `LayoutEditorState` - Centralized state với:
|
||||
- Data: `Level`, `Nodes`, `Edges`, `Stations`, `VehicleTypes`
|
||||
- Selection: `SelectedNodeIds`, `SelectedEdgeIds`
|
||||
- Viewport: `ViewportState` (ViewBoxX, ViewBoxY, ViewBoxWidth, ViewBoxHeight, ZoomLevel)
|
||||
- Display options: `ShowEdgeNames`, `ShowNodeNames`, `ShowGrid`, `ShowBackgroundImage`, `GridSpacing`
|
||||
- Undo/Redo: `UndoStack`, `RedoStack`
|
||||
- Editor mode: `CurrentMode`
|
||||
- Methods: `InitializeAsync()`, `Pan()`, `Zoom()`, `FitToScreen()`, `SelectNodes()`, `SelectEdges()`, etc.
|
||||
|
||||
**Command Pattern:**
|
||||
- `EditorCommand` abstract class với `Execute()` và `Undo()` methods
|
||||
- `MoveNodesCommand` - Example command cho move operations
|
||||
- Commands được push vào `UndoStack` khi execute
|
||||
|
||||
---
|
||||
|
||||
### **Phase 2: SVG Canvas (Basic)** ✅ COMPLETE
|
||||
**Status:** Hoàn thành trong cuộc hội thoại ban đầu
|
||||
|
||||
**Deliverables:**
|
||||
- `SvgEditorCanvas.razor` - SVG rendering component
|
||||
- `MousePositionDisplay.razor` - World coordinates display (góc trên trái)
|
||||
- `svgEditor.js` - JavaScript interop module
|
||||
|
||||
**Key Features:**
|
||||
- Background image rendering (Y-axis flip để match world coordinates)
|
||||
- Grid rendering (togglable, configurable spacing)
|
||||
- Nodes rendering (circles, green color for nodes with stations)
|
||||
- Edges rendering (lines with direction arrows at EndNode)
|
||||
- Edge/Node names (togglable, scale with zoom level)
|
||||
- Box select rectangle visualization (Scanner mode)
|
||||
- Create edge preview line (dashed/gray line from start node to mouse)
|
||||
- Mouse position tracking (world coordinates in meters, displayed top-left)
|
||||
|
||||
**Coordinate System:**
|
||||
- **World Coordinates:** Bottom-left origin (0,0), Y increases upward (meters)
|
||||
- **SVG Coordinates:** Top-left origin (0,0), Y increases downward (pixels)
|
||||
- **Transform:** `svgY = physicalHeight - worldY` (flip Y axis)
|
||||
|
||||
**JavaScript Interop (`svgEditor.js`):**
|
||||
- Mouse events: `mousemove`, `mousedown`, `mouseup`, `wheel`
|
||||
- Keyboard shortcuts: `keydown` (Ctrl+Z/Y/S, Delete, Escape)
|
||||
- Coordinate conversion: `screenToSvg()`, `screenToSvgArray()` (exported for Blazor)
|
||||
- SVG element reference management
|
||||
|
||||
**Rendering Layers (implemented):**
|
||||
1. Background Image (if available)
|
||||
2. Grid (if `ShowGrid` = true)
|
||||
3. Edges (with arrows and names if enabled)
|
||||
4. Nodes (circles with names if enabled)
|
||||
5. Selection highlights (rings around selected nodes)
|
||||
6. Box select rectangle (when in Scanner mode)
|
||||
7. Create edge preview line (when creating edge)
|
||||
|
||||
**Event Handling:**
|
||||
- `OnMouseMove` - Updates mouse position, handles box select, pan, create edge preview
|
||||
- `OnMouseDown` - Handles selection, starts pan, starts create edge
|
||||
- `OnMouseUp` - Ends pan, completes box select, completes create edge
|
||||
- `OnWheel` - Handles zoom (with cursor position preservation)
|
||||
|
||||
---
|
||||
|
||||
### **Phase 3: Viewport Controls** ⏳ IN PROGRESS
|
||||
**Status:** Đã implement cơ bản, đang polish và fix bugs
|
||||
|
||||
#### **3.1. Pan (Middle Mouse Drag)** ✅ FIXED
|
||||
**Vấn đề ban đầu:**
|
||||
- Pan bị giật và không di chuyển đúng theo chuột
|
||||
- Delta bị cộng dồn khi di chuyển chuột
|
||||
|
||||
**Nguyên nhân:**
|
||||
- Tính delta từ điểm bắt đầu pan mỗi lần mouse move
|
||||
- Khi ViewBox thay đổi sau mỗi lần pan, việc convert `panStartScreen` sang SVG cho giá trị khác
|
||||
- Gây ra việc cộng dồn delta
|
||||
|
||||
**Giải pháp:**
|
||||
- Thay đổi từ tính delta từ điểm bắt đầu sang **incremental delta**
|
||||
- Lưu `panLastScreen` (vị trí screen của lần move trước)
|
||||
- Mỗi lần mouse move:
|
||||
1. Convert `panLastScreen` và `currentScreen` sang SVG (dùng ViewBox hiện tại)
|
||||
2. Tính delta = `lastSvg - currentSvg`
|
||||
3. Pan ViewBox theo delta
|
||||
4. Update `panLastScreen = currentScreen`
|
||||
|
||||
**Code Changes:**
|
||||
```csharp
|
||||
// Thêm panLastScreen để track vị trí trước đó
|
||||
private (double X, double Y)? panLastScreen;
|
||||
|
||||
// Trong OnMouseMove:
|
||||
if (isPanning && panLastScreen.HasValue)
|
||||
{
|
||||
// Tính incremental delta từ lần move trước
|
||||
_ = PanIncrementalAsync(panLastScreen.Value.X, panLastScreen.Value.Y, screenX, screenY);
|
||||
panLastScreen = (screenX, screenY);
|
||||
}
|
||||
|
||||
// Trong OnMouseDown (button == 1):
|
||||
panStartScreen = (screenX, screenY);
|
||||
panLastScreen = (screenX, screenY); // Initialize
|
||||
```
|
||||
|
||||
**JavaScript Changes:**
|
||||
- Thêm `screenToSvgArray()` function để export cho Blazor
|
||||
- `OnMouseMove` và `OnMouseDown` nhận thêm `screenX, screenY` parameters
|
||||
|
||||
#### **3.2. Zoom (Mouse Wheel)** ✅ FIXED
|
||||
**Vấn đề ban đầu:**
|
||||
- Zoom không giữ tọa độ mouse không đổi
|
||||
- Zoom không chính xác tại vị trí cursor
|
||||
|
||||
**Giải pháp:**
|
||||
- Tính tỷ lệ vị trí cursor trong viewBox hiện tại
|
||||
- Giữ điểm cursor không đổi trong world coordinates khi zoom
|
||||
- Logic:
|
||||
```csharp
|
||||
// Tính ratio của cursor trong viewBox
|
||||
var ratioX = (svgCenterX - ViewBoxX) / ViewBoxWidth;
|
||||
var ratioY = (svgCenterY - ViewBoxY) / ViewBoxHeight;
|
||||
|
||||
// New dimensions
|
||||
var newWidth = ViewBoxWidth / factor;
|
||||
var newHeight = ViewBoxHeight / factor;
|
||||
|
||||
// Adjust ViewBox để giữ cursor tại cùng vị trí world
|
||||
ViewBoxX = svgCenterX - ratioX * newWidth;
|
||||
ViewBoxY = svgCenterY - ratioY * newHeight;
|
||||
```
|
||||
|
||||
#### **3.3. Grid Rendering** ✅ FIXED
|
||||
**Thay đổi:**
|
||||
- `stroke="#999"` (đậm hơn, từ #ccc)
|
||||
- `stroke-width="0.04"` (đậm gấp đôi, từ 0.02)
|
||||
- `opacity="0.7"` (rõ hơn, từ 0.5)
|
||||
- Thêm `stroke-dasharray="0.1,0.1"` (nét đứt)
|
||||
|
||||
---
|
||||
|
||||
### **Phase 4: Selection** ⏳ PARTIAL
|
||||
**Status:** Cơ bản đã có, cần enhancement
|
||||
|
||||
**Đã có:**
|
||||
- Single select (click)
|
||||
- Multi-select (Ctrl+click)
|
||||
- Box select (Scanner mode)
|
||||
- Selection visual feedback
|
||||
|
||||
**Cần làm:**
|
||||
- Enhancement và polish
|
||||
- Better visual feedback
|
||||
|
||||
---
|
||||
|
||||
### **Phase 5: Create Edge** ✅ COMPLETE
|
||||
**Status:** Đã hoàn thành
|
||||
|
||||
**Đã implement:**
|
||||
- CreateEdge 1-Way mode với API call (`CreateEdgeAsync`)
|
||||
- CreateEdge 2-Way mode với API call (tạo 2 edges ngược chiều)
|
||||
- Edge preview while creating (preview line + preview nodes)
|
||||
- Smart node detection (tích hợp API - tự động tìm node trong `NodeProximityRadius` hoặc tạo mới)
|
||||
- Preview node hiển thị tại vị trí click đầu tiên và vị trí mouse
|
||||
- Escape key để cancel CreateEdge operation
|
||||
|
||||
**API Integration:**
|
||||
- `POST /api/edges` - Create edge với smart node detection
|
||||
- Backend tự động tìm hoặc tạo nodes tại tọa độ start/end
|
||||
- Nếu điểm nằm trong `NodeProximityRadius` của node hiện có → kết nối với node đó
|
||||
- Nếu không → tạo node mới tại tọa độ đó
|
||||
|
||||
**Visual Features:**
|
||||
- Preview line: dashed line từ start node đến mouse position
|
||||
- Preview nodes: semi-transparent circles tại start và end positions
|
||||
- 2-way edges: hiển thị thành 2 đường song song với offset
|
||||
|
||||
---
|
||||
|
||||
### **Phase 6: Edit Operations** ⏳ PARTIAL
|
||||
**Status:** Đã implement một phần
|
||||
|
||||
#### **6.1. Move Nodes** ✅ COMPLETE
|
||||
- **Trigger:** Ctrl + Drag (hoặc Move mode với Ctrl+M)
|
||||
- **Behavior:** Di chuyển node theo chuột, khi thả Ctrl thì giữ nguyên vị trí hiện tại
|
||||
- **Undo/Redo:** Hỗ trợ qua `MoveNodesCommand`
|
||||
- **Change Tracking:** Đánh dấu nodes đã modified để Save sau
|
||||
|
||||
#### **6.2. Copy Nodes/Edges** ✅ COMPLETE
|
||||
- **Trigger:** Toolbar button hoặc Ctrl+C (khi có selection)
|
||||
- **Mode:** `EditorMode.Copy` - dedicated mode với preview
|
||||
- **Behavior:**
|
||||
- Click và drag để định offset
|
||||
- Preview nodes và edges tại vị trí mới
|
||||
- Thả chuột để hoàn thành copy
|
||||
- Escape để cancel
|
||||
- **API:** `POST /api/layout-data/copy-nodes` - Backend xử lý toàn bộ logic
|
||||
- **Logic:**
|
||||
- Tạo tất cả nodes mới với offset trước (validate coordinates, copy vehicle properties)
|
||||
- Sau đó tạo tất cả edges mới (sử dụng node ID mapping)
|
||||
- Xử lý 2-way edges: copy cả forward và reverse edge
|
||||
- Không copy stations
|
||||
- **Selection:** Tự động select các nodes/edges mới sau khi copy
|
||||
|
||||
#### **6.3. Delete Selected** ✅ COMPLETE
|
||||
- **Trigger:** Delete button hoặc Delete key
|
||||
- **API:** `DELETE /api/edges/{id}` hoặc batch delete
|
||||
- **Behavior:** Xóa edges, backend tự động xóa orphaned nodes
|
||||
- **Confirmation:** Dialog xác nhận trước khi xóa
|
||||
|
||||
#### **6.4. Merge Nodes** ✅ COMPLETE
|
||||
- **Trigger:** Toolbar button (khi có 2+ nodes selected)
|
||||
- **API:** `POST /api/layout-data/merge-nodes`
|
||||
- **Behavior:**
|
||||
- Gộp tất cả selected nodes thành 1 node tại vị trí center
|
||||
- Redirect tất cả edges đến node mới
|
||||
- Gộp vehicle properties từ tất cả nodes
|
||||
- Nếu nhiều nodes có stations → dialog chọn node giữ station
|
||||
- Distance check: nếu nodes ngoài `NodeProximityRadius` → confirmation dialog
|
||||
- **Selection:** Tự động select node mới sau khi merge
|
||||
|
||||
#### **6.5. Split Node** ✅ COMPLETE
|
||||
- **Trigger:** Toolbar button (khi có 1 node selected với 2+ edges)
|
||||
- **API:** `POST /api/layout-data/split-node`
|
||||
- **Behavior:**
|
||||
- Split 1 node thành N nodes (N = số edges connected)
|
||||
- Mỗi edge được redirect đến node mới tương ứng
|
||||
- Copy vehicle properties đến tất cả nodes mới
|
||||
- Nếu node có station → dialog chọn node nhận station
|
||||
- Validation: node phải có ít nhất 2 edges mới được split
|
||||
- **Selection:** Tự động select tất cả nodes mới sau khi split
|
||||
|
||||
#### **6.6. Alignment Functions** ❌ NOT STARTED
|
||||
- **Status:** Buttons đã có, chưa implement logic
|
||||
|
||||
---
|
||||
|
||||
### **Phase 7: Right Panel - Properties** ⏳ PARTIAL
|
||||
**Status:** UI đã có, cần tích hợp API
|
||||
|
||||
#### **7.1. Settings Tab** ✅ FIXED & ENHANCED
|
||||
**Thay đổi trong cuộc hội thoại này:**
|
||||
|
||||
**a) Chuyển Display Options về Settings Tab:**
|
||||
- Xóa Display Options khỏi Toolbar
|
||||
- Thêm Display Options vào Settings Tab (sau Grid Settings)
|
||||
- Bao gồm: Show Edge Names, Show Node Names, Show Grid, Show Background Map
|
||||
- Là session state (không lưu vào database)
|
||||
|
||||
**b) Auto-generation Settings - Editable:**
|
||||
- Chuyển từ read-only sang editable
|
||||
- Thêm local state để quản lý giá trị chỉnh sửa:
|
||||
- `nodeNameAutoGenerate` (bool)
|
||||
- `edgeNameAutoGenerate` (bool)
|
||||
- `edgeMinLengthCreate` (double)
|
||||
- `nodeProximityRadius` (double)
|
||||
- Thêm nút "Save Settings" với loading indicator
|
||||
- Tích hợp với API để lưu settings
|
||||
|
||||
**c) Thứ tự sections:**
|
||||
1. Grid Settings
|
||||
2. Display Options
|
||||
3. Auto-generation Settings (editable với Save button)
|
||||
4. Layout Level Info (read-only)
|
||||
5. Statistics
|
||||
|
||||
**d) API Integration:**
|
||||
- Tạo `EditorSettingsInfo` model mới
|
||||
- Thêm `EditorSettings` property vào `UpdateLayoutLevelRequest`
|
||||
- Cập nhật `LayoutService.UpdateLevelAsync()` để xử lý editor settings
|
||||
- Settings được lưu vào database khi click Save
|
||||
|
||||
**Code Structure:**
|
||||
```csharp
|
||||
// Local editable state
|
||||
private bool nodeNameAutoGenerate;
|
||||
private bool edgeNameAutoGenerate;
|
||||
private double edgeMinLengthCreate;
|
||||
private double nodeProximityRadius;
|
||||
|
||||
// Initialize từ settings khi component load
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
if (State.Level?.EditorSettings != null)
|
||||
{
|
||||
var settings = State.Level.EditorSettings;
|
||||
nodeNameAutoGenerate = settings.NodeNameAutoGenerate;
|
||||
// ... initialize other fields
|
||||
}
|
||||
}
|
||||
|
||||
// Save to API
|
||||
private async Task SaveEditorSettings()
|
||||
{
|
||||
var request = new UpdateLayoutLevelRequest
|
||||
{
|
||||
EditorSettings = new EditorSettingsInfo
|
||||
{
|
||||
NodeNameAutoGenerate = nodeNameAutoGenerate,
|
||||
// ... other fields
|
||||
}
|
||||
};
|
||||
var updatedLevel = await ApiService.UpdateLevelAsync(State.Level.Id, request);
|
||||
State.Level = updatedLevel; // Update state
|
||||
}
|
||||
```
|
||||
|
||||
**Đã có:**
|
||||
- Node properties editor (UI)
|
||||
- Edge properties editor (UI)
|
||||
- VehicleType dropdown
|
||||
- Vehicle properties editor (UI)
|
||||
- Station info display
|
||||
|
||||
**Cần làm:**
|
||||
- Save properties to API
|
||||
- Validation
|
||||
- Error handling
|
||||
|
||||
---
|
||||
|
||||
### **Phase 8: Trajectory Editor** ❌ NOT STARTED
|
||||
**Status:** Chưa thực hiện
|
||||
|
||||
**Cần implement:**
|
||||
- Display trajectory (NURBS curve)
|
||||
- Control points visualization
|
||||
- Drag control points
|
||||
- Add/Remove control points
|
||||
- Change degree (với logic giữ biên dạng ít thay đổi)
|
||||
|
||||
---
|
||||
|
||||
### **Phase 9: Actions Form Builder** ❌ NOT STARTED
|
||||
**Status:** Chưa thực hiện
|
||||
|
||||
**Cần implement:**
|
||||
- Actions form dialog
|
||||
- ActionType dropdown (VehicleType defaults + custom text)
|
||||
- Parameters key-value editor
|
||||
- Validation
|
||||
|
||||
---
|
||||
|
||||
### **Phase 10: Undo/Redo & Save** ⏳ PARTIAL
|
||||
**Status:** Structure đã có, Save API đã implement
|
||||
|
||||
**Đã có:**
|
||||
- Command pattern structure
|
||||
- Undo stack
|
||||
- Redo stack
|
||||
- Save button (UI)
|
||||
- Save API: `POST /api/layout-data/save` - Batch save với transaction
|
||||
- Change tracking: `_modifiedNodeIds`, `_modifiedEdgeIds`
|
||||
- `HasUnsavedChanges` flag
|
||||
|
||||
**Save API Details:**
|
||||
- **Endpoint:** `POST /api/layout-data/save`
|
||||
- **Request:** `SaveLayoutDataRequest` với:
|
||||
- `NodesToCreate`, `NodesToUpdate`, `NodesToDelete`
|
||||
- `EdgesToCreate`, `EdgesToUpdate`, `EdgesToDelete`
|
||||
- **Conflict Handling:** Force Overwrite (Option D)
|
||||
- **Transaction:** Toàn bộ operation trong 1 transaction, rollback nếu có lỗi
|
||||
- **Response:** `SaveLayoutDataResponse` với số lượng items created/updated
|
||||
|
||||
**Change Tracking:**
|
||||
- `MarkNodeModified(Guid nodeId)` - Đánh dấu node đã thay đổi
|
||||
- `MarkEdgeModified(Guid edgeId)` - Đánh dấu edge đã thay đổi
|
||||
- `SaveAsync()` - Thu thập tất cả modified/new/deleted items và gọi API
|
||||
- **Note:** Các API calls trực tiếp (CreateEdge, DeleteEdge, CopyNodes, MergeNodes, SplitNode) KHÔNG set `HasUnsavedChanges` vì đã lưu vào database
|
||||
|
||||
**Cần làm:**
|
||||
- Unsaved changes warning khi exit
|
||||
- Undo/Redo cho các operations khác (Align, etc.)
|
||||
|
||||
---
|
||||
|
||||
### **Phase 11: Polish** ⏳ PARTIAL
|
||||
**Status:** Một phần đã có
|
||||
|
||||
**Đã có:**
|
||||
- Keyboard shortcuts (Ctrl+Z/Y/S/C/M, Delete, Escape)
|
||||
- Display options
|
||||
- Settings tab (đã sửa và enhance)
|
||||
- Exit button
|
||||
- Text rendering với fontSize phụ thuộc ZoomLevel và Resolution
|
||||
- 2-way edges visualization (2 đường song song)
|
||||
- Box select trong Scanner mode (select cả nodes và edges)
|
||||
|
||||
**Text Font Size:**
|
||||
- **Formula:** `fontSizeSVG = baseFontSizeWorld / (Resolution * ZoomLevel)`
|
||||
- **baseFontSizeWorld:** 0.24 meters (cho node names và edge names)
|
||||
- **Resolution:** meters per pixel (từ `EditorSettings.Resolution`)
|
||||
- **ZoomLevel:** hệ số zoom hiện tại
|
||||
- **Result:** Text có cùng kích thước visual với mọi Resolution, zoom theo ZoomLevel
|
||||
|
||||
**Cần làm:**
|
||||
- CheckLayout validation logic
|
||||
- Performance optimization
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ Architecture Overview
|
||||
|
||||
### **Component Hierarchy**
|
||||
```
|
||||
LayoutManager (click Edit)
|
||||
→ Navigate to /layout-editor/{levelId}
|
||||
→ LayoutEditorComponent
|
||||
├── EditorToolbar (top)
|
||||
├── SVG Canvas (left) + MousePositionDisplay
|
||||
└── Right Panel
|
||||
├── Properties Tab
|
||||
│ ├── NodePropertiesEditor (single node)
|
||||
│ └── EdgePropertiesEditor (single edge)
|
||||
└── Settings Tab
|
||||
├── Grid Settings
|
||||
├── Display Options
|
||||
├── Auto-generation Settings
|
||||
├── Layout Level Info
|
||||
└── Statistics
|
||||
```
|
||||
|
||||
### **Data Flow**
|
||||
1. **Initialization:**
|
||||
- User clicks "Edit" on LayoutManager
|
||||
- Navigate to `/layout-editor/{levelId}`
|
||||
- `LayoutEditorComponent` loads
|
||||
- `LayoutEditorState.InitializeAsync()` fetches:
|
||||
- Level info (with EditorSettings)
|
||||
- Layout data (Nodes, Edges, Stations)
|
||||
- VehicleTypes
|
||||
- Background image (if available)
|
||||
- Viewport initialized to fit image bounds
|
||||
|
||||
2. **User Interactions:**
|
||||
- Mouse events → JavaScript (`svgEditor.js`) → Blazor (`SvgEditorCanvas.razor`)
|
||||
- State changes → `LayoutEditorState` → Notify components via `OnStateChanged` event
|
||||
- UI updates → Blazor re-renders affected components
|
||||
|
||||
3. **Save Operations:**
|
||||
- User edits properties → Local state changes
|
||||
- Click "Save Settings" → API call (`MapManagerApiService.UpdateLevelEditorSettingsAsync`)
|
||||
- Backend updates database → Returns updated Level
|
||||
- State updated → UI refreshed
|
||||
|
||||
### **State Management Pattern**
|
||||
- **Centralized State:** `LayoutEditorState` (scoped service)
|
||||
- **Event-driven:** Components subscribe to `OnStateChanged` event
|
||||
- **Immutable Updates:** State methods return new state or update properties and notify
|
||||
- **Command Pattern:** Undo/Redo via `EditorCommand` abstract class
|
||||
|
||||
### **Coordinate Transformation**
|
||||
- **World → SVG:** `WorldToSvg(worldX, worldY)` → `(worldX, physicalHeight - worldY)`
|
||||
- **SVG → World:** `SvgToWorld(svgX, svgY)` → `(svgX, physicalHeight - svgY)`
|
||||
- **Screen → SVG:** JavaScript `screenToSvgArray(screenX, screenY)` using `getScreenCTM()`
|
||||
|
||||
### **API Integration**
|
||||
- **Service:** `MapManagerApiService` (injected)
|
||||
- **Endpoints Used:**
|
||||
- `GET /api/layouts/{layoutId}/levels/{levelId}` - Get level info
|
||||
- `GET /api/layouts/{layoutId}/levels/{levelId}/data` - Get layout data
|
||||
- `PUT /api/layouts/{layoutId}/levels/{levelId}` - Update level (including editor settings)
|
||||
- `GET /api/layouts/{layoutId}/levels/{levelId}/background-image` - Get background image
|
||||
- `POST /api/edges` - Create edge (with smart node detection)
|
||||
- `DELETE /api/edges/{id}` - Delete edge (single)
|
||||
- `DELETE /api/edges/batch` - Delete edges (batch)
|
||||
- `POST /api/layout-data/save` - Batch save nodes/edges (transactional)
|
||||
- `POST /api/layout-data/copy-nodes` - Copy nodes/edges with offset
|
||||
- `POST /api/layout-data/merge-nodes` - Merge multiple nodes into one
|
||||
- `POST /api/layout-data/split-node` - Split one node into multiple nodes
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Technical Details
|
||||
|
||||
### **Coordinate System**
|
||||
- **World Coordinates:** Bottom-left origin (0,0), Y increases upward, units in meters
|
||||
- **SVG Coordinates:** Top-left origin (0,0), Y increases downward, units in pixels
|
||||
- **Transform:** `svgY = physicalHeight - worldY` (flip Y axis)
|
||||
|
||||
### **Pan Logic (Fixed)**
|
||||
- Sử dụng incremental delta thay vì delta từ điểm bắt đầu
|
||||
- Lưu `panLastScreen` để track vị trí trước đó
|
||||
- Convert screen coordinates sang SVG mỗi lần để đảm bảo chính xác khi ViewBox thay đổi
|
||||
|
||||
### **Zoom Logic (Fixed)**
|
||||
- Zoom tại vị trí cursor
|
||||
- Giữ tọa độ mouse không đổi trong world space
|
||||
- Tính ratio của cursor trong viewBox và adjust ViewBox position
|
||||
|
||||
### **Grid Styling**
|
||||
- Stroke: `#999` (đậm)
|
||||
- Stroke width: `0.04` (đậm gấp đôi)
|
||||
- Opacity: `0.7` (rõ)
|
||||
- Stroke dasharray: `0.1,0.1` (nét đứt)
|
||||
|
||||
### **Text Rendering (Node/Edge Names)**
|
||||
- **Font Size Formula:** `fontSizeSVG = baseFontSizeWorld / (Resolution * ZoomLevel)`
|
||||
- `baseFontSizeWorld`: 0.24 meters
|
||||
- `Resolution`: meters per pixel (from `EditorSettings.Resolution`)
|
||||
- `ZoomLevel`: current zoom factor
|
||||
- **Result:** Text có cùng kích thước visual với mọi Resolution, zoom theo ZoomLevel
|
||||
- **Color:** Red (#f44336)
|
||||
- **Font:** Segoe UI, font-weight 500
|
||||
- **Letter Spacing:** -0.02em (characters gần nhau hơn)
|
||||
- **Position:**
|
||||
- Node names: Below node center
|
||||
- Edge names: Above edge center
|
||||
|
||||
### **Settings Tab Structure**
|
||||
1. Grid Settings (editable)
|
||||
2. Display Options (session state)
|
||||
3. Auto-generation Settings (editable với Save button)
|
||||
4. Layout Level Info (read-only)
|
||||
5. Statistics (read-only)
|
||||
|
||||
---
|
||||
|
||||
## 📊 Implementation Status Summary
|
||||
|
||||
| Phase | Status | Progress |
|
||||
|-------|--------|----------|
|
||||
| Phase 1: Foundation | ✅ Complete | 100% |
|
||||
| Phase 2: SVG Canvas | ✅ Complete | 100% |
|
||||
| Phase 3: Viewport Controls | ✅ Complete | 100% |
|
||||
| Phase 4: Selection | ⏳ Partial | ~80% |
|
||||
| Phase 5: Create Edge | ✅ Complete | 100% |
|
||||
| Phase 6: Edit Operations | ⏳ Partial | ~70% |
|
||||
| Phase 7: Properties | ⏳ Partial | ~70% |
|
||||
| Phase 8: Trajectory Editor | ❌ Not Started | 0% |
|
||||
| Phase 9: Actions Form Builder | ❌ Not Started | 0% |
|
||||
| Phase 10: Undo/Redo & Save | ⏳ Partial | ~70% |
|
||||
| Phase 11: Polish | ⏳ Partial | ~60% |
|
||||
|
||||
**Overall Progress:** ~65-70%
|
||||
|
||||
---
|
||||
|
||||
## 🐛 Bugs Fixed in This Session
|
||||
|
||||
### **1. Pan Logic - Cộng Dồn Delta**
|
||||
**Problem:** Pan bị cộng dồn quãng đường di chuyển
|
||||
**Root Cause:** Tính delta từ điểm bắt đầu mỗi lần thay vì incremental
|
||||
**Solution:** Chuyển sang incremental delta từ lần move trước
|
||||
|
||||
### **2. Zoom Logic - Tọa Độ Mouse Thay Đổi**
|
||||
**Problem:** Zoom không giữ tọa độ mouse không đổi
|
||||
**Solution:** Tính ratio và adjust ViewBox để giữ cursor tại cùng vị trí world
|
||||
|
||||
### **3. Grid Styling**
|
||||
**Problem:** Grid quá nhạt và nét liền
|
||||
**Solution:** Đậm hơn, opacity cao hơn, thêm stroke-dasharray
|
||||
|
||||
### **4. Settings Tab - Display Options**
|
||||
**Problem:** Display Options ở Toolbar thay vì Settings
|
||||
**Solution:** Chuyển về Settings Tab, xóa khỏi Toolbar
|
||||
|
||||
### **5. Settings Tab - Auto-generation Settings Read-only**
|
||||
**Problem:** Auto-generation Settings chỉ hiển thị, không thể edit
|
||||
**Solution:** Làm editable với local state và Save button, tích hợp API
|
||||
|
||||
### **6. Create Edge - Smart Node Detection**
|
||||
**Problem:** CreateEdge không hoạt động, chỉ tạo được trên nodes đã có
|
||||
**Solution:**
|
||||
- Backend API tự động detect nodes trong `NodeProximityRadius` hoặc tạo mới
|
||||
- Frontend chỉ cần gửi world coordinates, backend xử lý logic
|
||||
|
||||
### **7. 2-Way Edges Visualization**
|
||||
**Problem:** 2-way edges hiển thị thành 1 line với 2 arrowheads
|
||||
**Solution:** Tính toán offset để hiển thị thành 2 đường song song riêng biệt
|
||||
|
||||
### **8. Text Font Size - Resolution Independence**
|
||||
**Problem:** Text size thay đổi khi Resolution thay đổi
|
||||
**Solution:** Công thức `fontSizeSVG = baseFontSizeWorld / (Resolution * ZoomLevel)` để text có cùng kích thước visual với mọi Resolution
|
||||
|
||||
### **9. HasUnsavedChanges - Direct API Calls**
|
||||
**Problem:** `HasUnsavedChanges` được set sau các API calls trực tiếp (CreateEdge, DeleteEdge, etc.)
|
||||
**Solution:** Xóa `HasUnsavedChanges = true` sau các API calls trực tiếp vì đã lưu vào database rồi
|
||||
|
||||
### **10. CopyNodesAsync - Refactor**
|
||||
**Problem:** Copy logic phức tạp ở frontend, dùng temp edges
|
||||
**Solution:**
|
||||
- Refactor để backend xử lý toàn bộ logic
|
||||
- Tạo nodes trực tiếp vào database (validate coordinates, copy vehicle properties)
|
||||
- Tạo edges trực tiếp với node ID mapping
|
||||
- Xử lý 2-way edges tự động
|
||||
|
||||
---
|
||||
|
||||
## 📝 Key Files Modified in This Session
|
||||
|
||||
### **Components:**
|
||||
- `Components/LayoutEditor/RightPanel/SettingsTab.razor` - Editable settings với Save
|
||||
- `Components/LayoutEditor/SvgEditorCanvas.razor` - Fixed pan/zoom logic, CreateEdge, Copy preview, text rendering
|
||||
- `Components/LayoutEditor/EditorToolbar.razor` - Removed Display Options, added Move mode button
|
||||
|
||||
### **Services:**
|
||||
- `Services/State/LayoutEditorState.cs` - Fixed zoom logic, CreateEdgeAsync, DeleteEdgesAsync, CompleteCopyAsync, MergeNodesAsync, SplitNodeAsync, SaveAsync, change tracking
|
||||
|
||||
### **JavaScript:**
|
||||
- `wwwroot/js/svgEditor.js` - Added screenToSvgArray export, screen coordinates, Ctrl+C/M handlers
|
||||
|
||||
### **Shared Models:**
|
||||
- `RobotNet10.MapEditor.Shared/Models/EditorSettingsInfo.cs` - New model
|
||||
- `RobotNet10.MapEditor.Shared/DTOs/Requests/UpdateLayoutLevelRequest.cs` - Added EditorSettings
|
||||
- `RobotNet10.MapEditor.Shared/DTOs/Requests/SaveLayoutDataRequest.cs` - New model
|
||||
- `RobotNet10.MapEditor.Shared/DTOs/Responses/SaveLayoutDataResponse.cs` - New model
|
||||
- `RobotNet10.MapEditor.Shared/DTOs/Requests/CopyNodesRequest.cs` - New model
|
||||
- `RobotNet10.MapEditor.Shared/DTOs/Responses/CopyNodesResponse.cs` - New model
|
||||
- `RobotNet10.MapEditor.Shared/DTOs/Requests/MergeNodesRequest.cs` - New model
|
||||
- `RobotNet10.MapEditor.Shared/DTOs/Requests/SplitNodeRequest.cs` - New model
|
||||
|
||||
### **Backend:**
|
||||
- `Commons/RobotNet10.MapManager/Services/LayoutService.cs` - Update editor settings logic
|
||||
- `Commons/RobotNet10.MapManager/Services/LayoutDataService.cs` - SaveLayoutDataAsync, CopyNodesAsync (refactored), MergeNodesAsync, SplitNodeAsync
|
||||
- `Commons/RobotNet10.MapManager/Controllers/LayoutDataController.cs` - New endpoints: save, copy-nodes, merge-nodes, split-node
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Next Steps
|
||||
|
||||
### **Immediate (Phase 3 completion):**
|
||||
- [ ] Test pan/zoom thoroughly
|
||||
- [ ] Verify no accumulation issues
|
||||
- [ ] Performance testing
|
||||
|
||||
### **Short-term (Phase 4-6):**
|
||||
- [x] Enhance selection features (Shift+Click multi-select, box select edges)
|
||||
- [x] Implement Create Edge với API
|
||||
- [x] Implement Edit Operations (Move, Copy, Delete, Merge, Split)
|
||||
- [ ] Implement Alignment functions (6 directions)
|
||||
|
||||
### **Medium-term (Phase 7-9):**
|
||||
- [x] Properties save to API (via Save button - batch save)
|
||||
- [ ] Trajectory Editor (NURBS)
|
||||
- [ ] Actions Form Builder
|
||||
|
||||
### **Long-term (Phase 10-11):**
|
||||
- [x] Save API với transaction và change tracking
|
||||
- [ ] Complete Undo/Redo cho tất cả operations
|
||||
- [ ] CheckLayout validation
|
||||
- [ ] Performance optimization
|
||||
- [ ] Additional polish
|
||||
|
||||
---
|
||||
|
||||
## 📚 References
|
||||
|
||||
- **Database Design:** `DATABASE_DESIGN_DISCUSSION.md`
|
||||
- **API Guide:** `API_IMPLEMENTATION_GUIDE.md`
|
||||
- **LayoutManager Implementation:** `LAYOUTMANAGER_TECHNICAL.md` (đã lưu trước đó)
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2024-12-XX
|
||||
**Session Focus:** CreateEdge, Copy/Move/Delete/Merge/Split operations, Save API, Text rendering with Resolution independence, CopyNodesAsync refactor
|
||||
|
||||
Reference in New Issue
Block a user