docs: add overview of overworld entity system and refactor plans

- Introduced a new document detailing the overworld entity system, including its architecture, core components, and critical bug fixes.
- Outlined the ongoing refactor plan aimed at modularizing the system for improved maintainability and compatibility with ZScream features.
- Documented specific bug fixes related to entity interaction, property management, and coordinate systems to enhance functionality.

Benefits:
- Provides a comprehensive reference for developers working on the overworld entity system.
- Facilitates understanding of recent changes and future development directions.
This commit is contained in:
scawful
2025-10-18 15:39:42 -04:00
parent d2e35dc645
commit 8e86c1bbdf
3 changed files with 284 additions and 462 deletions

View File

@@ -1,70 +1,171 @@
# Canvas System Overview
# Canvas System Guide
## Canvas Architecture
- **Canvas States**: track `canvas`, `content`, and `draw` rectangles independently; expose size/scale through `CanvasState` inspection panel
- **Layer Stack**: background ➝ bitmaps ➝ entity overlays ➝ selection/tooltip layers
- **Interaction Modes**: Tile Paint, Tile Select, Rectangle Select, Entity Manipulation, Palette Editing, Diagnostics
- **Context Menu**: persistent menu with material icon sections (Mode, View, Info, Bitmap, Palette, BPP, Performance, Layout, Custom)
This guide provides a comprehensive overview of the `yaze` canvas system, its architecture, and best practices for integration. It reflects the state of the system after the October 2025 refactoring.
## Core API Patterns
- Modern usage: `Begin/End` (auto grid/overlay, persistent context menu)
- Legacy helpers still available (`DrawBackground`, `DrawGrid`, `DrawSelectRect`, etc.)
- Unified state snapshot: `CanvasState` exposes geometry, zoom, scroll
- Interaction handler manages mode-specific tools (tile brush, select rect, entity gizmo)
## 1. Architecture
## Context Menu Sections
- **Mode Selector**: switch modes with icons (Brush, Select, Rect, Bitmap, Palette, BPP, Perf)
- **View & Grid**: reset/zoom, toggle grid/labels, advanced/scaling dialogs
- **Canvas Info**: real-time canvas/content size, scale, scroll, mouse position
- **Bitmap/Palette/BPP**: format conversion, palette analysis, BPP workflows with persistent modals
- **Performance**: profiler metrics, dashboard, usage report
- **Layout**: draggable toggle, auto resize, grid step
- **Custom Actions**: consumer-provided menu items
The canvas system was refactored from a monolithic class into a modular, component-based architecture. The main `gui::Canvas` class now acts as a façade, coordinating a set of single-responsibility components and free functions.
## Interaction Modes & Capabilities
- **Tile Painting**: tile16 painter, brush size, finish stroke callbacks
- Operations: finish_paint, reset_view, zoom, grid, scaling
- **Tile Selection**: multi-select rectangle, copy/paste selection
- Operations: select_all, clear_selection, reset_view, zoom, grid, scaling
- **Rectangle Selection**: drag-select area, clear selection
- Operations: clear_selection, reset_view, zoom, grid, scaling
- **Bitmap Editing**: format conversion, bitmap manipulation
- Operations: bitmap_convert, palette_edit, bpp_analysis, reset_view, zoom, grid, scaling
- **Palette Editing**: inline palette editor, ROM palette picker, color analysis
- Operations: palette_edit, palette_analysis, reset_palette, reset_view, zoom, grid, scaling
- **BPP Conversion**: format analysis, conversion workflows
- Operations: bpp_analysis, bpp_conversion, bitmap_convert, reset_view, zoom, grid, scaling
- **Performance Mode**: diagnostics, texture queue, performance overlays
- Operations: performance, usage_report, copy_metrics, reset_view, zoom, grid, scaling
### Core Principles
- **Modular Components**: Logic is broken down into smaller, testable units (e.g., state, rendering, interaction, menus).
- **Data-Oriented Design**: Plain-old-data (POD) structs like `CanvasState` and `CanvasConfig` hold state, which is operated on by free functions.
- **Backward Compatibility**: The refactor was designed to be 100% backward compatible. Legacy APIs still function, but new patterns are encouraged.
- **Editor Agnostic**: The core canvas system has no knowledge of `zelda3` specifics, making it reusable for any editor.
## Debug & Diagnostics
- Persistent modals (`View→Advanced`, `View→Scaling`, `Palette`, `BPP`) stay open until closed
- Texture inspector shows current bitmap, VRAM sheet, palette group, usage stats
- State overlay: canvas size, content size, global scale, scroll, highlight entity
- Performance HUD: operation counts, timing graphs, usage recommendations
### Code Organization
The majority of the canvas code resides in `src/app/gui/canvas/`.
```
src/app/gui/canvas/
├── canvas.h/cc # Main Canvas class (facade)
├── canvas_state.h # POD state structs
├── canvas_config.h # Unified configuration struct
├── canvas_geometry.h/cc # Geometry calculation helpers
├── canvas_rendering.h/cc # Rendering free functions
├── canvas_events.h # Interaction event structs
├── canvas_interaction.h/cc # Interaction event handlers
├── canvas_menu.h/cc # Declarative menu structures
├── canvas_menu_builder.h/cc # Fluent API for building menus
├── canvas_popup.h/cc # PopupRegistry for persistent popups
└── canvas_utils.h/cc # General utility functions
```
## Automation API
- CanvasAutomationAPI: tile operations (`SetTileAt`, `SelectRect`), view control (`ScrollToTile`, `SetZoom`), entity manipulation hooks
- Exposed through CLI (`z3ed`) and gRPC service, matching UI modes
## 2. Core Concepts
## Integration Steps for Editors
1. Construct `Canvas`, set renderer (optional) and ID
2. Call `InitializePaletteEditor` and `SetUsageMode`
3. Configure available modes: `SetAvailableModes({kTilePainting, kTileSelecting})`
4. Register mode callbacks (tile paint finish, selection clear, etc.)
5. During frame: `canvas.Begin(size)` → draw bitmaps/entities → `canvas.End()`
6. Provide custom menu items via `AddMenuItem`/`AddMenuItem(item, usage)`
7. Use `GetConfig()`/`GetSelection()` for state; respond to context menu commands via callback lambda in `Render`
### Configuration (`CanvasConfig`)
- A single, unified `gui::CanvasConfig` struct (defined in `canvas_config.h`) holds all configuration for a canvas instance.
- This includes display settings (grid, labels), sizing, scaling, and usage mode.
- This replaces duplicated config structs from previous versions.
## Migration Checklist
- Replace direct `DrawContextMenu` logic with new render callback signature
- Move palette/BPP helpers into `canvas/` module; update includes
- Ensure persistent modals wired (advanced/scaling/palette/bpp/perf)
- Update usage tracker integrations to record mode switches
- Validate overworld/tile16/dungeon editors in tile paint, select, entity modes
### State (`CanvasState`)
- A POD struct (`canvas_state.h`) that holds the dynamic state of the canvas, including geometry, zoom, and scroll.
- Editors can inspect this state for custom rendering and logic.
## Testing Notes
- Manual regression: overworld paint/select, tile16 painter, dungeon entity drag
- Verify context menu persists and modals remain until closed
- Ensure palette/BPP modals populate with correct bitmap/palette data
- Automation: run CanvasAutomation API tests/end-to-end scripts for overworld edits
### Coordinate Systems
The canvas operates with three distinct coordinate spaces. Using the correct one is critical to avoid bugs.
1. **Screen Space**: Absolute pixel coordinates on the monitor (from `ImGui::GetIO().MousePos`). **Never use this for canvas logic.**
2. **Canvas/World Space**: Coordinates relative to the canvas's content, accounting for scrolling and panning. Use `Canvas::hover_mouse_pos()` to get this. This is the correct space for entity positioning and high-level calculations.
3. **Tile/Grid Space**: Coordinates in tile units. Use `Canvas::CanvasToTile()` to convert from world space.
A critical fix was made to ensure `Canvas::hover_mouse_pos()` is updated continuously whenever the canvas is hovered, decoupling it from specific actions like painting.
## 3. Interaction System
The canvas supports several interaction modes, managed via the `CanvasUsage` enum.
### Interaction Modes
- `kTilePainting`: For painting tiles onto a tilemap.
- `kTileSelection`: For selecting one or more tiles.
- `kRectangleSelection`: For drag-selecting a rectangular area.
- `kEntityManipulation`: For moving and interacting with entities.
- `kPaletteEditing`: For palette-related work.
- `kDiagnostics`: For performance and debug overlays.
Set the mode using `canvas.SetUsageMode(gui::CanvasUsage::kTilePainting)`. This ensures the context menu and interaction handlers behave correctly.
### Event-Driven Model
Interaction logic is moving towards an event-driven model. Instead of inspecting canvas state directly, editors should handle events returned by interaction functions.
**Example**:
```cpp
RectSelectionEvent event = HandleRectangleSelection(geometry, ...);
if (event.is_complete) {
// Process the selection event
}
```
## 4. Context Menu & Popups
The context menu system is now unified, data-driven, and supports persistent popups.
### Key Features
- **Unified Item Definition**: All menu items use the `gui::CanvasMenuItem` struct.
- **Priority-Based Ordering**: Menu sections are automatically sorted based on the `MenuSectionPriority` enum, ensuring a consistent layout:
1. `kEditorSpecific` (highest priority)
2. `kBitmapOperations`
3. `kCanvasProperties`
4. `kDebug` (lowest priority)
- **Automatic Popup Persistence**: Popups defined declaratively will remain open until explicitly closed by the user (ESC or close button), rather than closing on any click outside.
- **Fluent Builder API**: The `gui::CanvasMenuBuilder` provides a clean, chainable API for constructing complex menus.
### API Patterns
**Add a Simple Menu Item**:
```cpp
canvas.AddContextMenuItem(
gui::CanvasMenuItem("Label", ICON_MD_ICON, []() { /* Action */ })
);
```
**Add a Declarative Popup Item**:
This pattern automatically handles popup registration and persistence.
```cpp
auto item = gui::CanvasMenuItem::WithPopup(
"Properties",
"props_popup_id",
[]() {
// Render popup content here
ImGui::Text("My Properties");
}
);
canvas.AddContextMenuItem(item);
```
**Build a Complex Menu with the Builder**:
```cpp
gui::CanvasMenuBuilder builder;
canvas.editor_menu() = builder
.BeginSection("Editor Actions", gui::MenuSectionPriority::kEditorSpecific)
.AddItem("Cut", ICON_MD_CUT, []() { Cut(); })
.AddPopupItem("Settings", "settings_popup", []() { RenderSettings(); })
.EndSection()
.Build();
```
## 5. Entity System
A generic, Zelda-agnostic entity system allows editors to manage on-canvas objects.
- **Flat Functions**: Entity creation logic is handled by pure functions in `src/app/editor/overworld/operations/entity_operations.h`, such as `InsertEntrance`, `InsertSprite`, etc. These functions are designed for ZScream feature parity.
- **Delegation Pattern**: The `OverworldEditor` delegates to the `MapPropertiesSystem`, which in turn calls these flat functions to modify the ROM state.
- **Mode-Aware Menu**: The "Insert Entity" context submenu is only available when the canvas is in `kEntityManipulation` mode.
**Usage Flow**:
1. Set canvas mode to `kEntityManipulation`.
2. Right-click on the canvas to open the context menu.
3. Select "Insert Entity" and choose the entity type.
4. The appropriate callback is fired, which calls the corresponding `Insert...` function.
5. A popup appears to configure the new entity's properties.
## 6. Integration Guide for Editors
1. **Construct `Canvas`**: Instantiate `gui::Canvas`, providing a unique ID.
2. **Configure**: Set the desired `CanvasUsage` mode via `canvas.SetUsageMode()`. Configure available modes and other options in the `CanvasConfig` struct.
3. **Register Callbacks**: If using interaction modes like tile painting, register callbacks for events like `finish_paint`.
4. **Render Loop**:
- Call `canvas.Begin(size)`.
- Draw your editor-specific content (bitmaps, entities, overlays).
- Call `canvas.End()`. This handles rendering the grid, overlays, and the context menu.
5. **Provide Custom Menus**: Use `canvas.AddContextMenuItem()` or the `CanvasMenuBuilder` to add editor-specific actions to the context menu. Assign the `kEditorSpecific` priority to ensure they appear at the top.
6. **Handle State**: Respond to user interactions by inspecting the `CanvasState` or handling events returned from interaction helpers.
## 7. Debugging
If you encounter issues with the canvas, check the following:
- **Context Menu Doesn't Appear**:
- Is `config.enable_context_menu` true?
- Is the mouse button right-click?
- Is the canvas focused and not being dragged?
- **Popup Doesn't Persist**:
- Are you using the `CanvasMenuItem::WithPopup` pattern?
- Is `canvas.End()` being called every frame to allow the `PopupRegistry` to render?
- **Incorrect Coordinates**:
- Are you using `canvas.hover_mouse_pos()` for world coordinates instead of `ImGui::GetIO().MousePos`?
- Verify that you are correctly converting between world space and tile space.
- **Menu Items in Wrong Order**:
- Have you set the correct `MenuSectionPriority` for your custom menu sections?
## 8. Automation API
The `CanvasAutomationAPI` provides hooks for testing and automation. It allows for programmatic control of tile operations (`SetTileAt`, `SelectRect`), view controls (`ScrollToTile`, `SetZoom`), and entity manipulation. This API is exposed via the `z3ed` CLI and a gRPC service.