Skip to content
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

Supporting Infinite Maps #1635

Merged
merged 11 commits into from
Jul 4, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
271 changes: 157 additions & 114 deletions src/libtiled/tilelayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,31 +271,42 @@ void TileLayer::erase(const QRegion &area)

void TileLayer::flip(FlipDirection direction)
{
QVector<Cell> newGrid(mWidth * mHeight);
TileLayer *newLayer = new TileLayer(QLatin1String(""), 0, 0, mWidth, mHeight);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way to make an empty string is QString().


Q_ASSERT(direction == FlipHorizontally || direction == FlipVertically);

for (int y = 0; y < mHeight; ++y) {
for (int x = 0; x < mWidth; ++x) {
Cell &dest = newGrid[x + y * mWidth];
if (direction == FlipHorizontally) {
const Cell &source = cellAt(mWidth - x - 1, y);
dest = source;
dest.setFlippedHorizontally(!source.flippedHorizontally());
} else if (direction == FlipVertically) {
const Cell &source = cellAt(x, mHeight - y - 1);
dest = source;
dest.setFlippedVertically(!source.flippedVertically());
QHashIterator<QPoint, Chunk* > it(mChunks);
while (it.hasNext()) {
it.next();
for (int y = 0; y < CHUNK_SIZE; ++y) {
for (int x = 0; x < CHUNK_SIZE; ++x) {
int _x = it.key().x() * CHUNK_SIZE + x;
int _y = it.key().y() * CHUNK_SIZE + y;

if (!contains(_x, _y))
continue;

const Cell &source = it.value()->cellAt(x, y);
Cell dest(source);

if (direction == FlipHorizontally) {
dest.setFlippedHorizontally(!source.flippedHorizontally());
newLayer->setCell(mWidth - _x - 1, _y, dest);
} else if (direction == FlipVertically) {
dest.setFlippedVertically(!source.flippedVertically());
newLayer->setCell(_x, mHeight -y - 1, dest);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space after -

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And it should be _y too.

}
}
}
}

copyGrid(newGrid);
mChunks = newLayer->mChunks;
delete newLayer;
}

void TileLayer::flipHexagonal(FlipDirection direction)
{
QVector<Cell> newGrid(mWidth * mHeight);
TileLayer *newLayer = new TileLayer(QLatin1String(""), 0, 0, mWidth, mHeight);

Q_ASSERT(direction == FlipHorizontally || direction == FlipVertically);

Expand All @@ -305,28 +316,40 @@ void TileLayer::flipHexagonal(FlipDirection direction)

const char (&flipMask)[16] = (direction == FlipHorizontally ? flipMaskH : flipMaskV);

for (int y = 0; y < mHeight; ++y) {
for (int x = 0; x < mWidth; ++x) {
const Cell &source = (direction == FlipHorizontally ? cellAt(mWidth - x - 1, y) : cellAt(x, mHeight - y - 1));
Cell &dest = newGrid[x + y * mWidth];
dest = source;

unsigned char mask =
(static_cast<unsigned char>(dest.flippedHorizontally()) << 3) |
(static_cast<unsigned char>(dest.flippedVertically()) << 2) |
(static_cast<unsigned char>(dest.flippedAntiDiagonally()) << 1) |
(static_cast<unsigned char>(dest.rotatedHexagonal120()) << 0);

mask = flipMask[mask];

dest.setFlippedHorizontally((mask & 8) != 0);
dest.setFlippedVertically((mask & 4) != 0);
dest.setFlippedAntiDiagonally((mask & 2) != 0);
dest.setRotatedHexagonal120((mask & 1) != 0);
QHashIterator<QPoint, Chunk* > it(mChunks);
while (it.hasNext()) {
it.next();
for (int y = 0; y < CHUNK_SIZE; ++y) {
for (int x = 0; x < CHUNK_SIZE; ++x) {
int _x = it.key().x() * CHUNK_SIZE + x;
int _y = it.key().y() * CHUNK_SIZE + y;

if (!contains(_x, _y))
continue;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think instead of doing this contains check each time, it would be better to check dest.isEmpty() below and continue then. This applies to the other functions as well.


Cell dest(it.value()->cellAt(x, y));

unsigned char mask =
(static_cast<unsigned char>(dest.flippedHorizontally()) << 3) |
(static_cast<unsigned char>(dest.flippedVertically()) << 2) |
(static_cast<unsigned char>(dest.flippedAntiDiagonally()) << 1) |
(static_cast<unsigned char>(dest.rotatedHexagonal120()) << 0);

mask = flipMask[mask];

dest.setFlippedHorizontally((mask & 8) != 0);
dest.setFlippedVertically((mask & 4) != 0);
dest.setFlippedAntiDiagonally((mask & 2) != 0);
dest.setRotatedHexagonal120((mask & 1) != 0);

(direction == FlipHorizontally) ? newLayer->setCell(mWidth - _x - 1, _y, dest)
: newLayer->setCell(_x, mHeight - _y - 1, dest);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please write this as an if, else

}
}
}

copyGrid(newGrid);
mChunks = newLayer->mChunks;
delete newLayer;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realized this only worked because we were never deleting the chunks. I've fixed that leak by pushing a change where the chunk instances are managed by the QHash.

Btw, instead of manual deletion, please declare the newLayer as:

QScopedPointer<TileLayer> newLayer

}

void TileLayer::rotate(RotateDirection direction)
Expand All @@ -339,34 +362,44 @@ void TileLayer::rotate(RotateDirection direction)

int newWidth = mHeight;
int newHeight = mWidth;
QVector<Cell> newGrid(newWidth * newHeight);
TileLayer *newLayer = new TileLayer(QLatin1String(""), 0, 0, newWidth, newHeight);

for (int y = 0; y < mHeight; ++y) {
for (int x = 0; x < mWidth; ++x) {
const Cell &source = cellAt(x, y);
Cell dest = source;
QHashIterator<QPoint, Chunk* > it(mChunks);
while (it.hasNext()) {
it.next();
for (int y = 0; y < CHUNK_SIZE; ++y) {
for (int x = 0; x < CHUNK_SIZE; ++x) {
int _x = it.key().x() * CHUNK_SIZE + x;
int _y = it.key().y() * CHUNK_SIZE + y;

unsigned char mask =
(dest.flippedHorizontally() << 2) |
(dest.flippedVertically() << 1) |
(dest.flippedAntiDiagonally() << 0);
if (!contains(_x, _y))
continue;

mask = rotateMask[mask];
Cell dest(it.value()->cellAt(x, y));

dest.setFlippedHorizontally((mask & 4) != 0);
dest.setFlippedVertically((mask & 2) != 0);
dest.setFlippedAntiDiagonally((mask & 1) != 0);
unsigned char mask =
(dest.flippedHorizontally() << 2) |
(dest.flippedVertically() << 1) |
(dest.flippedAntiDiagonally() << 0);

if (direction == RotateRight)
newGrid[x * newWidth + (mHeight - y - 1)] = dest;
else
newGrid[(mWidth - x - 1) * newWidth + y] = dest;
mask = rotateMask[mask];

dest.setFlippedHorizontally((mask & 4) != 0);
dest.setFlippedVertically((mask & 2) != 0);
dest.setFlippedAntiDiagonally((mask & 1) != 0);

if (direction == RotateRight)
newLayer->setCell(mHeight - _y - 1, _x, dest);
else
newLayer->setCell(_y, mWidth - _x - 1, dest);
}
}
}

mWidth = newWidth;
mHeight = newHeight;
copyGrid(newGrid);
mChunks = newLayer->mChunks;
delete newLayer;
}

void TileLayer::rotateHexagonal(RotateDirection direction, Map *map)
Expand All @@ -386,7 +419,7 @@ void TileLayer::rotateHexagonal(RotateDirection direction, Map *map)

int newWidth = topRight.toStaggered(staggerIndex, staggerAxis).x() * 2 + 2;
int newHeight = bottomRight.toStaggered(staggerIndex, staggerAxis).y() * 2 + 2;
QVector<Cell> newGrid(newWidth * newHeight);
TileLayer *newLayer = new TileLayer(QLatin1String(""), 0, 0, newWidth, newHeight);

Hex newCenter(newWidth / 2, newHeight / 2, staggerIndex, staggerAxis);

Expand Down Expand Up @@ -414,40 +447,48 @@ void TileLayer::rotateHexagonal(RotateDirection direction, Map *map)
const char (&rotateMask)[16] =
(direction == RotateRight) ? rotateRightMask : rotateLeftMask;

for (int y = 0; y < mHeight; ++y) {
for (int x = 0; x < mWidth; ++x) {
const Cell &source = cellAt(x, y);
Cell dest = source;
QHashIterator<QPoint, Chunk* > it(mChunks);
while (it.hasNext()) {
it.next();
for (int y = 0; y < CHUNK_SIZE; ++y) {
for (int x = 0; x < CHUNK_SIZE; ++x) {
int _x = it.key().x() * CHUNK_SIZE + x;
int _y = it.key().y() * CHUNK_SIZE + y;

if (!contains(_x, _y))
continue;

unsigned char mask =
(static_cast<unsigned char>(dest.flippedHorizontally()) << 3) |
(static_cast<unsigned char>(dest.flippedVertically()) << 2) |
(static_cast<unsigned char>(dest.flippedAntiDiagonally()) << 1) |
(static_cast<unsigned char>(dest.rotatedHexagonal120()) << 0);
Cell dest(it.value()->cellAt(x, y));

mask = rotateMask[mask];
unsigned char mask =
(static_cast<unsigned char>(dest.flippedHorizontally()) << 3) |
(static_cast<unsigned char>(dest.flippedVertically()) << 2) |
(static_cast<unsigned char>(dest.flippedAntiDiagonally()) << 1) |
(static_cast<unsigned char>(dest.rotatedHexagonal120()) << 0);

dest.setFlippedHorizontally((mask & 8) != 0);
dest.setFlippedVertically((mask & 4) != 0);
dest.setFlippedAntiDiagonally((mask & 2) != 0);
dest.setRotatedHexagonal120((mask & 1) != 0);
mask = rotateMask[mask];

Hex rotatedHex(x, y, staggerIndex, staggerAxis);
rotatedHex -= center;
rotatedHex.rotate(direction);
rotatedHex += newCenter;
dest.setFlippedHorizontally((mask & 8) != 0);
dest.setFlippedVertically((mask & 4) != 0);
dest.setFlippedAntiDiagonally((mask & 2) != 0);
dest.setRotatedHexagonal120((mask & 1) != 0);

QPoint rotatedPoint = rotatedHex.toStaggered(staggerIndex, staggerAxis);
Hex rotatedHex(_x, _y, staggerIndex, staggerAxis);
rotatedHex -= center;
rotatedHex.rotate(direction);
rotatedHex += newCenter;

int index = rotatedPoint.y() * newWidth + rotatedPoint.x();
QPoint rotatedPoint = rotatedHex.toStaggered(staggerIndex, staggerAxis);

newGrid[index] = dest;
newLayer->setCell(rotatedPoint.x(), rotatedPoint.y(), dest);
}
}
}

mWidth = newWidth;
mHeight = newHeight;
copyGrid(newGrid);
mChunks = newLayer->mChunks;
delete newLayer;

QRect filledRect = region().boundingRect();

Expand Down Expand Up @@ -551,45 +592,56 @@ void TileLayer::offsetTiles(const QPoint &offset,
const QRect &bounds,
bool wrapX, bool wrapY)
{
QVector<Cell> newGrid(mWidth * mHeight);
TileLayer *newLayer = clone();

for (int y = 0; y < mHeight; ++y) {
for (int x = 0; x < mWidth; ++x) {
// Skip out of bounds tiles
if (!bounds.contains(x, y)) {
newGrid[x + y * mWidth] = cellAt(x, y);
continue;
}
QHashIterator<QPoint, Chunk* > it(newLayer->mChunks);
while (it.hasNext()) {
it.next();
for (int y = 0; y < CHUNK_SIZE; ++y) {
for (int x = 0; x < CHUNK_SIZE; ++x) {
int _x = it.key().x() * CHUNK_SIZE + x;
int _y = it.key().y() * CHUNK_SIZE + y;

if (!contains(_x, _y))
continue;

// Skip out of bounds tiles
if (!bounds.contains(_x, _y)) {
newLayer->setCell(x, y, cellAt(_x, _y));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to do this, since the newLayer is already a clone of the current layer. Actually, this means that you should only iterate the area indicated by bounds. Iterating just the chunks is not enough, since it could fail to include the needed area.

Copy link
Contributor Author

@ketanhwr ketanhwr Jul 4, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would mean iterating like this, right?

    for (int x = bounds.x(); x < bounds.x() + bounds.width(); ++x) {
        for (int y = bound.y(); y < bounds.y() + bounds.height(); ++y) {
            ...
        }
    }

Copy link
Member

@bjorn bjorn Jul 4, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, though you can use top, left, right and bottom of QRect instead (noting the <=):

    for (int y = bounds.top(); y <= bounds.bottom(); ++y) {
        for (int x = bounds.left(); x <= bounds.right(); ++x) {
            ...
        }
    }

Edit: and reversed the iteration order.

continue;
}

// Get position to pull tile value from
int oldX = x - offset.x();
int oldY = y - offset.y();
// Get position to pull tile value from
int oldX = _x - offset.x();
int oldY = _y - offset.y();

// Wrap x value that will be pulled from
if (wrapX && bounds.width() > 0) {
while (oldX < bounds.left())
oldX += bounds.width();
while (oldX > bounds.right())
oldX -= bounds.width();
}
// Wrap x value that will be pulled from
if (wrapX && bounds.width() > 0) {
while (oldX < bounds.left())
oldX += bounds.width();
while (oldX > bounds.right())
oldX -= bounds.width();
}

// Wrap y value that will be pulled from
if (wrapY && bounds.height() > 0) {
while (oldY < bounds.top())
oldY += bounds.height();
while (oldY > bounds.bottom())
oldY -= bounds.height();
}
// Wrap y value that will be pulled from
if (wrapY && bounds.height() > 0) {
while (oldY < bounds.top())
oldY += bounds.height();
while (oldY > bounds.bottom())
oldY -= bounds.height();
}

// Set the new tile
if (contains(oldX, oldY) && bounds.contains(oldX, oldY))
newGrid[x + y * mWidth] = cellAt(oldX, oldY);
else
newGrid[x + y * mWidth] = Cell();
// Set the new tile
if (contains(oldX, oldY) && bounds.contains(oldX, oldY))
newLayer->setCell(x, y, cellAt(oldX, oldY));
else
newLayer->setCell(x, y, Cell());
}
}
}

copyGrid(newGrid);
mChunks = newLayer->mChunks;
delete newLayer;
}

bool TileLayer::canMergeWith(Layer *other) const
Expand Down Expand Up @@ -669,12 +721,3 @@ TileLayer *TileLayer::initializeClone(TileLayer *clone) const
clone->mUsedTilesetsDirty = mUsedTilesetsDirty;
return clone;
}

void TileLayer::copyGrid(const QVector<Cell> &newGrid)
{
for (int y = 0; y < mHeight; ++y) {
for (int x = 0; x < mWidth; ++x) {
setCell(x, y, newGrid[x + y * mWidth]);
}
}
}
2 changes: 0 additions & 2 deletions src/libtiled/tilelayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -395,8 +395,6 @@ class TILEDSHARED_EXPORT TileLayer : public Layer

TileLayer *clone() const override;

void copyGrid(const QVector<Cell> &newGrid);

protected:
TileLayer *initializeClone(TileLayer *clone) const;

Expand Down