backend-infra-engineer: Post v0.3.9-hotfix7 snapshot (build cleanup)

This commit is contained in:
scawful
2025-12-22 00:20:49 +00:00
parent 2934c82b75
commit 5c4cd57ff8
1259 changed files with 239160 additions and 43801 deletions

View File

@@ -0,0 +1,405 @@
# Object Selection System - Interaction Flow
## Visual Flow Diagrams
### 1. Single Object Selection (Left Click)
```
┌─────────────────────────────────────────────────────┐
│ User Input: Left Click on Object │
└────────────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ DungeonObjectInteraction::HandleCanvasMouseInput() │
│ - Detect left click │
│ - Get mouse position │
│ - Convert to room coordinates │
└────────────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ TrySelectObjectAtCursor(x, y, mode) │
│ - Iterate objects in reverse order │
│ - Check if cursor within object bounds │
│ - Find topmost object at cursor │
└────────────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ ObjectSelection::SelectObject(index, Single) │
│ - Clear previous selection │
│ - Add object to selection (set.insert) │
│ - Trigger selection changed callback │
└────────────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ Visual Feedback │
│ - Draw pulsing border (yellow-gold, 0.85f alpha) │
│ - Draw corner handles (cyan-white, 0.85f alpha) │
│ - Animate pulse at 4 Hz │
└─────────────────────────────────────────────────────┘
```
### 2. Multi-Selection (Shift+Click)
```
┌─────────────────────────────────────────────────────┐
│ User Input: Shift + Left Click │
└────────────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ HandleCanvasMouseInput() │
│ - Detect Shift key down │
│ - Set mode = SelectionMode::Add │
└────────────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ ObjectSelection::SelectObject(index, Add) │
│ - Keep existing selection │
│ - Add new object (set.insert) │
│ - Trigger callback │
└────────────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ Visual Feedback │
│ - Highlight ALL selected objects │
│ - Each with pulsing border + handles │
└─────────────────────────────────────────────────────┘
```
### 3. Toggle Selection (Ctrl+Click)
```
┌─────────────────────────────────────────────────────┐
│ User Input: Ctrl + Left Click │
└────────────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ HandleCanvasMouseInput() │
│ - Detect Ctrl key down │
│ - Set mode = SelectionMode::Toggle │
└────────────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ ObjectSelection::SelectObject(index, Toggle) │
│ - If selected: Remove (set.erase) │
│ - If not selected: Add (set.insert) │
│ - Trigger callback │
└────────────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ Visual Feedback │
│ - Update highlights for current selection │
│ - Removed objects no longer highlighted │
└─────────────────────────────────────────────────────┘
```
### 4. Rectangle Selection (Right Click + Drag)
```
┌─────────────────────────────────────────────────────┐
│ User Input: Right Click + Drag │
└────────────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ Phase 1: Mouse Down (Right Button) │
│ ObjectSelection::BeginRectangleSelection(x, y) │
│ - Store start position │
│ - Set rectangle_selection_active = true │
└────────────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ Phase 2: Mouse Drag │
│ ObjectSelection::UpdateRectangleSelection(x, y) │
│ - Update end position │
│ - Draw rectangle preview │
│ • Border: accent_color @ 0.85f alpha │
│ • Fill: accent_color @ 0.15f alpha │
└────────────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ Phase 3: Mouse Release │
│ ObjectSelection::EndRectangleSelection(objects) │
│ - Convert canvas coords to room coords │
│ - For each object: │
│ • Get object bounds │
│ • Check AABB intersection with rectangle │
│ • If intersects: Add to selection │
│ - Set rectangle_selection_active = false │
│ - Trigger callback │
└────────────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ Visual Feedback │
│ - Highlight all selected objects │
│ - Remove rectangle preview │
└─────────────────────────────────────────────────────┘
```
### 5. Select All (Ctrl+A)
```
┌─────────────────────────────────────────────────────┐
│ User Input: Ctrl + A │
└────────────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ HandleCanvasMouseInput() │
│ - Detect Ctrl + A key combination │
│ - Get current room object count │
└────────────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ ObjectSelection::SelectAll(object_count) │
│ - Clear previous selection │
│ - Add all object indices (0..count-1) │
│ - Trigger callback │
└────────────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ Visual Feedback │
│ - Highlight ALL objects in room │
│ - May cause performance impact if many objects │
└─────────────────────────────────────────────────────┘
```
## State Transitions
```
┌────────────┐
│ No │◄─────────────────────┐
│ Selection │ │
└──────┬─────┘ │
│ │
│ Left Click │ Esc or Clear
▼ │
┌────────────┐ │
│ Single │◄─────────┐ │
│ Selection │ │ │
└──────┬─────┘ │ │
│ │ │
│ Shift+Click │ Ctrl+Click│
│ Right+Drag │ (deselect)│
▼ │ │
┌────────────┐ │ │
│ Multi │──────────┘ │
│ Selection │──────────────────────┘
└────────────┘
```
## Rendering Pipeline
```
┌─────────────────────────────────────────────────────┐
│ DungeonCanvasViewer::DrawDungeonCanvas(room_id) │
└────────────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ 1. Draw Room Background Layers (BG1, BG2) │
│ - Load room graphics │
│ - Render to bitmaps │
│ - Draw bitmaps to canvas │
└────────────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ 2. Draw Sprites │
│ - Render sprite markers (8x8 squares) │
│ - Color-code by layer │
└────────────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ 3. Handle Object Interaction │
│ DungeonObjectInteraction::HandleCanvasMouseInput()│
│ - Process mouse/keyboard input │
│ - Update selection state │
└────────────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ 4. Draw Selection Visuals (TOP LAYER) │
│ ObjectSelection::DrawSelectionHighlights() │
│ - For each selected object: │
│ • Convert room coords to canvas coords │
│ • Apply canvas scale │
│ • Draw pulsing border │
│ • Draw corner handles │
│ ObjectSelection::DrawRectangleSelectionBox() │
│ - If active: Draw rectangle preview │
└────────────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ 5. Draw Canvas Overlays │
│ - Grid lines │
│ - Debug overlays (if enabled) │
└─────────────────────────────────────────────────────┘
```
## Data Flow for Object Operations
### Delete Selected Objects
```
┌─────────────────────────────────────────────────────┐
│ User Input: Delete Key │
└────────────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ DungeonObjectInteraction::HandleDeleteSelected() │
│ 1. Get selected indices from ObjectSelection │
│ 2. Sort indices in descending order │
│ 3. For each index (high to low): │
│ - Call room.RemoveTileObject(index) │
│ 4. Clear selection │
│ 5. Trigger cache invalidation (re-render) │
└────────────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ Room::RenderRoomGraphics() │
│ - Re-render room with deleted objects removed │
└─────────────────────────────────────────────────────┘
```
### Copy/Paste Selected Objects
```
Copy Flow:
┌─────────────────────────────────────────────────────┐
│ User Input: Ctrl+C │
└────────────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ DungeonObjectInteraction::HandleCopySelected() │
│ 1. Get selected indices from ObjectSelection │
│ 2. Copy objects to clipboard_ vector │
│ 3. Set has_clipboard_data_ = true │
└─────────────────────────────────────────────────────┘
Paste Flow:
┌─────────────────────────────────────────────────────┐
│ User Input: Ctrl+V │
└────────────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ DungeonObjectInteraction::HandlePasteObjects() │
│ 1. Get mouse position │
│ 2. Calculate offset from first clipboard object │
│ 3. For each clipboard object: │
│ - Create copy with offset position │
│ - Clamp to room bounds (0-63) │
│ - Add to room │
│ 4. Trigger re-render │
└─────────────────────────────────────────────────────┘
```
## Performance Considerations
### Selection State Storage
```
std::set<size_t> selected_indices_;
Advantages:
✓ O(log n) insert/delete/lookup
✓ Automatic sorting
✓ No duplicates
✓ Cache-friendly for small selections
Trade-offs:
✗ Slightly higher memory overhead
✗ Not as cache-friendly for iteration (vs vector)
Decision: Justified for correctness guarantees
```
### Rendering Optimization
```
void DrawSelectionHighlights() {
if (selected_indices_.empty()) {
return; // Early exit - O(1)
}
// Only render visible objects (canvas culling)
for (size_t index : selected_indices_) {
if (IsObjectVisible(index)) {
DrawHighlight(index); // O(k) where k = selected count
}
}
}
Complexity: O(k) where k = selected object count
Typical case: k < 20 objects selected
Worst case: k = 296 (all objects) - rare
```
## Memory Layout
```
ObjectSelection Instance (~64 bytes)
├── selected_indices_ (std::set<size_t>)
│ └── Red-Black Tree
│ ├── Node overhead: ~32 bytes per node
│ └── Typical selection: 5 objects = ~160 bytes
├── rectangle_selection_active_ (bool) = 1 byte
├── rect_start_x_ (int) = 4 bytes
├── rect_start_y_ (int) = 4 bytes
├── rect_end_x_ (int) = 4 bytes
├── rect_end_y_ (int) = 4 bytes
└── selection_changed_callback_ (std::function) = 32 bytes
Total: ~64 bytes + (32 bytes × selected_count)
Example: 10 objects selected = ~384 bytes
Negligible compared to room graphics (~2MB)
```
## Integration Checklist
When integrating ObjectSelection into DungeonObjectInteraction:
- [ ] Add `ObjectSelection selection_;` member
- [ ] Remove old selection state variables
- [ ] Update `HandleCanvasMouseInput()` to use selection modes
- [ ] Add `TrySelectObjectAtCursor()` helper
- [ ] Update `DrawSelectionHighlights()` to delegate to ObjectSelection
- [ ] Update `DrawSelectBox()` to delegate to ObjectSelection
- [ ] Update `HandleDeleteSelected()` to use `selection_.GetSelectedIndices()`
- [ ] Update `HandleCopySelected()` to use `selection_.GetSelectedIndices()`
- [ ] Update clipboard operations
- [ ] Add Ctrl+A handler for select all
- [ ] Test single selection
- [ ] Test multi-selection (Shift+click)
- [ ] Test toggle selection (Ctrl+click)
- [ ] Test rectangle selection
- [ ] Test select all (Ctrl+A)
- [ ] Test copy/paste/delete operations
- [ ] Verify visual feedback (borders, handles)
- [ ] Verify theme color usage
- [ ] Run unit tests
- [ ] Test performance with many objects
---
**Diagram Format**: ASCII art compatible with markdown viewers
**Last Updated**: 2025-11-26