Files
yaze/docs/internal/architecture/object_selection_flow.md

406 lines
24 KiB
Markdown
Raw 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.
# 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