-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix automap for infinite maps #1692
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See inline comments.
Does this also make infinite maps work for automapping rules?
src/tiled/automapper.cpp
Outdated
int endY = dstY + height; | ||
|
||
if (!mMapWork->infinite()) { | ||
startX = qMax(dstX, startX); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dstX
and startX
are the same at this point. You meant 0
instead of dstX
.
src/libtiled/tilelayer.cpp
Outdated
r &= QRect(dx, dy, other->width(), other->height()); | ||
} else { | ||
r = rect; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you need the rect
parameter? As far as I can see, r
just needs to cover both layers and it should be in local coordinates, so this should do it:
const QRect r = bounds().united(other->bounds()).translated(-position());
No, it doesn't work if the automap rules are infinite as well, right now. |
src/tiled/automapper.cpp
Outdated
@@ -691,7 +691,7 @@ static bool compareLayerTo(const TileLayer *setLayer, | |||
bool matchListNo = false; | |||
|
|||
|
|||
if (!setLayer->contains(x + offset.x(), y + offset.y())) { | |||
if (!(setLayer->contains(x + offset.x(), y + offset.y()) || setLayer->map()->infinite())) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Intuitively, this check and the special handling inside should actually not be necessary at all. If the setLayer
does not contain the given location, then c1
will just be an empty cell, which I think should be treated the same as a cell outside of the layer boundaries. Can you check this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No no, it needs to have that check. I found it strange too, but it doesn't work if this check is removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you give an example of where it goes wrong?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What part did you try removing exactly? I suggested removing this entire bit of code:
if (!(setLayer->contains(x + offset.x(), y + offset.y()) || setLayer->map()->infinite())) {
foreach (const TileLayer *comparedTileLayer, listYes) {
if (!comparedTileLayer->contains(x, y))
return false;
const Cell &c2 = comparedTileLayer->cellAt(x, y);
if (!c2.isEmpty())
return false;
}
continue;
}
It's not clear to me what is happening from those screenshots.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aah, I thought you meant setLayer->map()->infinite()
Reminder to self, or when you have time: need to check this change against issue #1224 (since that is where this special code we have now removed was introduced). |
Alright, I checked this against issue #1224 and unfortunately it re-introduces that issue. However, I think the consistency is more important than getting that particular example to work. The example in issue #1224 could be adjusted to have separate input and output regions in order to make the rule work at the border. However, I found an issue with the automapping in that even after this patch it doesn't work with infinite maps outside of the already used bounds. Please open this example.zip and try to place a rock somewhere. You should see that the first time it works, but then when you paint rocks in many places, it only places the flowers within the chunks that were allocated the first time the automapping was activated. |
Oh 😕 I'll try to fix this. |
What I've observed is when we apply automap and then paint outside the bounds and try automap again it doesn't work. Then if we undo the first automap and then paint outside the bounds and then try automap again, it works. What exactly is stored after calling 'Automap' for the first time ? |
Okay, so after spending the past 3 hours into this, I can say that I've wasted the whole time 🙁 So actually, automapping works even if we paint out-of-bounds. If you test after my previous commit, you cannot visually see the automapped tile layer. If you save it and then restart Tiled, the automapped tile layer is clearly visible 🤦♂️ I have no idea why this is happening. Here's a gif of the same. 😢 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, so after spending the past 3 hours into this, I can say that I've wasted the whole time
Actually it's not wasted, because you made me realize that it's probably to do with the bounds changed signal not being emitted. You know the thing you added to the TileLayerChangeWatcher
. Can you verify this and look into a fix if that's the reason?
Your change to TileLayer::copy
looks like a sensible code cleanup, so please keep that part.
src/libtiled/tilelayer.cpp
Outdated
@@ -236,7 +233,10 @@ void TileLayer::setCells(int x, int y, TileLayer *layer, | |||
const QRegion &mask) | |||
{ | |||
// Determine the overlapping area | |||
QRegion area = QRect(x, y, layer->width(), layer->height()); | |||
int width = qMax(layer->width(), layer->bounds().width()); | |||
int height = qMax(layer->height(), layer->bounds().height()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this makes little sense, please revert it back to what it was.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But why? Suppose a layer was created with size (1, 1) and then some cells were set out-of-bounds. If this layer is then used in setCells
for some other layer, then technically, these 'out-of-bounds' cells should also be set in this layer. Don't you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, the part that does not make sense, is to take the width/height of the bounds but still copy starting from 0,0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I guess we need to unite QRect(0, 0, layer->width(), layer->height()
with layer->bounds()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And adjust the offset accordingly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, if you use the bounds then the rect()
of the layer is completely irrelevant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got confused by something else 😕
Okay, automap is fixed now. But still some problem persists. When we undo, we need to zoom in/out or move around to see the effect of undo. This is because when we undo, the bounds of Tile Layer are not changed. But I don't think it's that big of a problem. |
Right, so this signal should actually be made part of the undo command. Would that be hard to do? |
This is primarily the reason, the signal cannot emit. Since the bounds do not change after removing cells, we cannot actually check if it is necessary to emit this signal. |
If the bounds do not change, it is not necessary to emit the signal. Then there must be some other thing missing to trigger an update. |
Done! 🙂 |
Nice, thanks! |
This patch aims to fix the automap issue in infinite-maps.