[nostalgia/gfx/studio/tilesheet] Add flip x and flip y functionality
All checks were successful
Build / build (push) Successful in 3m33s
All checks were successful
Build / build (push) Successful in 3m33s
This commit is contained in:
parent
1207dadee8
commit
be51838775
@ -414,6 +414,10 @@ void setPixel(TileSheet::SubSheet &ss, ox::Point const&pt, uint8_t palIdx) noexc
|
||||
|
||||
ox::Error setPixelCount(TileSheet::SubSheet &ss, std::size_t cnt) noexcept;
|
||||
|
||||
void flipX(TileSheet::SubSheet &ss, ox::Point const &a, ox::Point const &b) noexcept;
|
||||
|
||||
void flipY(TileSheet::SubSheet &ss, ox::Point const &a, ox::Point const &b) noexcept;
|
||||
|
||||
/**
|
||||
* Gets a count of the pixels in this sheet, and not that of its children.
|
||||
* @return a count of the pixels in this sheet
|
||||
|
@ -4,6 +4,7 @@ target_sources(
|
||||
cutpastecommand.cpp
|
||||
deletetilescommand.cpp
|
||||
drawcommand.cpp
|
||||
flipcommand.cpp
|
||||
inserttilescommand.cpp
|
||||
palettechangecommand.cpp
|
||||
rmsubsheetcommand.cpp
|
||||
|
@ -12,14 +12,16 @@ namespace nostalgia::gfx {
|
||||
// Command IDs to use with QUndoCommand::id()
|
||||
enum class CommandId {
|
||||
Draw = 1,
|
||||
AddSubSheet = 2,
|
||||
RmSubSheet = 3,
|
||||
DeleteTile = 4,
|
||||
InsertTile = 4,
|
||||
UpdateSubSheet = 5,
|
||||
Cut = 6,
|
||||
Paste = 7,
|
||||
PaletteChange = 8,
|
||||
AddSubSheet,
|
||||
RmSubSheet,
|
||||
DeleteTile,
|
||||
FlipXCommand,
|
||||
FlipYCommand,
|
||||
InsertTile,
|
||||
UpdateSubSheet,
|
||||
Cut,
|
||||
Paste,
|
||||
PaletteChange,
|
||||
};
|
||||
|
||||
constexpr bool operator==(CommandId c, int i) noexcept {
|
||||
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include "flipcommand.hpp"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace nostalgia::gfx {
|
||||
|
||||
FlipXCommand::FlipXCommand(
|
||||
TileSheet &img,
|
||||
TileSheet::SubSheetIdx subSheetIdx,
|
||||
ox::Point const &a,
|
||||
ox::Point const &b) noexcept:
|
||||
m_img{img},
|
||||
m_subSheetIdx{std::move(subSheetIdx)},
|
||||
m_ptA{a},
|
||||
m_ptB{b} {}
|
||||
|
||||
ox::Error FlipXCommand::redo() noexcept {
|
||||
auto &ss = getSubSheet(m_img, m_subSheetIdx);
|
||||
flipX(ss, m_ptA, m_ptB);
|
||||
return {};
|
||||
}
|
||||
|
||||
ox::Error FlipXCommand::undo() noexcept {
|
||||
auto &ss = getSubSheet(m_img, m_subSheetIdx);
|
||||
flipX(ss, m_ptA, m_ptB);
|
||||
return {};
|
||||
}
|
||||
|
||||
int FlipXCommand::commandId() const noexcept {
|
||||
return static_cast<int>(CommandId::FlipXCommand);
|
||||
}
|
||||
|
||||
TileSheet::SubSheetIdx const &FlipXCommand::subsheetIdx() const noexcept {
|
||||
return m_subSheetIdx;
|
||||
}
|
||||
|
||||
|
||||
FlipYCommand::FlipYCommand(
|
||||
TileSheet &img,
|
||||
TileSheet::SubSheetIdx subSheetIdx,
|
||||
ox::Point const &a,
|
||||
ox::Point const &b) noexcept:
|
||||
m_img{img},
|
||||
m_subSheetIdx{std::move(subSheetIdx)},
|
||||
m_ptA{a},
|
||||
m_ptB{b} {}
|
||||
|
||||
ox::Error FlipYCommand::redo() noexcept {
|
||||
auto &ss = getSubSheet(m_img, m_subSheetIdx);
|
||||
flipY(ss, m_ptA, m_ptB);
|
||||
return {};
|
||||
}
|
||||
|
||||
ox::Error FlipYCommand::undo() noexcept {
|
||||
auto &ss = getSubSheet(m_img, m_subSheetIdx);
|
||||
flipY(ss, m_ptA, m_ptB);
|
||||
return {};
|
||||
}
|
||||
|
||||
int FlipYCommand::commandId() const noexcept {
|
||||
return static_cast<int>(CommandId::FlipYCommand);
|
||||
}
|
||||
|
||||
TileSheet::SubSheetIdx const &FlipYCommand::subsheetIdx() const noexcept {
|
||||
return m_subSheetIdx;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "commands.hpp"
|
||||
|
||||
namespace nostalgia::gfx {
|
||||
|
||||
class FlipXCommand: public TileSheetCommand {
|
||||
private:
|
||||
TileSheet &m_img;
|
||||
TileSheet::SubSheetIdx m_subSheetIdx;
|
||||
ox::Point const m_ptA;
|
||||
ox::Point const m_ptB;
|
||||
|
||||
public:
|
||||
FlipXCommand(
|
||||
TileSheet &img,
|
||||
TileSheet::SubSheetIdx subSheetIdx,
|
||||
ox::Point const &a,
|
||||
ox::Point const &b) noexcept;
|
||||
|
||||
ox::Error redo() noexcept final;
|
||||
|
||||
ox::Error undo() noexcept final;
|
||||
|
||||
[[nodiscard]]
|
||||
int commandId() const noexcept final;
|
||||
|
||||
[[nodiscard]]
|
||||
TileSheet::SubSheetIdx const&subsheetIdx() const noexcept override;
|
||||
|
||||
};
|
||||
|
||||
class FlipYCommand: public TileSheetCommand {
|
||||
private:
|
||||
TileSheet &m_img;
|
||||
TileSheet::SubSheetIdx m_subSheetIdx;
|
||||
ox::Point const m_ptA;
|
||||
ox::Point const m_ptB;
|
||||
|
||||
public:
|
||||
FlipYCommand(
|
||||
TileSheet &img,
|
||||
TileSheet::SubSheetIdx subSheetIdx,
|
||||
ox::Point const &a,
|
||||
ox::Point const &b) noexcept;
|
||||
|
||||
ox::Error redo() noexcept final;
|
||||
|
||||
ox::Error undo() noexcept final;
|
||||
|
||||
[[nodiscard]]
|
||||
int commandId() const noexcept final;
|
||||
|
||||
[[nodiscard]]
|
||||
TileSheet::SubSheetIdx const&subsheetIdx() const noexcept override;
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -222,7 +222,19 @@ void TileSheetEditorImGui::draw(studio::StudioContext&) noexcept {
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
auto const ySize = controlsSize.y - 38;
|
||||
ImGui::BeginChild("OperationsBox", {s_palViewWidth - 24, ig::BtnSz.y + 17}, true);
|
||||
{
|
||||
auto constexpr btnSz = ImVec2{55, ig::BtnSz.y};
|
||||
if (ig::PushButton("Flip X", btnSz)) {
|
||||
oxLogError(m_model.flipX());
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ig::PushButton("Flip Y", btnSz)) {
|
||||
oxLogError(m_model.flipY());
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
auto const ySize = controlsSize.y - (38 + ig::BtnSz.y + 21);
|
||||
// draw palette/color picker
|
||||
ImGui::BeginChild("Palette", {s_palViewWidth - 24, ySize / 2.f}, true);
|
||||
{
|
||||
|
@ -22,6 +22,8 @@
|
||||
#include "commands/updatesubsheetcommand.hpp"
|
||||
#include "tilesheeteditormodel.hpp"
|
||||
|
||||
#include "commands/flipcommand.hpp"
|
||||
|
||||
namespace nostalgia::gfx {
|
||||
|
||||
// delete pixels of all non-leaf nodes
|
||||
@ -75,7 +77,7 @@ 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<CutPasteCommand>(
|
||||
std::ignore = pushCommand(ox::make<CutPasteCommand>(
|
||||
CommandId::Cut, m_img, m_activeSubsSheetIdx, pt1, pt2, blankCb));
|
||||
}
|
||||
|
||||
@ -108,7 +110,7 @@ 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<CutPasteCommand>(
|
||||
std::ignore = pushCommand(ox::make<CutPasteCommand>(
|
||||
CommandId::Paste, m_img, m_activeSubsSheetIdx, pt1, pt2, *cb));
|
||||
}
|
||||
|
||||
@ -123,7 +125,7 @@ ox::StringView TileSheetEditorModel::palPath() const noexcept {
|
||||
|
||||
ox::Error TileSheetEditorModel::setPalette(ox::StringViewCR path) noexcept {
|
||||
OX_REQUIRE(uuid, keelCtx(m_tctx).pathToUuid.at(path));
|
||||
pushCommand(ox::make<PaletteChangeCommand>(
|
||||
std::ignore = pushCommand(ox::make<PaletteChangeCommand>(
|
||||
activeSubSheetIdx(), m_img, uuid->toString()));
|
||||
return {};
|
||||
}
|
||||
@ -146,7 +148,7 @@ void TileSheetEditorModel::drawCommand(ox::Point const&pt, std::size_t const pal
|
||||
if (m_ongoingDrawCommand) {
|
||||
m_updated = m_updated || m_ongoingDrawCommand->append(idx);
|
||||
} else if (getPixel(activeSubSheet, idx) != palIdx) {
|
||||
pushCommand(ox::make<DrawCommand>(
|
||||
std::ignore = pushCommand(ox::make<DrawCommand>(
|
||||
m_img, m_activeSubsSheetIdx, idx, static_cast<int>(palIdx)));
|
||||
}
|
||||
}
|
||||
@ -156,27 +158,27 @@ void TileSheetEditorModel::endDrawCommand() noexcept {
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::addSubsheet(TileSheet::SubSheetIdx const&parentIdx) noexcept {
|
||||
pushCommand(ox::make<AddSubSheetCommand>(m_img, parentIdx));
|
||||
std::ignore = pushCommand(ox::make<AddSubSheetCommand>(m_img, parentIdx));
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::rmSubsheet(TileSheet::SubSheetIdx const&idx) noexcept {
|
||||
pushCommand(ox::make<RmSubSheetCommand>(m_img, idx));
|
||||
std::ignore = pushCommand(ox::make<RmSubSheetCommand>(m_img, idx));
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::insertTiles(
|
||||
TileSheet::SubSheetIdx const&idx, std::size_t const tileIdx, std::size_t const tileCnt) noexcept {
|
||||
pushCommand(ox::make<InsertTilesCommand>(m_img, idx, tileIdx, tileCnt));
|
||||
std::ignore = pushCommand(ox::make<InsertTilesCommand>(m_img, idx, tileIdx, tileCnt));
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::deleteTiles(
|
||||
TileSheet::SubSheetIdx const&idx, std::size_t const tileIdx, std::size_t const tileCnt) noexcept {
|
||||
pushCommand(ox::make<DeleteTilesCommand>(m_img, idx, tileIdx, tileCnt));
|
||||
std::ignore = pushCommand(ox::make<DeleteTilesCommand>(m_img, idx, tileIdx, tileCnt));
|
||||
}
|
||||
|
||||
ox::Error TileSheetEditorModel::updateSubsheet(
|
||||
TileSheet::SubSheetIdx const&idx, ox::StringViewCR name, int const cols, int const rows) noexcept {
|
||||
OX_REQUIRE(cmd, ox::makeCatch<UpdateSubSheetCommand>(m_img, idx, name, cols, rows));
|
||||
pushCommand(cmd);
|
||||
std::ignore = pushCommand(cmd);
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -206,7 +208,7 @@ void TileSheetEditorModel::fill(ox::Point const&pt, int const palIdx) noexcept {
|
||||
if (m_ongoingDrawCommand) {
|
||||
m_updated = m_updated || m_ongoingDrawCommand->append(idxList);
|
||||
} else if (getPixel(activeSubSheet, pt) != palIdx) {
|
||||
pushCommand(ox::make<DrawCommand>(m_img, m_activeSubsSheetIdx, idxList, palIdx));
|
||||
std::ignore = pushCommand(ox::make<DrawCommand>(m_img, m_activeSubsSheetIdx, idxList, palIdx));
|
||||
}
|
||||
}
|
||||
|
||||
@ -279,6 +281,28 @@ bool TileSheetEditorModel::pixelSelected(std::size_t const idx) const noexcept {
|
||||
return m_selection && m_selection->contains(pt);
|
||||
}
|
||||
|
||||
ox::Error TileSheetEditorModel::flipX() noexcept {
|
||||
auto const &ss = activeSubSheet();
|
||||
ox::Point a;
|
||||
ox::Point b{ss.columns * TileWidth - 1, ss.rows * TileHeight - 1};
|
||||
if (m_selection) {
|
||||
a = m_selection->a;
|
||||
b = m_selection->b;
|
||||
}
|
||||
return pushCommand(ox::make<FlipXCommand>(m_img, m_activeSubsSheetIdx, a, b));
|
||||
}
|
||||
|
||||
ox::Error TileSheetEditorModel::flipY() noexcept {
|
||||
auto const &ss = activeSubSheet();
|
||||
ox::Point a;
|
||||
ox::Point b{ss.columns * TileWidth - 1, ss.rows * TileHeight - 1};
|
||||
if (m_selection) {
|
||||
a = m_selection->a;
|
||||
b = m_selection->b;
|
||||
}
|
||||
return pushCommand(ox::make<FlipYCommand>(m_img, m_activeSubsSheetIdx, a, b));
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::getFillPixels(
|
||||
TileSheet::SubSheet const&activeSubSheet,
|
||||
ox::Span<bool> pixels,
|
||||
@ -323,10 +347,11 @@ void TileSheetEditorModel::setPalPath() noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::pushCommand(studio::UndoCommand *cmd) noexcept {
|
||||
ox::Error TileSheetEditorModel::pushCommand(studio::UndoCommand *cmd) noexcept {
|
||||
std::ignore = m_undoStack.push(ox::UPtr<studio::UndoCommand>{cmd});
|
||||
m_ongoingDrawCommand = dynamic_cast<DrawCommand*>(cmd);
|
||||
m_updated = true;
|
||||
return {};
|
||||
}
|
||||
|
||||
ox::Error TileSheetEditorModel::handleFileRename(ox::StringViewCR, ox::StringViewCR newPath, ox::UUID const&id) noexcept {
|
||||
|
@ -126,6 +126,10 @@ class TileSheetEditorModel: public ox::SignalHandler {
|
||||
|
||||
bool pixelSelected(std::size_t idx) const noexcept;
|
||||
|
||||
ox::Error flipX() noexcept;
|
||||
|
||||
ox::Error flipY() noexcept;
|
||||
|
||||
private:
|
||||
void getFillPixels(
|
||||
TileSheet::SubSheet const&activeSubSheet,
|
||||
@ -135,7 +139,7 @@ class TileSheetEditorModel: public ox::SignalHandler {
|
||||
|
||||
void setPalPath() noexcept;
|
||||
|
||||
void pushCommand(studio::UndoCommand *cmd) noexcept;
|
||||
ox::Error pushCommand(studio::UndoCommand *cmd) noexcept;
|
||||
|
||||
ox::Error handleFileRename(ox::StringViewCR, ox::StringViewCR newPath, ox::UUID const&id) noexcept;
|
||||
|
||||
|
@ -103,6 +103,30 @@ ox::Error setPixelCount(TileSheet::SubSheet &ss, std::size_t const cnt) noexcept
|
||||
return setPixelCount(ss.pixels, cnt);
|
||||
}
|
||||
|
||||
void flipX(TileSheet::SubSheet &ss, ox::Point const &a, ox::Point const &b) noexcept {
|
||||
auto const w = (b.x - a.x) / 2 + 1;
|
||||
auto const pixCols = ss.columns * TileWidth;
|
||||
for (int32_t yi = a.y; yi <= b.y; ++yi) {
|
||||
for (int32_t xi = 0; xi < w; ++xi) {
|
||||
auto const aIdx = ptToIdx(a.x + xi, yi, pixCols);
|
||||
auto const bIdx = ptToIdx(b.x - xi, yi, pixCols);
|
||||
std::swap(ss.pixels[aIdx], ss.pixels[bIdx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void flipY(TileSheet::SubSheet &ss, ox::Point const &a, ox::Point const &b) noexcept {
|
||||
auto const h = (b.y - a.y) / 2 + 1;
|
||||
auto const pixCols = ss.columns * TileWidth;
|
||||
for (int32_t yi = 0; yi < h; ++yi) {
|
||||
for (int32_t xi = a.x; xi <= b.x; ++xi) {
|
||||
auto const aIdx = ptToIdx(xi, a.y + yi, pixCols);
|
||||
auto const bIdx = ptToIdx(xi, b.y - yi, pixCols);
|
||||
std::swap(ss.pixels[aIdx], ss.pixels[bIdx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned pixelCnt(TileSheet::SubSheet const&ss) noexcept {
|
||||
return static_cast<unsigned>(ss.pixels.size());
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user