[nostalgia/core/studio/tilesheet] Add ability to rotate a selection

This commit is contained in:
Gary Talent 2025-02-02 20:22:20 -06:00
parent fb8d295fcb
commit 1bc18e34a8
3 changed files with 77 additions and 29 deletions

View File

@ -6,50 +6,58 @@
namespace nostalgia::gfx { namespace nostalgia::gfx {
static void rotateLeft(TileSheet::SubSheet &ss, ox::Point const &pt, int const depth = 0) noexcept { static void rotateLeft(
TileSheet::SubSheet &ss,
ox::Point const &pt,
ox::Point const &pt1,
ox::Point const &pt2,
int const depth = 0) noexcept {
if (depth >= 4) { if (depth >= 4) {
return; return;
} }
auto const h = ss.rows * TileHeight; auto const dstPt = ox::Point{pt1.x + pt.y, pt2.y - pt.x};
auto const dstPt = ox::Point{pt.y, h - 1 - pt.x}; auto const srcIdx = ptToIdx(pt + pt1, ss.columns);
auto const srcIdx = ptToIdx(pt, ss.columns);
auto const dstIdx = ptToIdx(dstPt, ss.columns); auto const dstIdx = ptToIdx(dstPt, ss.columns);
auto const src = ss.pixels[srcIdx]; auto const src = ss.pixels[srcIdx];
auto &dst = ss.pixels[dstIdx]; auto &dst = ss.pixels[dstIdx];
rotateLeft(ss, dstPt, depth + 1); rotateLeft(ss, dstPt - pt1, pt1, pt2, depth + 1);
dst = src; dst = src;
} }
static void rotateLeft(TileSheet::SubSheet &ss) noexcept { static void rotateLeft(TileSheet::SubSheet &ss, ox::Point const &pt1, ox::Point const &pt2) noexcept {
auto const w = ss.columns * TileWidth; auto const w = pt2.x - pt1.x;
auto const h = ss.rows * TileHeight; auto const h = pt2.y - pt1.y;
for (int x = 0; x < w / 2; ++x) { for (int x = 0; x <= w / 2; ++x) {
for (int y = 0; y < h / 2; ++y) { for (int y = 0; y <= h / 2; ++y) {
rotateLeft(ss, {w - 1 - y, x}); rotateLeft(ss, {x, y}, pt1, pt2);
} }
} }
} }
static void rotateRight(TileSheet::SubSheet &ss, ox::Point const &pt, int const depth = 0) noexcept { static void rotateRight(
TileSheet::SubSheet &ss,
ox::Point const &pt,
ox::Point const &pt1,
ox::Point const &pt2,
int const depth = 0) noexcept {
if (depth >= 4) { if (depth >= 4) {
return; return;
} }
auto const w = ss.columns * TileWidth; auto const dstPt = ox::Point{pt2.x - pt.y, pt1.y + pt.x};
auto const dstPt = ox::Point{w - 1 - pt.y, pt.x}; auto const srcIdx = ptToIdx(pt + pt1, ss.columns);
auto const srcIdx = ptToIdx(pt, ss.columns);
auto const dstIdx = ptToIdx(dstPt, ss.columns); auto const dstIdx = ptToIdx(dstPt, ss.columns);
auto const src = ss.pixels[srcIdx]; auto const src = ss.pixels[srcIdx];
auto &dst = ss.pixels[dstIdx]; auto &dst = ss.pixels[dstIdx];
rotateRight(ss, dstPt, depth + 1); rotateRight(ss, dstPt - pt1, pt1, pt2, depth + 1);
dst = src; dst = src;
} }
static void rotateRight(TileSheet::SubSheet &ss) noexcept { static void rotateRight(TileSheet::SubSheet &ss, ox::Point const &pt1, ox::Point const &pt2) noexcept {
auto const w = ss.columns * TileWidth; auto const w = pt2.x - pt1.x;
auto const h = ss.rows * TileHeight; auto const h = pt2.y - pt1.y;
for (int x = 0; x < w / 2; ++x) { for (int x = 0; x <= w / 2; ++x) {
for (int y = 0; y < h / 2; ++y) { for (int y = 0; y <= h / 2; ++y) {
rotateRight(ss, {w - 1 - y, x}); rotateRight(ss, {x, y}, pt1, pt2);
} }
} }
} }
@ -61,28 +69,47 @@ RotateCommand::RotateCommand(
Direction const dir) noexcept: Direction const dir) noexcept:
m_img(img), m_img(img),
m_idx(std::move(idx)), m_idx(std::move(idx)),
m_pt2{[this] {
auto &ss = getSubSheet(m_img, m_idx);
return ox::Point{ss.columns * TileWidth - 1, ss.rows * TileHeight - 1};
}()},
m_dir{dir} {
}
RotateCommand::RotateCommand(
TileSheet &img,
TileSheet::SubSheetIdx idx,
ox::Point const &pt1,
ox::Point const &pt2,
Direction const dir) noexcept:
m_img(img),
m_idx(std::move(idx)),
m_pt1{pt1},
m_pt2{pt2},
m_dir{dir} { m_dir{dir} {
} }
ox::Error RotateCommand::redo() noexcept { ox::Error RotateCommand::redo() noexcept {
auto &ss = getSubSheet(m_img, m_idx);
switch (m_dir) { switch (m_dir) {
case Direction::Left: case Direction::Left:
rotateLeft(getSubSheet(m_img, m_idx)); rotateLeft(ss, m_pt1, m_pt2);
break; break;
case Direction::Right: case Direction::Right:
rotateRight(getSubSheet(m_img, m_idx)); rotateRight(ss, m_pt1, m_pt2);
break; break;
} }
return {}; return {};
} }
ox::Error RotateCommand::undo() noexcept { ox::Error RotateCommand::undo() noexcept {
auto &ss = getSubSheet(m_img, m_idx);
switch (m_dir) { switch (m_dir) {
case Direction::Left: case Direction::Left:
rotateRight(getSubSheet(m_img, m_idx)); rotateRight(ss, m_pt1, m_pt2);
break; break;
case Direction::Right: case Direction::Right:
rotateLeft(getSubSheet(m_img, m_idx)); rotateLeft(ss, m_pt1, m_pt2);
break; break;
} }
return {}; return {};

View File

@ -18,11 +18,20 @@ class RotateCommand: public TileSheetCommand {
private: private:
TileSheet &m_img; TileSheet &m_img;
TileSheet::SubSheetIdx m_idx; TileSheet::SubSheetIdx m_idx;
ox::Point const m_pt1;
ox::Point const m_pt2;
Direction const m_dir; Direction const m_dir;
public: public:
RotateCommand(TileSheet &img, TileSheet::SubSheetIdx idx, Direction dir) noexcept; RotateCommand(TileSheet &img, TileSheet::SubSheetIdx idx, Direction dir) noexcept;
RotateCommand(
TileSheet &img,
TileSheet::SubSheetIdx idx,
ox::Point const &pt1,
ox::Point const &pt2,
Direction dir) noexcept;
ox::Error redo() noexcept final; ox::Error redo() noexcept final;
ox::Error undo() noexcept final; ox::Error undo() noexcept final;

View File

@ -239,13 +239,25 @@ void TileSheetEditorModel::fill(ox::Point const&pt, int const palIdx) noexcept {
} }
ox::Error TileSheetEditorModel::rotateLeft() noexcept { ox::Error TileSheetEditorModel::rotateLeft() noexcept {
auto &ss = activeSubSheet();
ox::Point pt1, pt2{ss.columns * TileWidth - 1, ss.rows * TileHeight - 1};
if (m_selection) {
pt1 = m_selection->a;
pt2 = m_selection->b;
}
return pushCommand(ox::make<RotateCommand>( return pushCommand(ox::make<RotateCommand>(
m_img, m_activeSubsSheetIdx, RotateCommand::Direction::Left)); m_img, m_activeSubsSheetIdx, pt1, pt2, RotateCommand::Direction::Left));
} }
ox::Error TileSheetEditorModel::rotateRight() noexcept { ox::Error TileSheetEditorModel::rotateRight() noexcept {
auto &ss = activeSubSheet();
ox::Point pt1, pt2{ss.columns * TileWidth - 1, ss.rows * TileHeight - 1};
if (m_selection) {
pt1 = m_selection->a;
pt2 = m_selection->b;
}
return pushCommand(ox::make<RotateCommand>( return pushCommand(ox::make<RotateCommand>(
m_img, m_activeSubsSheetIdx, RotateCommand::Direction::Right)); m_img, m_activeSubsSheetIdx, pt1, pt2, RotateCommand::Direction::Right));
} }
void TileSheetEditorModel::setSelection(studio::Selection const&sel) noexcept { void TileSheetEditorModel::setSelection(studio::Selection const&sel) noexcept {