From 8e816a261fcc3fb8d4293d8e7d235e7651d53783 Mon Sep 17 00:00:00 2001 From: Gary Talent Date: Tue, 14 Jan 2025 23:06:12 -0600 Subject: [PATCH] [nostalgia/core/studio] Cleanup, fix possible TileSheet fill tool failure --- .../commands/updatesubsheetcommand.cpp | 25 ++-- .../commands/updatesubsheetcommand.hpp | 7 +- .../tilesheeteditor/tilesheeteditormodel.cpp | 113 ++++++++++-------- .../tilesheeteditor/tilesheeteditormodel.hpp | 12 +- 4 files changed, 80 insertions(+), 77 deletions(-) diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/updatesubsheetcommand.cpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/updatesubsheetcommand.cpp index f5e38067..f15a3603 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/updatesubsheetcommand.cpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/updatesubsheetcommand.cpp @@ -9,27 +9,24 @@ namespace nostalgia::core { core::UpdateSubSheetCommand::UpdateSubSheetCommand( TileSheet &img, TileSheet::SubSheetIdx idx, - ox::String name, - int cols, - int rows) noexcept: - m_img(img), - m_idx(std::move(idx)), - m_sheet(getSubSheet(m_img, m_idx)), - m_newName(std::move(name)), - m_newCols(cols), - m_newRows(rows) { + ox::StringParam name, + int const cols, + int const rows): + m_img{img}, + m_idx{std::move(idx)}, + m_sheet{getSubSheet(m_img, m_idx)} { + m_sheet = getSubSheet(m_img, m_idx); + m_sheet.name = std::move(name); + OX_THROW_ERROR(resizeSubsheet(m_sheet, m_img.bpp, {cols, rows})); } ox::Error UpdateSubSheetCommand::redo() noexcept { - auto &sheet = getSubSheet(m_img, m_idx); - sheet.name = m_newName; - oxLogError(resizeSubsheet(sheet, m_img.bpp, {m_newCols, m_newRows})); + std::swap(m_sheet, getSubSheet(m_img, m_idx)); return {}; } ox::Error UpdateSubSheetCommand::undo() noexcept { - auto &sheet = getSubSheet(m_img, m_idx); - sheet = m_sheet; + std::swap(m_sheet, getSubSheet(m_img, m_idx)); return {}; } diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/updatesubsheetcommand.hpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/updatesubsheetcommand.hpp index 7bdb617e..9a90feef 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/updatesubsheetcommand.hpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/updatesubsheetcommand.hpp @@ -13,17 +13,14 @@ class UpdateSubSheetCommand: public TileSheetCommand { TileSheet &m_img; TileSheet::SubSheetIdx m_idx; TileSheet::SubSheet m_sheet; - ox::String m_newName; - int m_newCols = 0; - int m_newRows = 0; public: UpdateSubSheetCommand( TileSheet &img, TileSheet::SubSheetIdx idx, - ox::String name, + ox::StringParam name, int cols, - int rows) noexcept; + int rows); ox::Error redo() noexcept final; diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditormodel.cpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditormodel.cpp index b3acb13f..c31620f0 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditormodel.cpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditormodel.cpp @@ -26,11 +26,6 @@ namespace nostalgia::core { -Palette const TileSheetEditorModel::s_defaultPalette = { - .colorNames = {ox::Vector{{}}}, - .pages = {{"Page 1", ox::Vector(128)}}, -}; - // delete pixels of all non-leaf nodes static void normalizeSubsheets(TileSheet::SubSheet &ss) noexcept { if (ss.subsheets.empty()) { @@ -42,7 +37,14 @@ static void normalizeSubsheets(TileSheet::SubSheet &ss) noexcept { } } -TileSheetEditorModel::TileSheetEditorModel(studio::StudioContext &sctx, ox::StringView path, studio::UndoStack &undoStack): + +Palette const TileSheetEditorModel::s_defaultPalette = { + .colorNames = {ox::Vector{{}}}, + .pages = {{"Page 1", ox::Vector(128)}}, +}; + +TileSheetEditorModel::TileSheetEditorModel( + studio::StudioContext &sctx, ox::StringViewCR path, studio::UndoStack &undoStack): m_sctx(sctx), m_tctx(m_sctx.tctx), m_path(path), @@ -62,7 +64,7 @@ void TileSheetEditorModel::cut() { TileSheetClipboard blankCb; auto cb = ox::make_unique(); auto const&s = activeSubSheet(); - iterateSelectionRows(*m_selection, [&](int x, int y) { + iterateSelectionRows(*m_selection, [&](int const x, int const y) { auto pt = ox::Point{x, y}; auto const idx = core::idx(s, pt); auto const c = getPixel(s, m_img.bpp, idx); @@ -73,7 +75,8 @@ void TileSheetEditorModel::cut() { auto const pt1 = m_selection->a; auto const pt2 = ox::Point{s.columns * TileWidth, s.rows * TileHeight}; turbine::setClipboardObject(m_tctx, std::move(cb)); - pushCommand(ox::make(CommandId::Cut, m_img, m_activeSubsSheetIdx, pt1, pt2, blankCb)); + pushCommand(ox::make( + CommandId::Cut, m_img, m_activeSubsSheetIdx, pt1, pt2, blankCb)); } void TileSheetEditorModel::copy() { @@ -81,7 +84,7 @@ void TileSheetEditorModel::copy() { return; } auto cb = ox::make_unique(); - iterateSelectionRows(*m_selection, [&](int x, int y) { + iterateSelectionRows(*m_selection, [&](int const x, int const y) { auto pt = ox::Point{x, y}; const auto&s = activeSubSheet(); const auto idx = core::idx(s, pt); @@ -105,7 +108,8 @@ void TileSheetEditorModel::paste() { auto const&s = activeSubSheet(); auto const pt1 = m_selection->a; auto const pt2 = ox::Point{s.columns * TileWidth, s.rows * TileHeight}; - pushCommand(ox::make(CommandId::Paste, m_img, m_activeSubsSheetIdx, pt1, pt2, *cb)); + pushCommand(ox::make( + CommandId::Paste, m_img, m_activeSubsSheetIdx, pt1, pt2, *cb)); } bool TileSheetEditorModel::acceptsClipboardPayload() const noexcept { @@ -120,8 +124,8 @@ ox::StringView TileSheetEditorModel::palPath() const noexcept { } constexpr ox::StringView uuidPrefix = "uuid://"; if (ox::beginsWith(path, uuidPrefix)) { - auto uuid = ox::StringView(&path[uuidPrefix.bytes()], path.bytes() - uuidPrefix.bytes()); - auto out = keelCtx(m_tctx).uuidToPath.at(uuid); + auto const uuid = ox::StringView(&path[uuidPrefix.bytes()], path.bytes() - uuidPrefix.bytes()); + auto const out = keelCtx(m_tctx).uuidToPath.at(uuid); if (out.error) { return {}; } @@ -131,13 +135,14 @@ ox::StringView TileSheetEditorModel::palPath() const noexcept { } } -ox::Error TileSheetEditorModel::setPalette(ox::StringView path) noexcept { +ox::Error TileSheetEditorModel::setPalette(ox::StringViewCR path) noexcept { OX_REQUIRE(uuid, keelCtx(m_tctx).pathToUuid.at(path)); - pushCommand(ox::make(activeSubSheetIdx(), m_img, uuid->toString())); + pushCommand(ox::make( + activeSubSheetIdx(), m_img, uuid->toString())); return {}; } -void TileSheetEditorModel::setPalettePage(size_t pg) noexcept { +void TileSheetEditorModel::setPalettePage(size_t const pg) noexcept { m_palettePage = ox::clamp(pg, 0, m_pal->pages.size() - 1); m_updated = true; } @@ -146,7 +151,7 @@ size_t TileSheetEditorModel::palettePage() const noexcept { return m_palettePage; } -void TileSheetEditorModel::drawCommand(ox::Point const&pt, std::size_t palIdx) noexcept { +void TileSheetEditorModel::drawCommand(ox::Point const&pt, std::size_t const palIdx) noexcept { const auto &activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx); if (pt.x >= activeSubSheet.columns * TileWidth || pt.y >= activeSubSheet.rows * TileHeight) { return; @@ -155,7 +160,8 @@ void TileSheetEditorModel::drawCommand(ox::Point const&pt, std::size_t palIdx) n if (m_ongoingDrawCommand) { m_updated = m_updated || m_ongoingDrawCommand->append(idx); } else if (getPixel(activeSubSheet, m_img.bpp, idx) != palIdx) { - pushCommand(ox::make(m_img, m_activeSubsSheetIdx, idx, static_cast(palIdx))); + pushCommand(ox::make( + m_img, m_activeSubsSheetIdx, idx, static_cast(palIdx))); } } @@ -171,16 +177,20 @@ void TileSheetEditorModel::rmSubsheet(TileSheet::SubSheetIdx const&idx) noexcept pushCommand(ox::make(m_img, idx)); } -void TileSheetEditorModel::insertTiles(TileSheet::SubSheetIdx const&idx, std::size_t tileIdx, std::size_t tileCnt) noexcept { +void TileSheetEditorModel::insertTiles( + TileSheet::SubSheetIdx const&idx, std::size_t const tileIdx, std::size_t const tileCnt) noexcept { pushCommand(ox::make(m_img, idx, tileIdx, tileCnt)); } -void TileSheetEditorModel::deleteTiles(TileSheet::SubSheetIdx const&idx, std::size_t tileIdx, std::size_t tileCnt) noexcept { +void TileSheetEditorModel::deleteTiles( + TileSheet::SubSheetIdx const&idx, std::size_t const tileIdx, std::size_t const tileCnt) noexcept { pushCommand(ox::make(m_img, idx, tileIdx, tileCnt)); } -ox::Error TileSheetEditorModel::updateSubsheet(TileSheet::SubSheetIdx const&idx, ox::StringView const&name, int cols, int rows) noexcept { - pushCommand(ox::make(m_img, idx, ox::String(name), cols, rows)); +ox::Error TileSheetEditorModel::updateSubsheet( + TileSheet::SubSheetIdx const&idx, ox::StringViewCR name, int const cols, int const rows) noexcept { + OX_REQUIRE(cmd, ox::makeCatch(m_img, idx, name, cols, rows)); + pushCommand(cmd); return {}; } @@ -189,7 +199,7 @@ void TileSheetEditorModel::setActiveSubsheet(TileSheet::SubSheetIdx const&idx) n this->activeSubsheetChanged.emit(m_activeSubsSheetIdx); } -void TileSheetEditorModel::fill(ox::Point const&pt, int palIdx) noexcept { +void TileSheetEditorModel::fill(ox::Point const&pt, int const palIdx) noexcept { auto const&activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx); // build idx list if (pt.x >= activeSubSheet.columns * TileWidth || pt.y >= activeSubSheet.rows * TileHeight) { @@ -197,10 +207,10 @@ void TileSheetEditorModel::fill(ox::Point const&pt, int palIdx) noexcept { } ox::Array updateMap = {}; auto const oldColor = getPixel(activeSubSheet, m_img.bpp, pt); - getFillPixels(updateMap, pt, oldColor); + getFillPixels(activeSubSheet, updateMap, pt, oldColor); ox::Vector idxList; auto i = core::idx(activeSubSheet, pt) / PixelsPerTile * PixelsPerTile; - for (auto u : updateMap) { + for (auto const u : updateMap) { if (u) { idxList.emplace_back(i); } @@ -230,7 +240,7 @@ void TileSheetEditorModel::completeSelection() noexcept { m_selTracker.finishSelection(); m_selection.emplace(m_selTracker.selection()); auto&pt = m_selection->b; - auto&s = activeSubSheet(); + auto const&s = activeSubSheet(); pt.x = ox::min(s.columns * TileWidth - 1, pt.x); pt.y = ox::min(s.rows * TileHeight - 1, pt.y); } @@ -275,47 +285,44 @@ ox::Error TileSheetEditorModel::saveFile() noexcept { return m_sctx.project->writeObj(m_path, m_img, ox::ClawFormat::Metal); } -bool TileSheetEditorModel::pixelSelected(std::size_t idx) const noexcept { +bool TileSheetEditorModel::pixelSelected(std::size_t const idx) const noexcept { auto const&s = activeSubSheet(); auto const pt = idxToPt(static_cast(idx), s.columns); return m_selection && m_selection->contains(pt); } -void TileSheetEditorModel::getFillPixels(ox::Span pixels, ox::Point const&pt, int oldColor) const noexcept { - const auto &activeSubSheet = this->activeSubSheet(); - const auto tileIdx = [activeSubSheet](const ox::Point &pt) noexcept { - return ptToIdx(pt, activeSubSheet.columns) / PixelsPerTile; - }; - // get points - const auto leftPt = pt + ox::Point(-1, 0); - const auto rightPt = pt + ox::Point(1, 0); - const auto topPt = pt + ox::Point(0, -1); - const auto bottomPt = pt + ox::Point(0, 1); - // calculate indices - const auto idx = ptToIdx(pt, activeSubSheet.columns); - const auto leftIdx = ptToIdx(leftPt, activeSubSheet.columns); - const auto rightIdx = ptToIdx(rightPt, activeSubSheet.columns); - const auto topIdx = ptToIdx(topPt, activeSubSheet.columns); - const auto bottomIdx = ptToIdx(bottomPt, activeSubSheet.columns); - const auto tile = tileIdx(pt); +void TileSheetEditorModel::getFillPixels( + TileSheet::SubSheet const&activeSubSheet, + ox::Span pixels, + ox::Point const&pt, + int const oldColor) const noexcept { + auto const idx = ptToIdx(pt, activeSubSheet.columns); + auto const relIdx = idx % PixelsPerTile; + if (pixels[relIdx] || getPixel(activeSubSheet, m_img.bpp, idx) != oldColor) { + return; + } // mark pixels to update - pixels[idx % PixelsPerTile] = true; - if (!pixels[leftIdx % PixelsPerTile] && tile == tileIdx(leftPt) && getPixel(activeSubSheet, m_img.bpp, leftIdx) == oldColor) { - getFillPixels(pixels, leftPt, oldColor); + pixels[relIdx] = true; + if (pt.x % TileWidth != 0) { + auto const leftPt = pt + ox::Point{-1, 0}; + getFillPixels(activeSubSheet, pixels, leftPt, oldColor); } - if (!pixels[rightIdx % PixelsPerTile] && tile == tileIdx(rightPt) && getPixel(activeSubSheet, m_img.bpp, rightIdx) == oldColor) { - getFillPixels(pixels, rightPt, oldColor); + if (pt.x % TileWidth != TileWidth - 1) { + auto const rightPt = pt + ox::Point{1, 0}; + getFillPixels(activeSubSheet, pixels, rightPt, oldColor); } - if (!pixels[topIdx % PixelsPerTile] && tile == tileIdx(topPt) && getPixel(activeSubSheet, m_img.bpp, topIdx) == oldColor) { - getFillPixels(pixels, topPt, oldColor); + if (pt.y % TileHeight != 0) { + auto const topPt = pt + ox::Point{0, -1}; + getFillPixels(activeSubSheet, pixels, topPt, oldColor); } - if (!pixels[bottomIdx % PixelsPerTile] && tile == tileIdx(bottomPt) && getPixel(activeSubSheet, m_img.bpp, bottomIdx) == oldColor) { - getFillPixels(pixels, bottomPt, oldColor); + if (pt.y % TileHeight != TileHeight - 1) { + auto const bottomPt = pt + ox::Point{0, 1}; + getFillPixels(activeSubSheet, pixels, bottomPt, oldColor); } } void TileSheetEditorModel::pushCommand(studio::UndoCommand *cmd) noexcept { - std::ignore = m_undoStack.push(ox::UPtr(cmd)); + std::ignore = m_undoStack.push(ox::UPtr{cmd}); m_ongoingDrawCommand = dynamic_cast(cmd); m_updated = true; } diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditormodel.hpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditormodel.hpp index 6dd03b65..6371a4e3 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditormodel.hpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditormodel.hpp @@ -4,9 +4,7 @@ #pragma once -#include #include -#include #include #include @@ -38,7 +36,7 @@ class TileSheetEditorModel: public ox::SignalHandler { bool m_updated = false; public: - TileSheetEditorModel(studio::StudioContext &sctx, ox::StringView path, studio::UndoStack &undoStack); + TileSheetEditorModel(studio::StudioContext &sctx, ox::StringViewCR path, studio::UndoStack &undoStack); ~TileSheetEditorModel() override = default; @@ -63,7 +61,7 @@ class TileSheetEditorModel: public ox::SignalHandler { [[nodiscard]] ox::StringView palPath() const noexcept; - ox::Error setPalette(ox::StringView path) noexcept; + ox::Error setPalette(ox::StringViewCR path) noexcept; void setPalettePage(size_t pg) noexcept; @@ -128,7 +126,11 @@ class TileSheetEditorModel: public ox::SignalHandler { bool pixelSelected(std::size_t idx) const noexcept; private: - void getFillPixels(ox::Span pixels, ox::Point const&pt, int oldColor) const noexcept; + void getFillPixels( + TileSheet::SubSheet const&activeSubSheet, + ox::Span pixels, + ox::Point const&pt, + int oldColor) const noexcept; void pushCommand(studio::UndoCommand *cmd) noexcept;