From 36e602b2b0aa20d451cc1d5b2cb3cb7f1547a2b1 Mon Sep 17 00:00:00 2001
From: Gary Talent <gary@drinkingtea.net>
Date: Sun, 6 Dec 2020 03:04:03 -0600
Subject: [PATCH] [nostalgia/core/studio] Make handle copy/cut/paste selections
 off tile lines

---
 src/nostalgia/core/studio/tilesheeteditor.cpp | 70 +++++++++----------
 src/nostalgia/core/studio/tilesheeteditor.hpp | 14 ++--
 2 files changed, 39 insertions(+), 45 deletions(-)

diff --git a/src/nostalgia/core/studio/tilesheeteditor.cpp b/src/nostalgia/core/studio/tilesheeteditor.cpp
index 9d68093b..02d1f480 100644
--- a/src/nostalgia/core/studio/tilesheeteditor.cpp
+++ b/src/nostalgia/core/studio/tilesheeteditor.cpp
@@ -348,17 +348,11 @@ class PasteClipboardCommand: public QUndoCommand {
 };
 
 
-void TileSheetClipboard::add(int idx, int color) {
-	if (m_chunks.size() && m_chunks.back().index + m_chunks.back().size == idx) {
-		++m_chunks.back().size;
-	} else {
-		m_chunks.push_back({idx, 1});
-	}
+void TileSheetClipboard::addPixel(int color) {
 	m_pixels.push_back(color);
 }
 
 void TileSheetClipboard::clear() {
-	m_chunks.clear();
 	m_pixels.clear();
 }
 
@@ -366,22 +360,17 @@ bool TileSheetClipboard::empty() const {
 	return m_pixels.empty();
 }
 
-void TileSheetClipboard::paste(int targetIdx, QVector<int> *pixels) const {
-	std::size_t ci = 0; // clipboard index
-	// set prevSrcIdx to current source index, as first iteration is already
-	// correct
-	int prevSrcIdx = m_chunks.size() ? m_chunks[0].index : 0;
-	for (std::size_t chunkIdx = 0; chunkIdx < m_chunks.size(); ++chunkIdx) {
-		const auto &chunk = m_chunks[chunkIdx];
-		targetIdx += chunk.index - prevSrcIdx;
-		prevSrcIdx = chunk.index;
-		const auto targetMod = targetIdx - chunk.index;
-		for (int i = 0; i < chunk.size; ++i) {
-			const auto ti = chunk.index + i + targetMod; // target index
-			if (ti < pixels->size()) {
-				(*pixels)[ti] = m_pixels[ci];
+void TileSheetClipboard::pastePixels(common::Point pt, QVector<int> *tgtPixels, int tgtColumns) const {
+	std::size_t srcIdx = 0;
+	const auto w = m_p2.x - m_p1.x;
+	const auto h = m_p2.y - m_p1.y;
+	for (int x = 0; x <= w; ++x) {
+		for (int y = 0; y <= h; ++y) {
+			const auto tgtIdx = ptToIdx(pt + common::Point(x, y), tgtColumns);
+			if (tgtIdx < tgtPixels->size()) {
+				(*tgtPixels)[tgtIdx] = m_pixels[srcIdx];
 			}
-			++ci;
+			++srcIdx;
 		}
 	}
 }
@@ -616,18 +605,23 @@ void SheetData::cutToClipboard() {
 }
 
 void SheetData::cutToClipboard(TileSheetClipboard *cb) {
-	const auto start = ptToIdx(cb->point1(), m_columns);
-	const auto end = ptToIdx(cb->point2(), m_columns);
+	const auto pt1 = cb->point1();
+	const auto pt2 = cb->point2();
 	auto apply = std::make_unique<TileSheetClipboard>();
-	for (int i = start; i <= end; ++i) {
-		const auto s = m_pixelSelected[i];
-		if (s) {
-			cb->add(i, m_pixels[i]);
-			apply->add(i, 0);
+	for (int x = pt1.x; x <= pt2.x; ++x) {
+		for (int y = pt1.y; y <= pt2.y; ++y) {
+			const auto pt = common::Point(x, y);
+			const auto i = ptToIdx(pt, m_columns);
+			const auto s = m_pixelSelected[i];
+			if (s) {
+				cb->addPixel(m_pixels[i]);
+				apply->addPixel(0);
+			}
 		}
 	}
 	apply->setPoints(cb->point1(), cb->point2());
-	m_cmdStack->push(new PasteClipboardCommand(this, std::make_unique<TileSheetClipboard>(*cb), std::move(apply)));
+	auto restore = std::make_unique<TileSheetClipboard>(*cb);
+	m_cmdStack->push(new PasteClipboardCommand(this, std::move(restore), std::move(apply)));
 }
 
 void SheetData::copyToClipboard() {
@@ -636,12 +630,13 @@ void SheetData::copyToClipboard() {
 }
 
 void SheetData::copyToClipboard(TileSheetClipboard *cb) {
-	const auto start = ptToIdx(cb->point1(), m_columns);
-	const auto end = ptToIdx(cb->point2(), m_columns);
-	for (int i = start; i <= end; ++i) {
-		const auto s = m_pixelSelected[i];
-		if (s) {
-			cb->add(i, m_pixels[i]);
+	const auto p1 = cb->point1();
+	const auto p2 = cb->point2();
+	for (int x = p1.x; x <= p2.x; ++x) {
+		for (int y = p1.y; y <= p2.y; ++y) {
+			const auto pt = common::Point(x, y);
+			const auto idx = ptToIdx(pt, m_columns);
+			cb->addPixel(m_pixels[idx]);
 		}
 	}
 }
@@ -658,8 +653,7 @@ void SheetData::pasteClipboard() {
 }
 
 void SheetData::applyClipboard(const TileSheetClipboard &cb) {
-	const auto idx = ptToIdx(cb.point1(), m_columns);
-	cb.paste(idx, &m_pixels);
+	cb.pastePixels(cb.point1(), &m_pixels, m_columns);
 	emit pixelsChanged();
 	emit changeOccurred();
 }
diff --git a/src/nostalgia/core/studio/tilesheeteditor.hpp b/src/nostalgia/core/studio/tilesheeteditor.hpp
index 9a09d9a8..79576b08 100644
--- a/src/nostalgia/core/studio/tilesheeteditor.hpp
+++ b/src/nostalgia/core/studio/tilesheeteditor.hpp
@@ -51,40 +51,39 @@ struct PixelChunk {
 	static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.studio.PixelChunk";
 	static constexpr auto Fields = 2;
 	static constexpr auto TypeVersion = 1;
-	int index = -1;
+	common::Point pt;
 	int size = 0;
 };
 
 template<typename T>
 ox::Error model(T *io, PixelChunk *c) {
 	io->template setTypeInfo<PixelChunk>();
-	oxReturnError(io->field("index", &c->index));
+	oxReturnError(io->field("pt", &c->pt));
 	oxReturnError(io->field("size", &c->size));
 	return OxError(0);
 }
 
 struct TileSheetClipboard {
 	static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.studio.TileSheetClipboard";
-	static constexpr auto Fields = 2;
+	static constexpr auto Fields = 3;
 	static constexpr auto TypeVersion = 1;
 
 	template<typename T>
 	friend ox::Error model(T*, TileSheetClipboard*);
 
 	protected:
-		std::vector<PixelChunk> m_chunks;
 		std::vector<int> m_pixels;
 		common::Point m_p1;
 		common::Point m_p2;
 
 	public:
-		void add(int idx, int color);
+		void addPixel(int color);
 
 		void clear();
 
 		[[nodiscard]] bool empty() const;
 
-		void paste(int targetIdx, QVector<int> *pixels) const;
+		void pastePixels(common::Point pt, QVector<int> *tgt, int tgtColumns) const;
 
 		void setPoints(common::Point p1, common::Point p2);
 
@@ -97,8 +96,9 @@ struct TileSheetClipboard {
 template<typename T>
 ox::Error model(T *io, TileSheetClipboard *b) {
 	io->template setTypeInfo<TileSheetClipboard>();
-	oxReturnError(io->field("chunks", &b->m_chunks));
 	oxReturnError(io->field("pixels", &b->m_pixels));
+	oxReturnError(io->field("p1", &b->m_p1));
+	oxReturnError(io->field("p2", &b->m_p2));
 	return OxError(0);
 }