From 9fcbc6e692c0a650a8022f6d63918fc644cc2c0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Baptista=20de=20Paula=20e=20Silva?= Date: Sat, 15 Jun 2019 22:56:37 +0200 Subject: [PATCH 1/6] Add options "OverflowBorder" and "WrapBorder" to customize "MatchOutsideMap" behavior --- src/tiled/automapper.cpp | 46 ++++++++++++++++++++++++++++++++++------ src/tiled/automapper.h | 12 +++++++++++ 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/src/tiled/automapper.cpp b/src/tiled/automapper.cpp index 17b93ca2d1..5fbe8be750 100644 --- a/src/tiled/automapper.cpp +++ b/src/tiled/automapper.cpp @@ -108,6 +108,16 @@ bool AutoMapper::setupRuleMapProperties() mOptions.matchOutsideMap = value.toBool(); continue; } + } else if (name.compare(QLatin1String("OverflowBorder"), Qt::CaseInsensitive) == 0) { + if (value.canConvert(QVariant::Bool)) { + mOptions.overflowBorder = value.toBool(); + continue; + } + } else if (name.compare(QLatin1String("WrapBorder"), Qt::CaseInsensitive) == 0) { + if (value.canConvert(QVariant::Bool)) { + mOptions.wrapBorder = value.toBool(); + continue; + } } else if (name.compare(QLatin1String("AutomappingRadius"), Qt::CaseInsensitive) == 0) { if (value.canConvert(QVariant::Int)) { mOptions.autoMappingRadius = value.toInt(); @@ -631,8 +641,23 @@ static bool layerMatchesConditions(const TileLayer &setLayer, !setLayer.contains(x + offset.x(), y + offset.y())) return false; - const Cell &setCell = setLayer.cellAt(x + offset.x(), - y + offset.y()); + int xd = x + offset.x(); + int yd = y + offset.y(); + if (options.wrapBorder) { + int width = setLayer.size().width(); + int height = setLayer.size().height(); + xd = (xd % width + width) % width; + yd = (yd % height + height) % height; + } else if (options.overflowBorder) { + if (xd < 0) xd = 0; + else if (xd >= setLayer.size().width()) + xd = setLayer.size().width() - 1; + if (yd < 0) yd = 0; + else if (yd >= setLayer.size().height()) + yd = setLayer.size().height() - 1; + } + + const Cell &setCell = setLayer.cellAt(xd, yd); // First check listNo. If any tile matches there, we can // immediately know there is no match. @@ -836,11 +861,14 @@ void AutoMapper::copyTileRegion(const TileLayer *srcLayer, int srcX, int srcY, int endX = dstX + width; int endY = dstY + height; - if (!mMapWork->infinite()) { + int dwidth = dstLayer->width(); + int dheight = dstLayer->height(); + + if (!mOptions.wrapBorder && !mMapWork->infinite()) { startX = qMax(0, startX); startY = qMax(0, startY); - endX = qMin(dstLayer->width(), endX); - endY = qMin(dstLayer->height(), endY); + endX = qMin(dwidth, endX); + endY = qMin(dheight, endY); } const int offsetX = srcX - dstX; @@ -851,7 +879,13 @@ void AutoMapper::copyTileRegion(const TileLayer *srcLayer, int srcX, int srcY, const Cell &cell = srcLayer->cellAt(x + offsetX, y + offsetY); if (!cell.isEmpty()) { // this is without graphics update, it's done afterwards for all - dstLayer->setCell(x, y, cell); + int xd = x; + int yd = y; + if (mOptions.wrapBorder && !mMapWork->infinite()) { + xd = (xd % dwidth + dwidth) % dwidth; + yd = (yd % dheight + dheight) % dheight; + } + dstLayer->setCell(xd, yd, cell); } } } diff --git a/src/tiled/automapper.h b/src/tiled/automapper.h index 993803d751..1ff84f2156 100644 --- a/src/tiled/automapper.h +++ b/src/tiled/automapper.h @@ -95,6 +95,18 @@ class AutoMapper : public QObject */ bool matchOutsideMap = true; + /** + * If "matchOutsideMap" is true, treat the out-of-bounds tiles as if they + * were the nearest inbound tile possible + */ + bool overflowBorder = false; + + /** + * If "matchOutsideMap" is true, wrap the map in the edges to apply the + * automapping rules + */ + bool wrapBorder = false; + /** * Determines if a rule is allowed to overlap itself. */ From 1579d7ba5619e269f1a48b5404367855d69549b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Baptista=20de=20Paula=20e=20Silva?= Date: Sat, 15 Jun 2019 23:03:40 +0200 Subject: [PATCH 2/6] Add forgotten check for infinite map in order not to produce bugs --- src/tiled/automapper.cpp | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/tiled/automapper.cpp b/src/tiled/automapper.cpp index 5fbe8be750..73c7f06e45 100644 --- a/src/tiled/automapper.cpp +++ b/src/tiled/automapper.cpp @@ -618,7 +618,8 @@ static bool layerMatchesConditions(const TileLayer &setLayer, const InputConditions &conditions, const QRegion &ruleRegion, const QPoint offset, - const AutoMapper::Options &options) + const AutoMapper::Options &options, + bool isMapInfinite) { const auto &listYes = conditions.listYes; const auto &listNo = conditions.listNo; @@ -643,18 +644,21 @@ static bool layerMatchesConditions(const TileLayer &setLayer, int xd = x + offset.x(); int yd = y + offset.y(); - if (options.wrapBorder) { - int width = setLayer.size().width(); - int height = setLayer.size().height(); - xd = (xd % width + width) % width; - yd = (yd % height + height) % height; - } else if (options.overflowBorder) { - if (xd < 0) xd = 0; - else if (xd >= setLayer.size().width()) - xd = setLayer.size().width() - 1; - if (yd < 0) yd = 0; - else if (yd >= setLayer.size().height()) - yd = setLayer.size().height() - 1; + + if (!isMapInfinite) { + if (options.wrapBorder) { + int width = setLayer.size().width(); + int height = setLayer.size().height(); + xd = (xd % width + width) % width; + yd = (yd % height + height) % height; + } else if (options.overflowBorder) { + if (xd < 0) xd = 0; + else if (xd >= setLayer.size().width()) + xd = setLayer.size().width() - 1; + if (yd < 0) yd = 0; + else if (yd >= setLayer.size().height()) + yd = setLayer.size().height() - 1; + } } const Cell &setCell = setLayer.cellAt(xd, yd); @@ -747,7 +751,7 @@ QRect AutoMapper::applyRule(int ruleIndex, const QRect &where) const int i = mMapWork->indexOfLayer(name, Layer::TileLayerType); const TileLayer &setLayer = (i >= 0) ? *mMapWork->layerAt(i)->asTileLayer() : dummy; - if (!layerMatchesConditions(setLayer, conditions, ruleInputRegion, QPoint(x, y), mOptions)) { + if (!layerMatchesConditions(setLayer, conditions, ruleInputRegion, QPoint(x, y), mOptions, mMapWork->infinite())) { allLayerNamesMatch = false; break; } From 4f0898f71bfcb480719b6a02070f067c4a40c0e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Baptista=20de=20Paula=20e=20Silva?= Date: Sun, 16 Jun 2019 00:59:55 +0200 Subject: [PATCH 3/6] Incorporate changes requested by bjorn on the pull request --- src/tiled/automapper.cpp | 59 ++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/src/tiled/automapper.cpp b/src/tiled/automapper.cpp index 73c7f06e45..98966eac2e 100644 --- a/src/tiled/automapper.cpp +++ b/src/tiled/automapper.cpp @@ -42,6 +42,11 @@ using namespace Tiled; +// Should i leave this function here? +inline int wrap(int value, int bound) { + return (value % bound + bound) % bound; +} + /* * About the order of the methods in this file. * The Automapper class has 3 bigger public functions, that is @@ -134,6 +139,17 @@ bool AutoMapper::setupRuleMapProperties() "Ignoring this property.") .arg(mRulePath, name, value.toString()) + QLatin1Char('\n'); } + + // OverflowBorder and WrapBorder make no sense for infinite maps + if (mMapWork->infinite()) { + mOptions.overflowBorder = false; + mOptions.wrapBorder = false; + } + + // Each of the border options imply MatchOutsideMap + if (mOptions.overflowBorder || mOptions.wrapBorder) + mOptions.matchOutsideMap = true; + return true; } @@ -618,8 +634,7 @@ static bool layerMatchesConditions(const TileLayer &setLayer, const InputConditions &conditions, const QRegion &ruleRegion, const QPoint offset, - const AutoMapper::Options &options, - bool isMapInfinite) + const AutoMapper::Options &options) { const auto &listYes = conditions.listYes; const auto &listNo = conditions.listNo; @@ -638,27 +653,21 @@ static bool layerMatchesConditions(const TileLayer &setLayer, #endif for (int x = rect.left(); x <= rect.right(); ++x) { for (int y = rect.top(); y <= rect.bottom(); ++y) { - if (!options.matchOutsideMap && - !setLayer.contains(x + offset.x(), y + offset.y())) - return false; - int xd = x + offset.x(); int yd = y + offset.y(); - if (!isMapInfinite) { - if (options.wrapBorder) { - int width = setLayer.size().width(); - int height = setLayer.size().height(); - xd = (xd % width + width) % width; - yd = (yd % height + height) % height; - } else if (options.overflowBorder) { - if (xd < 0) xd = 0; - else if (xd >= setLayer.size().width()) - xd = setLayer.size().width() - 1; - if (yd < 0) yd = 0; - else if (yd >= setLayer.size().height()) - yd = setLayer.size().height() - 1; - } + if (!options.matchOutsideMap && + !setLayer.contains(xd, yd)) + return false; + + // Those two options are guaranteed to be false if the map is infinite, + // so no "invalid" width/height accessing here. + if (options.wrapBorder) { + xd = wrap(xd, setLayer.width()); + yd = wrap(yd, setLayer.height()); + } else if (options.overflowBorder) { + xd = qBound(0, xd, setLayer.width() - 1); + yd = qBound(0, yd, setLayer.height() - 1); } const Cell &setCell = setLayer.cellAt(xd, yd); @@ -751,7 +760,7 @@ QRect AutoMapper::applyRule(int ruleIndex, const QRect &where) const int i = mMapWork->indexOfLayer(name, Layer::TileLayerType); const TileLayer &setLayer = (i >= 0) ? *mMapWork->layerAt(i)->asTileLayer() : dummy; - if (!layerMatchesConditions(setLayer, conditions, ruleInputRegion, QPoint(x, y), mOptions, mMapWork->infinite())) { + if (!layerMatchesConditions(setLayer, conditions, ruleInputRegion, QPoint(x, y), mOptions)) { allLayerNamesMatch = false; break; } @@ -885,9 +894,11 @@ void AutoMapper::copyTileRegion(const TileLayer *srcLayer, int srcX, int srcY, // this is without graphics update, it's done afterwards for all int xd = x; int yd = y; - if (mOptions.wrapBorder && !mMapWork->infinite()) { - xd = (xd % dwidth + dwidth) % dwidth; - yd = (yd % dheight + dheight) % dheight; + + // WrapBorder is only true on finite maps + if (mOptions.wrapBorder) { + xd = wrap(xd, dwidth); + yd = wrap(yd, dheight); } dstLayer->setCell(xd, yd, cell); } From 9e4fb2dfe0923064124d0b792744a050e7d89131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Baptista=20de=20Paula=20e=20Silva?= Date: Sun, 16 Jun 2019 16:31:37 +0200 Subject: [PATCH 4/6] Turned 'wrap' method static --- src/tiled/automapper.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tiled/automapper.cpp b/src/tiled/automapper.cpp index 98966eac2e..86a67fa6f0 100644 --- a/src/tiled/automapper.cpp +++ b/src/tiled/automapper.cpp @@ -43,7 +43,8 @@ using namespace Tiled; // Should i leave this function here? -inline int wrap(int value, int bound) { +inline static int wrap(int value, int bound) +{ return (value % bound + bound) % bound; } From 644189293247fa140fd8d98353fe3e6a2b7ef4b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Baptista=20de=20Paula=20e=20Silva?= Date: Mon, 17 Jun 2019 00:12:32 +0200 Subject: [PATCH 5/6] Turned 'wrap' function static --- src/tiled/automapper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tiled/automapper.cpp b/src/tiled/automapper.cpp index 86a67fa6f0..55da2edc33 100644 --- a/src/tiled/automapper.cpp +++ b/src/tiled/automapper.cpp @@ -43,7 +43,7 @@ using namespace Tiled; // Should i leave this function here? -inline static int wrap(int value, int bound) +static int wrap(int value, int bound) { return (value % bound + bound) % bound; } From c96a74562280f3951d8f668789253c75a9c81fd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Thu, 20 Jun 2019 09:37:48 +0200 Subject: [PATCH 6/6] Tweaks --- src/tiled/automapper.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/tiled/automapper.cpp b/src/tiled/automapper.cpp index 55da2edc33..b12cb21040 100644 --- a/src/tiled/automapper.cpp +++ b/src/tiled/automapper.cpp @@ -42,7 +42,6 @@ using namespace Tiled; -// Should i leave this function here? static int wrap(int value, int bound) { return (value % bound + bound) % bound; @@ -657,8 +656,7 @@ static bool layerMatchesConditions(const TileLayer &setLayer, int xd = x + offset.x(); int yd = y + offset.y(); - if (!options.matchOutsideMap && - !setLayer.contains(xd, yd)) + if (!options.matchOutsideMap && !setLayer.contains(xd, yd)) return false; // Those two options are guaranteed to be false if the map is infinite,