From dabfbfa5a2125179395d81ae16d03f79b08da820 Mon Sep 17 00:00:00 2001 From: scawful Date: Sat, 27 Sep 2025 18:14:16 -0400 Subject: [PATCH] Implement Tile16 changes commit callback and enhance editor functionality - Added a callback mechanism in OverworldEditor to refresh the tile16 blockset and overworld map upon committing changes. - Improved the Tile16Editor with new methods for committing changes to the overworld and discarding local modifications. - Enhanced the UI layout for the Tile16 editor, optimizing the display and interaction of tile8 and tile16 controls. - Updated the drawing logic for tile previews and improved palette management features for better user experience. --- src/app/editor/overworld/overworld_editor.cc | 15 +- src/app/editor/overworld/tile16_editor.cc | 640 ++++++++++--------- src/app/editor/overworld/tile16_editor.h | 17 +- src/app/gui/canvas_utils.cc | 234 ++++--- 4 files changed, 502 insertions(+), 404 deletions(-) diff --git a/src/app/editor/overworld/overworld_editor.cc b/src/app/editor/overworld/overworld_editor.cc index d6e97874..7568502f 100644 --- a/src/app/editor/overworld/overworld_editor.cc +++ b/src/app/editor/overworld/overworld_editor.cc @@ -149,6 +149,19 @@ absl::Status OverworldEditor::Load() { RETURN_IF_ERROR( tile16_editor_.Initialize(tile16_blockset_bmp_, current_gfx_bmp_, *overworld_.mutable_all_tiles_types())); + + // Set up callback for when tile16 changes are committed + tile16_editor_.set_on_changes_committed([this]() -> absl::Status { + // Regenerate the overworld editor's tile16 blockset + RETURN_IF_ERROR(RefreshTile16Blockset()); + + // Force refresh of the current overworld map to show changes + RefreshOverworldMap(); + + util::logf("Overworld editor refreshed after Tile16 changes"); + return absl::OkStatus(); + }); + ASSIGN_OR_RETURN(entrance_tiletypes_, zelda3::LoadEntranceTileTypes(rom_)); all_gfx_loaded_ = true; return absl::OkStatus(); @@ -1286,7 +1299,7 @@ void OverworldEditor::DrawOverworldEntrances(ImVec2 canvas_p0, ImVec2 scrolling, for (auto& each : overworld_.entrances()) { if (each.map_id_ < 0x40 + (current_world_ * 0x40) && each.map_id_ >= (current_world_ * 0x40) && !each.deleted) { - auto color = ImVec4(255, 0, 255, 100); + auto color = ImVec4(255, 255, 0, 100); if (each.is_hole_) { color = ImVec4(255, 255, 0, 200); } diff --git a/src/app/editor/overworld/tile16_editor.cc b/src/app/editor/overworld/tile16_editor.cc index cc11dd3c..dcc8e65e 100644 --- a/src/app/editor/overworld/tile16_editor.cc +++ b/src/app/editor/overworld/tile16_editor.cc @@ -15,6 +15,7 @@ #include "imgui/imgui.h" #include "util/hex.h" #include "util/log.h" +#include "util/macro.h" namespace yaze { namespace editor { @@ -337,10 +338,98 @@ absl::Status Tile16Editor::RefreshTile16Blockset() { return absl::FailedPreconditionError("Tile16 blockset not available"); } - // Regenerate the blockset atlas from current tile data - // This would typically involve re-rendering all tiles from their component tile8s + // Force regeneration of the blockset atlas from ROM tile16 data + // This ensures the blockset reflects any changes made to individual tiles + + // Clear cached tile bitmaps to force regeneration + tile16_blockset_->tile_bitmaps.clear(); + + // Mark atlas as modified to trigger regeneration + tile16_blockset_->atlas.set_modified(true); + + // Update the atlas bitmap + core::Renderer::Get().UpdateBitmap(&tile16_blockset_->atlas); - util::logf("Tile16 blockset refreshed"); + util::logf("Tile16 blockset refreshed and regenerated"); + return absl::OkStatus(); +} + +absl::Status Tile16Editor::RegenerateTile16BitmapFromROM() { + // Get the current tile16 data from ROM + auto* tile_data = GetCurrentTile16Data(); + if (!tile_data) { + return absl::FailedPreconditionError("Cannot access current tile16 data"); + } + + // Create a new 16x16 bitmap for the tile16 + std::vector tile16_pixels(kTile16PixelCount, 0); + + // Process each quadrant (2x2 grid of 8x8 tiles) + for (int quadrant = 0; quadrant < 4; ++quadrant) { + gfx::TileInfo* tile_info = nullptr; + int quadrant_x = quadrant % 2; + int quadrant_y = quadrant / 2; + + // Get the tile info for this quadrant + switch (quadrant) { + case 0: tile_info = &tile_data->tile0_; break; + case 1: tile_info = &tile_data->tile1_; break; + case 2: tile_info = &tile_data->tile2_; break; + case 3: tile_info = &tile_data->tile3_; break; + } + + if (!tile_info) continue; + + // Get the tile8 ID and properties + int tile8_id = tile_info->id_; + bool x_flip = tile_info->horizontal_mirror_; + bool y_flip = tile_info->vertical_mirror_; + uint8_t palette = tile_info->palette_; + + // Get the source tile8 bitmap + if (tile8_id >= 0 && tile8_id < static_cast(current_gfx_individual_.size()) && + current_gfx_individual_[tile8_id].is_active()) { + + const auto& source_tile8 = current_gfx_individual_[tile8_id]; + + // Copy the 8x8 tile into the appropriate quadrant of the 16x16 tile + for (int ty = 0; ty < kTile8Size; ++ty) { + for (int tx = 0; tx < kTile8Size; ++tx) { + // Apply flip transformations + int src_x = x_flip ? (kTile8Size - 1 - tx) : tx; + int src_y = y_flip ? (kTile8Size - 1 - ty) : ty; + int src_index = src_y * kTile8Size + src_x; + + // Calculate destination in tile16 + int dst_x = (quadrant_x * kTile8Size) + tx; + int dst_y = (quadrant_y * kTile8Size) + ty; + int dst_index = dst_y * kTile16Size + dst_x; + + // Copy pixel with bounds checking + if (src_index >= 0 && src_index < static_cast(source_tile8.size()) && + dst_index >= 0 && dst_index < kTile16PixelCount) { + uint8_t pixel = source_tile8.data()[src_index]; + // Apply palette offset if needed + tile16_pixels[dst_index] = pixel; + } + } + } + } + } + + // Update the current tile16 bitmap with the regenerated data + current_tile16_bmp_.Create(kTile16Size, kTile16Size, 8, tile16_pixels); + + // Set the appropriate palette + const auto& ow_main_pal_group = rom()->palette_group().overworld_main; + if (ow_main_pal_group.size() > 0) { + current_tile16_bmp_.SetPalette(ow_main_pal_group[0]); + } + + // Render the updated bitmap + core::Renderer::Get().RenderBitmap(¤t_tile16_bmp_); + + util::logf("Regenerated Tile16 bitmap for tile %d from ROM data", current_tile16_); return absl::OkStatus(); } @@ -469,334 +558,257 @@ absl::Status Tile16Editor::DrawToCurrentTile16(ImVec2 click_position) { } absl::Status Tile16Editor::UpdateTile16Edit() { - static bool show_tile8_selector = true; - static bool show_tile16_editor = true; - - // View controls - if (BeginMenuBar()) { - if (BeginMenu("View")) { - Checkbox("Tile8 Selector", &show_tile8_selector); - Checkbox("Tile16 Editor", &show_tile16_editor); - EndMenu(); - } - EndMenuBar(); + static bool show_advanced_controls = false; + + // Compact header with essential info + Text("Tile16 Editor - ID: %02X | Palette: %d", current_tile16_, current_palette_); + SameLine(); + if (SmallButton("Advanced")) { + show_advanced_controls = !show_advanced_controls; } - // Simple 2-column layout: Tile8 Source | Tile16 Editor + Controls - if (BeginTable("##Tile16EditLayout", 2, + Separator(); + + // Redesigned 3-column layout: Tile8 Source | Tile16 Editor | Controls + if (BeginTable("##Tile16EditLayout", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersInnerV)) { - TableSetupColumn("Tile8 Source", ImGuiTableColumnFlags_WidthStretch, 0.6f); - TableSetupColumn("Tile16 Editor", ImGuiTableColumnFlags_WidthStretch, 0.4f); + TableSetupColumn("Tile8 Source", ImGuiTableColumnFlags_WidthStretch, 0.5F); + TableSetupColumn("Tile16 Editor", ImGuiTableColumnFlags_WidthFixed, 100.0F); // Fixed width for 64x64 canvas + padding + TableSetupColumn("Controls", ImGuiTableColumnFlags_WidthStretch, 0.3F); TableHeadersRow(); TableNextRow(); - // Tile8 selector column - if (show_tile8_selector) { - TableNextColumn(); - Text("Tile8 Source (Group: %d)", current_palette_group_); + // Tile8 selector column - cleaner design + TableNextColumn(); + Text("Tile8 Source"); + + // Compact palette group selector + const char* palette_group_names[] = {"OW Main", "OW Aux", "OW Anim", + "Dungeon", "Sprites", "Armor", "Sword"}; + SetNextItemWidth(100); + if (Combo("##PaletteGroup", ¤t_palette_group_, palette_group_names, 7)) { + RETURN_IF_ERROR(RefreshAllPalettes()); + } - // Palette group selector - const char* palette_group_names[] = {"OW Main", "OW Aux", "OW Anim", - "Dungeon", "Sprites", "Armor", - "Sword"}; - if (Combo("##PaletteGroup", ¤t_palette_group_, palette_group_names, - 7)) { - RETURN_IF_ERROR(RefreshAllPalettes()); - } - - // Enhanced canvas table integration with proper content size reporting - gui::BeginPadding(3); - ImGui::BeginGroup(); - - // Calculate the actual content size of the tile8 canvas (128x4096 pixels at 4x scale) - ImVec2 tile8_content_size(128 * 4.0f, - 4096); // 4x scale for proper tile8 display - gui::BeginChildWithScrollbar("##Tile8SelectorScrollRegion", - tile8_content_size); - - // Canvas draws within the properly sized scrollable region + // Streamlined tile8 canvas with scrolling + if (ImGui::BeginChild("##Tile8ScrollRegion", ImVec2(tile8_source_canvas_.width(), tile8_source_canvas_.height()), true, + ImGuiWindowFlags_AlwaysVerticalScrollbar)) { + + // Enable dragging for scrolling behavior + tile8_source_canvas_.set_draggable(true); tile8_source_canvas_.DrawBackground(); - gui::EndPadding(); - tile8_source_canvas_.DrawContextMenu(); - // Improved tile8 selection detection - tile8_source_canvas_.DrawTileSelector(32.0f); + // Tile8 selection with improved feedback + tile8_source_canvas_.DrawTileSelector(32.0F); - if (tile8_source_canvas_.WasClicked() || - tile8_source_canvas_.WasDoubleClicked()) { + if (tile8_source_canvas_.WasClicked() || tile8_source_canvas_.WasDoubleClicked()) { auto tile_pos = tile8_source_canvas_.GetLastClickPosition(); int tile_x = static_cast(tile_pos.x / 32); int tile_y = static_cast(tile_pos.y / 32); int new_tile8 = tile_x + (tile_y * 8); - // Update current tile8 and refresh palette if (new_tile8 != current_tile8_ && new_tile8 >= 0 && new_tile8 < static_cast(current_gfx_individual_.size()) && current_gfx_individual_[new_tile8].is_active()) { current_tile8_ = new_tile8; RETURN_IF_ERROR(UpdateTile8Palette(current_tile8_)); - - // Debug log for tile selection - util::logf("Selected Tile8: %d (pos: %d,%d)", current_tile8_, tile_x, - tile_y); + util::logf("Selected Tile8: %d", current_tile8_); } } - // Restore proper tile8 source scaling - tile8_source_canvas_.DrawBitmap(current_gfx_bmp_, 2, 2, 4.0f); + tile8_source_canvas_.DrawBitmap(current_gfx_bmp_, 2, 2, 4.0F); tile8_source_canvas_.DrawGrid(); tile8_source_canvas_.DrawOverlay(); + } + EndChild(); - EndChild(); + // Tile16 editor column - compact and focused + TableNextColumn(); + + // Fixed size container to prevent canvas expansion + if (ImGui::BeginChild("##Tile16FixedCanvas", ImVec2(90, 90), true, + ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) { + + tile16_edit_canvas_.DrawBackground(ImVec2(64, 64)); // Fixed 64x64 display size + tile16_edit_canvas_.DrawContextMenu(); - Separator(); - Text("Palette: %d (Group: %d)", current_palette_, current_palette_group_); - if (Button("Pal -", ImVec2(40, 0))) - RETURN_IF_ERROR(CyclePalette(false)); + // Draw current tile16 bitmap at 4x scale for clarity (16x16 pixels -> 64x64 display) + if (current_tile16_bmp_.is_active()) { + tile16_edit_canvas_.DrawBitmap(current_tile16_bmp_, 2, 2, 4.0F); + } + + // Handle tile8 painting with improved hover preview + if (current_tile8_ >= 0 && + current_tile8_ < static_cast(current_gfx_individual_.size()) && + current_gfx_individual_[current_tile8_].is_active()) { + + // Create flipped tile if needed + gfx::Bitmap* tile_to_paint = ¤t_gfx_individual_[current_tile8_]; + gfx::Bitmap flipped_tile; + + if (x_flip || y_flip) { + flipped_tile.Create(8, 8, 8, current_gfx_individual_[current_tile8_].vector()); + auto& data = flipped_tile.mutable_data(); + + if (x_flip) { + for (int y = 0; y < 8; ++y) { + for (int x = 0; x < 4; ++x) { + std::swap(data[y * 8 + x], data[y * 8 + (7 - x)]); + } + } + } + + if (y_flip) { + for (int y = 0; y < 4; ++y) { + for (int x = 0; x < 8; ++x) { + std::swap(data[y * 8 + x], data[(7 - y) * 8 + x]); + } + } + } + + flipped_tile.SetPalette(current_gfx_individual_[current_tile8_].palette()); + core::Renderer::Get().RenderBitmap(&flipped_tile); + tile_to_paint = &flipped_tile; + } + + // Paint with 8x8 tile size at 4x scale (32 display pixels per 8x8 tile) + if (tile16_edit_canvas_.DrawTilePainter(*tile_to_paint, 8, 4.0F)) { + ImVec2 click_pos = tile16_edit_canvas_.drawn_tile_position(); + // Convert from display coordinates to tile16 bitmap coordinates + // The canvas shows 16x16 pixels at 4x scale (64x64 display), so divide by 4 to get actual pixel coordinates + click_pos.x = (click_pos.x - 2) / 4.0F; // Account for padding and 4x scale + click_pos.y = (click_pos.y - 2) / 4.0F; + + // Ensure coordinates are within the 16x16 tile bounds + click_pos.x = std::max(0.0F, std::min(15.0F, click_pos.x)); + click_pos.y = std::max(0.0F, std::min(15.0F, click_pos.y)); + + RETURN_IF_ERROR(DrawToCurrentTile16(click_pos)); + } + } + + tile16_edit_canvas_.DrawGrid(8.0F); // 8x8 grid + tile16_edit_canvas_.DrawOverlay(); + + } // End fixed canvas child window + ImGui::EndChild(); + + // Compact preview below canvas + if (current_tile16_bmp_.is_active()) { + auto* texture = current_tile16_bmp_.texture(); + if (texture) { + Text("Preview:"); + ImGui::Image((ImTextureID)(intptr_t)texture, ImVec2(32.0F, 32.0F)); + } + } + + // Controls column - clean and organized + TableNextColumn(); + Text("Controls"); + + // Essential tile8 controls at the top + Text("Tile8 Options:"); + Checkbox("X Flip", &x_flip); + SameLine(); + Checkbox("Y Flip", &y_flip); + Checkbox("Priority", &priority_tile); + + // Show current tile8 selection + if (current_tile8_ >= 0 && + current_tile8_ < static_cast(current_gfx_individual_.size()) && + current_gfx_individual_[current_tile8_].is_active()) { + Text("Tile8: %d", current_tile8_); SameLine(); - if (Button("Pal +", ImVec2(40, 0))) - RETURN_IF_ERROR(CyclePalette(true)); - - // Quick palette grid - for (int i = 0; i < 8; ++i) { - if (i > 0 && i % 4 != 0) - SameLine(); - bool is_current = (current_palette_ == i); - if (is_current) - PushStyleColor(ImGuiCol_Button, ImVec4(0.4f, 0.7f, 0.4f, 1.0f)); - if (Button(std::to_string(i).c_str(), ImVec2(20, 20))) { - current_palette_ = i; - RETURN_IF_ERROR(RefreshAllPalettes()); - } - if (is_current) - PopStyleColor(); - if (i == 3) { /* New line */ - } + auto* tile8_texture = current_gfx_individual_[current_tile8_].texture(); + if (tile8_texture) { + ImGui::Image((ImTextureID)(intptr_t)tile8_texture, ImVec2(16, 16)); } - - ImGui::EndGroup(); } - - // Tile16 editor column with integrated previews and controls - if (show_tile16_editor) { - TableNextColumn(); - Text("Tile16 Editor (ID: %02X)", current_tile16_); - - // Wrap tile16 editor in a scrollable region - if (ImGui::BeginChild("##Tile16EditorScrollRegion", ImVec2(0, 0), true, - ImGuiWindowFlags_AlwaysVerticalScrollbar)) { - // Main tile16 editing area - gui::BeginPadding(3); - tile16_edit_canvas_.DrawBackground(); - gui::EndPadding(); - - tile16_edit_canvas_.DrawContextMenu(); - - // Draw current tile16 bitmap with better table scaling (2x to fit in smaller space) - if (current_tile16_bmp_.is_active()) { - tile16_edit_canvas_.DrawBitmap(current_tile16_bmp_, 2, 2, 2.0f); - } - - // Handle tile8 painting with flip controls - improved detection - if (tile8_source_canvas_.HasValidSelection() && current_tile8_ >= 0 && - current_tile8_ < static_cast(current_gfx_individual_.size()) && - current_gfx_individual_[current_tile8_].is_active()) { - - // Create a flipped version of the tile8 for preview/painting if needed - gfx::Bitmap* tile_to_paint = ¤t_gfx_individual_[current_tile8_]; - gfx::Bitmap flipped_tile; - - if (x_flip || y_flip) { - // Create flipped version - flipped_tile.Create( - 8, 8, 8, current_gfx_individual_[current_tile8_].vector()); - auto& data = flipped_tile.mutable_data(); - - // Apply flips to the data - if (x_flip) { - for (int y = 0; y < 8; ++y) { - for (int x = 0; x < 4; ++x) { // Only need to swap half - int left_idx = y * 8 + x; - int right_idx = y * 8 + (7 - x); - std::swap(data[left_idx], data[right_idx]); - } - } - } - - if (y_flip) { - for (int y = 0; y < 4; ++y) { // Only need to swap half - for (int x = 0; x < 8; ++x) { - int top_idx = y * 8 + x; - int bottom_idx = (7 - y) * 8 + x; - std::swap(data[top_idx], data[bottom_idx]); - } - } - } - - flipped_tile.SetPalette( - current_gfx_individual_[current_tile8_].palette()); - core::Renderer::Get().RenderBitmap(&flipped_tile); - tile_to_paint = &flipped_tile; - } - - // Use 2x scale to match the tile16 bitmap scaling - if (tile16_edit_canvas_.DrawTilePainter(*tile_to_paint, 16, 2.0f)) { - ImVec2 click_pos = tile16_edit_canvas_.drawn_tile_position(); - // Scale down accounting for padding and 2x scale - click_pos.x = (click_pos.x - 2) / 2.0f; - click_pos.y = (click_pos.y - 2) / 2.0f; - RETURN_IF_ERROR(DrawToCurrentTile16(click_pos)); - } - } - - tile16_edit_canvas_.DrawGrid(); - tile16_edit_canvas_.DrawOverlay(); - - // Preview section right below the editor - Separator(); - Text("Preview"); - if (current_tile16_bmp_.is_active()) { - auto* texture = current_tile16_bmp_.texture(); - if (texture) { - Text("1x:"); - SameLine(); - ImGui::Image((ImTextureID)(intptr_t)texture, ImVec2(16, 16)); - SameLine(); - Text("2x:"); - SameLine(); - ImGui::Image((ImTextureID)(intptr_t)texture, ImVec2(32, 32)); - SameLine(); - Text("4x:"); - SameLine(); - ImGui::Image((ImTextureID)(intptr_t)texture, ImVec2(64, 64)); - } - } - - // Controls integrated right below the editor - Separator(); - - // Tile8 flip controls - right under the editor - Text("Tile8 Controls:"); - Checkbox("X Flip", &x_flip); - SameLine(); - Checkbox("Y Flip", &y_flip); - SameLine(); - Checkbox("Priority", &priority_tile); - - // Show selected tile8 preview inline - if (current_tile8_ >= 0 && - current_tile8_ < static_cast(current_gfx_individual_.size()) && - current_gfx_individual_[current_tile8_].is_active()) { - Text("Selected Tile8 (%d):", current_tile8_); - SameLine(); - auto* tile8_texture = - current_gfx_individual_[current_tile8_].texture(); - if (tile8_texture) { - ImGui::Image((ImTextureID)(intptr_t)tile8_texture, ImVec2(24, 24)); - } - } + + Separator(); + + // Quick palette selector + Text("Palette: %d", current_palette_); + for (int i = 0; i < 8; ++i) { + if (i > 0 && i % 4 != 0) SameLine(); + bool is_current = (current_palette_ == i); + if (is_current) + PushStyleColor(ImGuiCol_Button, ImVec4(0.4F, 0.7F, 0.4F, 1.0F)); + if (Button(std::to_string(i).c_str(), ImVec2(18, 18))) { + current_palette_ = i; + RETURN_IF_ERROR(RefreshAllPalettes()); } - if (BeginChild("InfoPaletteChild", ImVec2(270, 120), true)) { - gui::DrawTable(tile_edit_table_); - } - EndChild(); - // Compact controls section directly below - if (BeginTable( - "##Tile16CompactControls", 2, - ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersInnerV)) { - TableSetupColumn("Actions", ImGuiTableColumnFlags_WidthFixed, 100); - TableSetupColumn("Advanced", ImGuiTableColumnFlags_WidthStretch); - - TableNextRow(); - - // Actions column - TableNextColumn(); - if (BeginChild("ActionsChild", ImVec2(190, 120), true)) { - if (Button("Clear Tile16", ImVec2(80, 0))) - RETURN_IF_ERROR(ClearTile16()); - if (Button("Copy", ImVec2(80, 0))) - RETURN_IF_ERROR(CopyTile16ToClipboard(current_tile16_)); - if (Button("Paste", ImVec2(80, 0))) - RETURN_IF_ERROR(PasteTile16FromClipboard()); - - Separator(); - - bool can_undo = !undo_stack_.empty(); - bool can_redo = !redo_stack_.empty(); - - if (!can_undo) - BeginDisabled(); - if (Button("Undo", ImVec2(80, 0))) - RETURN_IF_ERROR(Undo()); - if (!can_undo) - EndDisabled(); - - if (!can_redo) - BeginDisabled(); - if (Button("Redo", ImVec2(80, 0))) - RETURN_IF_ERROR(Redo()); - if (!can_redo) - EndDisabled(); - - Separator(); - DrawScratchSpace(); - } - EndChild(); - - // Advanced settings column - TableNextColumn(); - if (BeginChild("AdvancedChild", ImVec2(0, 120), true)) { - if (Button("Palette Settings")) { - show_palette_settings_ = !show_palette_settings_; - } - - if (Button("Manual Tile8 Inputs")) { - ImGui::OpenPopup("ManualTile8Editor"); - } - HOVER_HINT("Edit tile8 IDs and properties directly"); - - if (Button("Refresh Blockset")) { - RETURN_IF_ERROR(RefreshTile16Blockset()); - } - HOVER_HINT("Regenerate tile16 blockset from ROM data"); - - Text("Advanced Palette:"); - const char* palette_group_names[] = {"OW Main", "OW Aux", "OW Anim", - "Dungeon", "Sprites", "Armor", - "Sword"}; - if (Combo("##AdvPaletteGroup", ¤t_palette_group_, - palette_group_names, 7)) { - RETURN_IF_ERROR(RefreshAllPalettes()); - } - - Text("Normalization:"); - int mask_value = static_cast(palette_normalization_mask_); - if (SliderInt("##NormMask", &mask_value, 1, 255, "0x%02X")) { - palette_normalization_mask_ = static_cast(mask_value); - RETURN_IF_ERROR(LoadTile8()); // Reload with new mask - } - - Checkbox("Auto Normalize", &auto_normalize_pixels_); - } - EndChild(); - - // Manual tile8 editor popup - DrawManualTile8Inputs(); - - EndTable(); - } - - // Close the tile16 editor scroll region - EndChild(); - - EndTable(); + if (is_current) PopStyleColor(); } - - // Close show_controls conditional + + Separator(); + + // Essential actions + Text("Actions:"); + if (Button("Clear", ImVec2(50, 0))) { + RETURN_IF_ERROR(ClearTile16()); + } + SameLine(); + if (Button("Copy", ImVec2(50, 0))) { + RETURN_IF_ERROR(CopyTile16ToClipboard(current_tile16_)); + } + if (Button("Paste", ImVec2(50, 0))) { + RETURN_IF_ERROR(PasteTile16FromClipboard()); + } + + Separator(); + + // Save/Discard changes + Text("Changes:"); + if (Button("Save", ImVec2(50, 0))) { + RETURN_IF_ERROR(CommitChangesToOverworld()); + } + HOVER_HINT("Apply changes to overworld and regenerate blockset"); + SameLine(); + if (Button("Discard", ImVec2(50, 0))) { + RETURN_IF_ERROR(DiscardChanges()); + } + HOVER_HINT("Reload tile16 from ROM, discarding local changes"); + + bool can_undo = !undo_stack_.empty(); + + if (!can_undo) BeginDisabled(); + if (Button("Undo", ImVec2(50, 0))) { + RETURN_IF_ERROR(Undo()); + } + if (!can_undo) EndDisabled(); + + // Advanced controls (collapsible) + if (show_advanced_controls) { + Separator(); + Text("Advanced:"); + + if (Button("Palette Settings")) { + show_palette_settings_ = !show_palette_settings_; + } + + if (Button("Manual Edit")) { + ImGui::OpenPopup("ManualTile8Editor"); + } + + if (Button("Refresh Blockset")) { + RETURN_IF_ERROR(RefreshTile16Blockset()); + } + + // Scratch space in compact form + Text("Scratch:"); + DrawScratchSpace(); + + // Manual tile8 editor popup + DrawManualTile8Inputs(); + } + + EndTable(); } + // Draw palette settings if enabled DrawPaletteSettings(); return absl::OkStatus(); @@ -1402,7 +1414,8 @@ absl::Status Tile16Editor::CommitChangesToBlockset() { } // Update individual tile bitmaps (tile_bitmaps is a map) - for (auto& [tile_id, tile_bitmap] : tile16_blockset_->tile_bitmaps) { + for (auto& pair : tile16_blockset_->tile_bitmaps) { + auto& tile_bitmap = pair.second; if (tile_bitmap.modified()) { core::Renderer::Get().UpdateBitmap(&tile_bitmap); tile_bitmap.set_modified(false); @@ -1412,6 +1425,33 @@ absl::Status Tile16Editor::CommitChangesToBlockset() { return absl::OkStatus(); } +absl::Status Tile16Editor::CommitChangesToOverworld() { + // Write all tile16 changes to ROM + RETURN_IF_ERROR(SaveTile16ToROM()); + + // Regenerate the tile16 blockset to reflect changes + RETURN_IF_ERROR(RefreshTile16Blockset()); + + // Update the overworld tilemap to use the new tile16 data + RETURN_IF_ERROR(UpdateOverworldTilemap()); + + // Notify the parent editor (overworld editor) to regenerate its blockset + if (on_changes_committed_) { + RETURN_IF_ERROR(on_changes_committed_()); + } + + util::logf("Committed all Tile16 changes to overworld system"); + return absl::OkStatus(); +} + +absl::Status Tile16Editor::DiscardChanges() { + // Reload the current tile16 from ROM to discard any local changes + RETURN_IF_ERROR(SetCurrentTile(current_tile16_)); + + util::logf("Discarded Tile16 changes for tile %d", current_tile16_); + return absl::OkStatus(); +} + // Helper methods for palette management absl::Status Tile16Editor::UpdateTile8Palette(int tile8_id) { if (tile8_id < 0 || @@ -1580,13 +1620,17 @@ void Tile16Editor::DrawPaletteSettings() { } Text("Pixel Value Distribution:"); - for (const auto& [value, count] : pixel_counts) { + for (const auto& pair : pixel_counts) { + int value = pair.first; + int count = pair.second; Text(" Value %d (0x%X): %d pixels", value, value, count); } Text("Palette Colors Used:"); const auto& palette = current_gfx_individual_[current_tile8_].palette(); - for (const auto& [value, count] : pixel_counts) { + for (const auto& pair : pixel_counts) { + int value = pair.first; + int count = pair.second; if (value < static_cast(palette.size())) { auto color = palette[value]; ImVec4 display_color = color.rgb(); diff --git a/src/app/editor/overworld/tile16_editor.h b/src/app/editor/overworld/tile16_editor.h index baafab39..65881d88 100644 --- a/src/app/editor/overworld/tile16_editor.h +++ b/src/app/editor/overworld/tile16_editor.h @@ -104,6 +104,8 @@ class Tile16Editor : public gfx::GfxContext { absl::Status SaveTile16ToROM(); absl::Status UpdateOverworldTilemap(); absl::Status CommitChangesToBlockset(); + absl::Status CommitChangesToOverworld(); + absl::Status DiscardChanges(); // Helper methods for palette management absl::Status UpdateTile8Palette(int tile8_id); @@ -114,12 +116,18 @@ class Tile16Editor : public gfx::GfxContext { absl::Status UpdateROMTile16Data(); absl::Status RefreshTile16Blockset(); gfx::Tile16* GetCurrentTile16Data(); + absl::Status RegenerateTile16BitmapFromROM(); // Manual tile8 input controls void DrawManualTile8Inputs(); void set_rom(Rom* rom) { rom_ = rom; } Rom* rom() const { return rom_; } + + // Callback for when changes are committed to notify parent editor + void set_on_changes_committed(std::function callback) { + on_changes_committed_ = callback; + } private: Rom* rom_ = nullptr; @@ -198,10 +206,10 @@ class Tile16Editor : public gfx::GfxContext { gui::CanvasGridSize::k32x32}; gfx::Bitmap tile16_blockset_bmp_; - // Canvas for editing the selected tile - smaller size for table fit + // Canvas for editing the selected tile - optimized for 2x2 grid of 8x8 tiles (16x16 total) gui::Canvas tile16_edit_canvas_{"Tile16EditCanvas", - ImVec2(128, 128), // Reduced from kTile16CanvasSize to fit tables - gui::CanvasGridSize::k64x64, 2.0f}; // Reduced scale to fit tables + ImVec2(64, 64), // Fixed 64x64 display size (16x16 pixels at 4x scale) + gui::CanvasGridSize::k8x8, 4.0F}; // 8x8 grid with 4x scale for clarity gfx::Bitmap current_tile16_bmp_; // Tile8 canvas to get the tile to drawing in the tile16_edit_canvas_ @@ -220,6 +228,9 @@ class Tile16Editor : public gfx::GfxContext { gfx::SnesPalette palette_; absl::Status status_; + + // Callback to notify parent editor when changes are committed + std::function on_changes_committed_; }; } // namespace editor diff --git a/src/app/gui/canvas_utils.cc b/src/app/gui/canvas_utils.cc index 6259e660..fee6a36c 100644 --- a/src/app/gui/canvas_utils.cc +++ b/src/app/gui/canvas_utils.cc @@ -16,19 +16,22 @@ ImVec2 AlignToGrid(ImVec2 pos, float grid_step) { std::floor(pos.y / grid_step) * grid_step); } -float CalculateEffectiveScale(ImVec2 canvas_size, ImVec2 content_size, float global_scale) { - if (content_size.x <= 0 || content_size.y <= 0) return global_scale; - +float CalculateEffectiveScale(ImVec2 canvas_size, ImVec2 content_size, + float global_scale) { + if (content_size.x <= 0 || content_size.y <= 0) + return global_scale; + float scale_x = (canvas_size.x * global_scale) / content_size.x; float scale_y = (canvas_size.y * global_scale) / content_size.y; return std::min(scale_x, scale_y); } -int GetTileIdFromPosition(ImVec2 mouse_pos, float tile_size, float scale, int tiles_per_row) { +int GetTileIdFromPosition(ImVec2 mouse_pos, float tile_size, float scale, + int tiles_per_row) { float scaled_tile_size = tile_size * scale; int tile_x = static_cast(mouse_pos.x / scaled_tile_size); int tile_y = static_cast(mouse_pos.y / scaled_tile_size); - + return tile_x + (tile_y * tiles_per_row); } @@ -36,35 +39,40 @@ bool LoadROMPaletteGroups(Rom* rom, CanvasPaletteManager& palette_manager) { if (!rom || palette_manager.palettes_loaded) { return palette_manager.palettes_loaded; } - + try { const auto& palette_groups = rom->palette_group(); palette_manager.rom_palette_groups.clear(); palette_manager.palette_group_names.clear(); - + // Overworld palettes if (palette_groups.overworld_main.size() > 0) { - palette_manager.rom_palette_groups.push_back(palette_groups.overworld_main[0]); + palette_manager.rom_palette_groups.push_back( + palette_groups.overworld_main[0]); palette_manager.palette_group_names.push_back("Overworld Main"); } if (palette_groups.overworld_aux.size() > 0) { - palette_manager.rom_palette_groups.push_back(palette_groups.overworld_aux[0]); + palette_manager.rom_palette_groups.push_back( + palette_groups.overworld_aux[0]); palette_manager.palette_group_names.push_back("Overworld Aux"); } if (palette_groups.overworld_animated.size() > 0) { - palette_manager.rom_palette_groups.push_back(palette_groups.overworld_animated[0]); + palette_manager.rom_palette_groups.push_back( + palette_groups.overworld_animated[0]); palette_manager.palette_group_names.push_back("Overworld Animated"); } - + // Dungeon palettes if (palette_groups.dungeon_main.size() > 0) { - palette_manager.rom_palette_groups.push_back(palette_groups.dungeon_main[0]); + palette_manager.rom_palette_groups.push_back( + palette_groups.dungeon_main[0]); palette_manager.palette_group_names.push_back("Dungeon Main"); } - + // Sprite palettes if (palette_groups.global_sprites.size() > 0) { - palette_manager.rom_palette_groups.push_back(palette_groups.global_sprites[0]); + palette_manager.rom_palette_groups.push_back( + palette_groups.global_sprites[0]); palette_manager.palette_group_names.push_back("Global Sprites"); } if (palette_groups.armors.size() > 0) { @@ -75,38 +83,43 @@ bool LoadROMPaletteGroups(Rom* rom, CanvasPaletteManager& palette_manager) { palette_manager.rom_palette_groups.push_back(palette_groups.swords[0]); palette_manager.palette_group_names.push_back("Swords"); } - + palette_manager.palettes_loaded = true; - util::logf("Canvas: Loaded %zu ROM palette groups", palette_manager.rom_palette_groups.size()); + util::logf("Canvas: Loaded %zu ROM palette groups", + palette_manager.rom_palette_groups.size()); return true; - + } catch (const std::exception& e) { util::logf("Canvas: Failed to load ROM palette groups: %s", e.what()); return false; } } -bool ApplyPaletteGroup(gfx::Bitmap* bitmap, const CanvasPaletteManager& palette_manager, +bool ApplyPaletteGroup(gfx::Bitmap* bitmap, + const CanvasPaletteManager& palette_manager, int group_index, int palette_index) { - if (!bitmap || group_index < 0 || - group_index >= static_cast(palette_manager.rom_palette_groups.size())) { + if (!bitmap || group_index < 0 || + group_index >= + static_cast(palette_manager.rom_palette_groups.size())) { return false; } - + try { - const auto& selected_palette = palette_manager.rom_palette_groups[group_index]; - + const auto& selected_palette = + palette_manager.rom_palette_groups[group_index]; + // Apply the palette based on the index if (palette_index >= 0 && palette_index < 8) { bitmap->SetPaletteWithTransparent(selected_palette, palette_index); } else { bitmap->SetPalette(selected_palette); } - + Renderer::Get().UpdateBitmap(bitmap); - util::logf("Canvas: Applied palette group %d, index %d to bitmap", group_index, palette_index); + util::logf("Canvas: Applied palette group %d, index %d to bitmap", + group_index, palette_index); return true; - + } catch (const std::exception& e) { util::logf("Canvas: Failed to apply palette: %s", e.what()); return false; @@ -114,22 +127,23 @@ bool ApplyPaletteGroup(gfx::Bitmap* bitmap, const CanvasPaletteManager& palette_ } // Drawing utility functions -void DrawCanvasRect(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling, - int x, int y, int w, int h, ImVec4 color, float global_scale) { +void DrawCanvasRect(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling, + int x, int y, int w, int h, ImVec4 color, + float global_scale) { // Apply global scale to position and size float scaled_x = x * global_scale; float scaled_y = y * global_scale; float scaled_w = w * global_scale; float scaled_h = h * global_scale; - + ImVec2 origin(canvas_p0.x + scrolling.x + scaled_x, canvas_p0.y + scrolling.y + scaled_y); ImVec2 size(canvas_p0.x + scrolling.x + scaled_x + scaled_w, canvas_p0.y + scrolling.y + scaled_y + scaled_h); - - uint32_t color_u32 = IM_COL32(color.x * 255, color.y * 255, color.z * 255, color.w * 255); + + uint32_t color_u32 = IM_COL32(color.x, color.y, color.z, color.w); draw_list->AddRectFilled(origin, size, color_u32); - + // Add a black outline ImVec2 outline_origin(origin.x - 1, origin.y - 1); ImVec2 outline_size(size.x + 1, size.y + 1); @@ -137,62 +151,67 @@ void DrawCanvasRect(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling, } void DrawCanvasText(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling, - const std::string& text, int x, int y, float global_scale) { + const std::string& text, int x, int y, float global_scale) { // Apply global scale to text position float scaled_x = x * global_scale; float scaled_y = y * global_scale; - - ImVec2 text_pos(canvas_p0.x + scrolling.x + scaled_x, + + ImVec2 text_pos(canvas_p0.x + scrolling.x + scaled_x, canvas_p0.y + scrolling.y + scaled_y); - + // Draw text with black shadow for better visibility draw_list->AddText(ImVec2(text_pos.x + 1, text_pos.y + 1), IM_COL32(0, 0, 0, 255), text.c_str()); draw_list->AddText(text_pos, IM_COL32(255, 255, 255, 255), text.c_str()); } -void DrawCanvasOutline(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling, - int x, int y, int w, int h, uint32_t color) { - ImVec2 origin(canvas_p0.x + scrolling.x + x, - canvas_p0.y + scrolling.y + y); +void DrawCanvasOutline(ImDrawList* draw_list, ImVec2 canvas_p0, + ImVec2 scrolling, int x, int y, int w, int h, + uint32_t color) { + ImVec2 origin(canvas_p0.x + scrolling.x + x, canvas_p0.y + scrolling.y + y); ImVec2 size(canvas_p0.x + scrolling.x + x + w, canvas_p0.y + scrolling.y + y + h); draw_list->AddRect(origin, size, color, 0, 0, 1.5f); } -void DrawCanvasOutlineWithColor(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling, - int x, int y, int w, int h, ImVec4 color) { - uint32_t color_u32 = IM_COL32(color.x * 255, color.y * 255, color.z * 255, color.w * 255); +void DrawCanvasOutlineWithColor(ImDrawList* draw_list, ImVec2 canvas_p0, + ImVec2 scrolling, int x, int y, int w, int h, + ImVec4 color) { + uint32_t color_u32 = + IM_COL32(color.x * 255, color.y * 255, color.z * 255, color.w * 255); DrawCanvasOutline(draw_list, canvas_p0, scrolling, x, y, w, h, color_u32); } // Grid utility functions -void DrawCanvasGridLines(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 canvas_p1, - ImVec2 scrolling, float grid_step, float global_scale) { +void DrawCanvasGridLines(ImDrawList* draw_list, ImVec2 canvas_p0, + ImVec2 canvas_p1, ImVec2 scrolling, float grid_step, + float global_scale) { const uint32_t grid_color = IM_COL32(200, 200, 200, 50); const float grid_thickness = 0.5f; - + float scaled_grid_step = grid_step * global_scale; - + for (float x = fmodf(scrolling.x, scaled_grid_step); x < (canvas_p1.x - canvas_p0.x); x += scaled_grid_step) { draw_list->AddLine(ImVec2(canvas_p0.x + x, canvas_p0.y), - ImVec2(canvas_p0.x + x, canvas_p1.y), - grid_color, grid_thickness); + ImVec2(canvas_p0.x + x, canvas_p1.y), grid_color, + grid_thickness); } - + for (float y = fmodf(scrolling.y, scaled_grid_step); y < (canvas_p1.y - canvas_p0.y); y += scaled_grid_step) { draw_list->AddLine(ImVec2(canvas_p0.x, canvas_p0.y + y), - ImVec2(canvas_p1.x, canvas_p0.y + y), - grid_color, grid_thickness); + ImVec2(canvas_p1.x, canvas_p0.y + y), grid_color, + grid_thickness); } } -void DrawCustomHighlight(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling, - int highlight_tile_id, float grid_step) { - if (highlight_tile_id == -1) return; - +void DrawCustomHighlight(ImDrawList* draw_list, ImVec2 canvas_p0, + ImVec2 scrolling, int highlight_tile_id, + float grid_step) { + if (highlight_tile_id == -1) + return; + int tile_x = highlight_tile_id % 8; int tile_y = highlight_tile_id / 8; ImVec2 tile_pos(canvas_p0.x + scrolling.x + tile_x * grid_step, @@ -202,10 +221,11 @@ void DrawCustomHighlight(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolli draw_list->AddRectFilled(tile_pos, tile_pos_end, IM_COL32(255, 0, 255, 255)); } -void DrawHexTileLabels(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling, - ImVec2 canvas_sz, float grid_step, float global_scale) { +void DrawHexTileLabels(ImDrawList* draw_list, ImVec2 canvas_p0, + ImVec2 scrolling, ImVec2 canvas_sz, float grid_step, + float global_scale) { float scaled_grid_step = grid_step * global_scale; - + for (float x = fmodf(scrolling.x, scaled_grid_step); x < canvas_sz.x * global_scale; x += scaled_grid_step) { for (float y = fmodf(scrolling.y, scaled_grid_step); @@ -213,19 +233,20 @@ void DrawHexTileLabels(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling int tile_x = (x - scrolling.x) / scaled_grid_step; int tile_y = (y - scrolling.y) / scaled_grid_step; int tile_id = tile_x + (tile_y * 16); - + char hex_id[8]; snprintf(hex_id, sizeof(hex_id), "%02X", tile_id); - + draw_list->AddText(ImVec2(canvas_p0.x + x + (scaled_grid_step / 2) - 4, - canvas_p0.y + y + (scaled_grid_step / 2) - 4), - IM_COL32(255, 255, 255, 255), hex_id); + canvas_p0.y + y + (scaled_grid_step / 2) - 4), + IM_COL32(255, 255, 255, 255), hex_id); } } } // Layout and interaction utilities -ImVec2 CalculateCanvasSize(ImVec2 content_region, ImVec2 custom_size, bool use_custom) { +ImVec2 CalculateCanvasSize(ImVec2 content_region, ImVec2 custom_size, + bool use_custom) { return use_custom ? custom_size : content_region; } @@ -239,28 +260,30 @@ bool IsPointInCanvas(ImVec2 point, ImVec2 canvas_p0, ImVec2 canvas_p1) { } // Size reporting for ImGui table integration -ImVec2 CalculateMinimumCanvasSize(ImVec2 content_size, float global_scale, float padding) { +ImVec2 CalculateMinimumCanvasSize(ImVec2 content_size, float global_scale, + float padding) { // Calculate minimum size needed to display content with padding ImVec2 min_size = ImVec2(content_size.x * global_scale + padding * 2, - content_size.y * global_scale + padding * 2); - + content_size.y * global_scale + padding * 2); + // Ensure minimum practical size min_size.x = std::max(min_size.x, 64.0f); min_size.y = std::max(min_size.y, 64.0f); - + return min_size; } -ImVec2 CalculatePreferredCanvasSize(ImVec2 content_size, float global_scale, float min_scale) { +ImVec2 CalculatePreferredCanvasSize(ImVec2 content_size, float global_scale, + float min_scale) { // Calculate preferred size with minimum scale constraint float effective_scale = std::max(global_scale, min_scale); ImVec2 preferred_size = ImVec2(content_size.x * effective_scale + 8.0f, - content_size.y * effective_scale + 8.0f); - + content_size.y * effective_scale + 8.0f); + // Cap to reasonable maximum sizes for table integration preferred_size.x = std::min(preferred_size.x, 800.0f); preferred_size.y = std::min(preferred_size.y, 600.0f); - + return preferred_size; } @@ -271,7 +294,8 @@ void ReserveCanvasSpace(ImVec2 canvas_size, const std::string& label) { } ImGui::Dummy(canvas_size); ImGui::SameLine(); - ImGui::SetCursorPosX(ImGui::GetCursorPosX() - canvas_size.x); // Move back to start + ImGui::SetCursorPosX(ImGui::GetCursorPosX() - + canvas_size.x); // Move back to start } void SetNextCanvasSize(ImVec2 size, bool auto_resize) { @@ -286,34 +310,38 @@ void SetNextCanvasSize(ImVec2 size, bool auto_resize) { // High-level composite operations void DrawCanvasGrid(const CanvasRenderContext& ctx, int highlight_tile_id) { - if (!ctx.enable_grid) return; - + if (!ctx.enable_grid) + return; + ctx.draw_list->PushClipRect(ctx.canvas_p0, ctx.canvas_p1, true); - + // Draw grid lines - DrawCanvasGridLines(ctx.draw_list, ctx.canvas_p0, ctx.canvas_p1, - ctx.scrolling, ctx.grid_step, ctx.global_scale); - + DrawCanvasGridLines(ctx.draw_list, ctx.canvas_p0, ctx.canvas_p1, + ctx.scrolling, ctx.grid_step, ctx.global_scale); + // Draw highlight if specified if (highlight_tile_id != -1) { - DrawCustomHighlight(ctx.draw_list, ctx.canvas_p0, ctx.scrolling, - highlight_tile_id, ctx.grid_step * ctx.global_scale); + DrawCustomHighlight(ctx.draw_list, ctx.canvas_p0, ctx.scrolling, + highlight_tile_id, ctx.grid_step * ctx.global_scale); } - + // Draw hex labels if enabled if (ctx.enable_hex_labels) { DrawHexTileLabels(ctx.draw_list, ctx.canvas_p0, ctx.scrolling, - ImVec2(ctx.canvas_p1.x - ctx.canvas_p0.x, ctx.canvas_p1.y - ctx.canvas_p0.y), - ctx.grid_step, ctx.global_scale); + ImVec2(ctx.canvas_p1.x - ctx.canvas_p0.x, + ctx.canvas_p1.y - ctx.canvas_p0.y), + ctx.grid_step, ctx.global_scale); } - + ctx.draw_list->PopClipRect(); } -void DrawCanvasOverlay(const CanvasRenderContext& ctx, const ImVector& points, - const ImVector& selected_points) { - const ImVec2 origin(ctx.canvas_p0.x + ctx.scrolling.x, ctx.canvas_p0.y + ctx.scrolling.y); - +void DrawCanvasOverlay(const CanvasRenderContext& ctx, + const ImVector& points, + const ImVector& selected_points) { + const ImVec2 origin(ctx.canvas_p0.x + ctx.scrolling.x, + ctx.canvas_p0.y + ctx.scrolling.y); + // Draw hover points for (int n = 0; n < points.Size; n += 2) { ctx.draw_list->AddRect( @@ -326,20 +354,22 @@ void DrawCanvasOverlay(const CanvasRenderContext& ctx, const ImVector& p if (!selected_points.empty()) { for (int n = 0; n < selected_points.size(); n += 2) { ctx.draw_list->AddRect(ImVec2(origin.x + selected_points[n].x, - origin.y + selected_points[n].y), - ImVec2(origin.x + selected_points[n + 1].x + 0x10, - origin.y + selected_points[n + 1].y + 0x10), - IM_COL32(255, 255, 255, 255), 1.0f); + origin.y + selected_points[n].y), + ImVec2(origin.x + selected_points[n + 1].x + 0x10, + origin.y + selected_points[n + 1].y + 0x10), + IM_COL32(255, 255, 255, 255), 1.0f); } } } -void DrawCanvasLabels(const CanvasRenderContext& ctx, const ImVector>& labels, - int current_labels, int tile_id_offset) { - if (current_labels >= labels.size()) return; - +void DrawCanvasLabels(const CanvasRenderContext& ctx, + const ImVector>& labels, + int current_labels, int tile_id_offset) { + if (current_labels >= labels.size()) + return; + float scaled_grid_step = ctx.grid_step * ctx.global_scale; - + for (float x = fmodf(ctx.scrolling.x, scaled_grid_step); x < (ctx.canvas_p1.x - ctx.canvas_p0.x); x += scaled_grid_step) { for (float y = fmodf(ctx.scrolling.y, scaled_grid_step); @@ -351,7 +381,7 @@ void DrawCanvasLabels(const CanvasRenderContext& ctx, const ImVector= labels[current_labels].size()) { break; } - + const std::string& label = labels[current_labels][tile_id]; ctx.draw_list->AddText( ImVec2(ctx.canvas_p0.x + x + (scaled_grid_step / 2) - tile_id_offset, @@ -361,6 +391,6 @@ void DrawCanvasLabels(const CanvasRenderContext& ctx, const ImVector