-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
Supporting Infinite Maps #1635
Changes from 2 commits
2d1a3c8
44c2e2c
e4f1621
3650afd
309db5c
011043d
bc1c9c4
e937a65
ea2163c
9046a38
42ec140
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,11 +34,71 @@ | |
|
||
using namespace Tiled; | ||
|
||
QRegion Block::region(std::function<bool (const Cell &)> condition) const | ||
{ | ||
QRegion region; | ||
|
||
for (int y = 0; y < CHUNK_SIZE; ++y) { | ||
for (int x = 0; x < CHUNK_SIZE; ++x) { | ||
if (condition(cellAt(x, y))) { | ||
const int rangeStart = x; | ||
for (++x; x <= CHUNK_SIZE; ++x) { | ||
if (x == CHUNK_SIZE || !condition(cellAt(x, y))) { | ||
const int rangeEnd = x; | ||
region += QRect(rangeStart, y, rangeEnd - rangeStart, 1); | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
return region; | ||
} | ||
|
||
void Block::setCell(int x, int y, const Cell &cell) | ||
{ | ||
int index = x + y * CHUNK_SIZE; | ||
|
||
if (mGrid[index].isEmpty() && !cell.isEmpty()) | ||
mCells++; | ||
else if (!mGrid[index].isEmpty() && cell.isEmpty()) | ||
mCells--; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would effectively do the same thing with less checking: if (!mGrid[index].isEmpty())
--mCells;
else if (!cell.isEmpty())
++mCells; If an empty cell gets overwritten with an empty cell, nothing happens. If a non-empty cell gets overwritten with a non-empty cell, it decrements and then increments, which is fine as well. Or even without branching at all: mCells -= !mGrid[index].isEmpty();
mCells += !cell.isEmpty(); This is using the fact that a boolean will be either 0 or 1 (not sure if the compiler will warn, though, then a And we could even remove the negation: mCells += mGrid[index].isEmpty();
mCells -= cell.isEmpty(); The first line increments mCells += mGrid[index].isEmpty() - cell.isEmpty(); There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Awesome! |
||
|
||
mGrid[index] = cell; | ||
} | ||
|
||
bool Block::hasCell(std::function<bool (const Cell &)> condition) const | ||
{ | ||
for (const Cell &cell : mGrid) | ||
if (condition(cell)) | ||
return true; | ||
|
||
return false; | ||
} | ||
|
||
void Block::removeReferencesToTileset(Tileset *tileset) | ||
{ | ||
for (int i = 0, i_end = mGrid.size(); i < i_end; ++i) { | ||
if (mGrid.at(i).tileset() == tileset) | ||
mGrid.replace(i, Cell()); | ||
} | ||
} | ||
|
||
void Block::replaceReferencesToTileset(Tileset *oldTileset, | ||
Tileset *newTileset) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indentation is off. |
||
{ | ||
for (Cell &cell : mGrid) { | ||
if (cell.tileset() == oldTileset) | ||
cell.setTile(newTileset, cell.tileId()); | ||
} | ||
} | ||
|
||
TileLayer::TileLayer(const QString &name, int x, int y, int width, int height) | ||
: Layer(TileLayerType, name, x, y) | ||
, mWidth(width) | ||
, mHeight(height) | ||
, mGrid(width * height) | ||
, mEmptyCell(Cell()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're calling the copy constructor here to initialize |
||
, mUsedTilesetsDirty(false) | ||
{ | ||
Q_ASSERT(width >= 0); | ||
|
@@ -88,20 +148,11 @@ QRegion TileLayer::region(std::function<bool (const Cell &)> condition) const | |
{ | ||
QRegion region; | ||
|
||
for (int y = 0; y < mHeight; ++y) { | ||
for (int x = 0; x < mWidth; ++x) { | ||
if (condition(cellAt(x, y))) { | ||
const int rangeStart = x; | ||
for (++x; x <= mWidth; ++x) { | ||
if (x == mWidth || !condition(cellAt(x, y))) { | ||
const int rangeEnd = x; | ||
region += QRect(rangeStart + mX, y + mY, | ||
rangeEnd - rangeStart, 1); | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
QMapIterator< QPair<int, int>, Block* > it(mMap); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the reason for using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In a map, the key should of such a data type such that it can be easily compared. QPair has operators for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a good point, however we don't need this container to be sorted and if you use a |
||
while (it.hasNext()) { | ||
it.next(); | ||
region += it.value()->region(condition).translated(it.key().first * CHUNK_SIZE + mX, | ||
it.key().second * CHUNK_SIZE + mY); | ||
} | ||
|
||
return region; | ||
|
@@ -114,7 +165,12 @@ void Tiled::TileLayer::setCell(int x, int y, const Cell &cell) | |
{ | ||
Q_ASSERT(contains(x, y)); | ||
|
||
Cell &existingCell = mGrid[x + y * mWidth]; | ||
Cell existingCell = Cell(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can just write: Cell existingCell; The default constructor will be called either way. |
||
|
||
if (mMap.contains(block(x, y))) | ||
existingCell = mMap[block(x, y)]->cellAt(x % CHUNK_SIZE, y % CHUNK_SIZE); | ||
else | ||
mMap[block(x, y)] = new Block(); | ||
|
||
if (!mUsedTilesetsDirty) { | ||
Tileset *oldTileset = existingCell.isEmpty() ? nullptr : existingCell.tileset(); | ||
|
@@ -127,7 +183,12 @@ void Tiled::TileLayer::setCell(int x, int y, const Cell &cell) | |
} | ||
} | ||
|
||
existingCell = cell; | ||
mMap[block(x, y)]->setCell(x % CHUNK_SIZE, y % CHUNK_SIZE, cell); | ||
|
||
if (mMap[block(x, y)]->isEmpty()) { | ||
delete mMap[block(x, y)]; | ||
mMap.remove(block(x, y)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can be done in one line: delete mMap.take(block(x, y)); |
||
} | ||
} | ||
|
||
TileLayer *TileLayer::copy(const QRegion ®ion) const | ||
|
@@ -233,7 +294,7 @@ void TileLayer::flip(FlipDirection direction) | |
} | ||
} | ||
|
||
mGrid = newGrid; | ||
copyGrid(newGrid); | ||
} | ||
|
||
void TileLayer::flipHexagonal(FlipDirection direction) | ||
|
@@ -269,7 +330,7 @@ void TileLayer::flipHexagonal(FlipDirection direction) | |
} | ||
} | ||
|
||
mGrid = newGrid; | ||
copyGrid(newGrid); | ||
} | ||
|
||
void TileLayer::rotate(RotateDirection direction) | ||
|
@@ -309,7 +370,7 @@ void TileLayer::rotate(RotateDirection direction) | |
|
||
mWidth = newWidth; | ||
mHeight = newHeight; | ||
mGrid = newGrid; | ||
copyGrid(newGrid); | ||
} | ||
|
||
void TileLayer::rotateHexagonal(RotateDirection direction, Map *map) | ||
|
@@ -390,7 +451,7 @@ void TileLayer::rotateHexagonal(RotateDirection direction, Map *map) | |
|
||
mWidth = newWidth; | ||
mHeight = newHeight; | ||
mGrid = newGrid; | ||
copyGrid(newGrid); | ||
|
||
QRect filledRect = region().boundingRect(); | ||
|
||
|
@@ -411,22 +472,30 @@ QSet<SharedTileset> TileLayer::usedTilesets() const | |
if (mUsedTilesetsDirty) { | ||
QSet<SharedTileset> tilesets; | ||
|
||
for (const Cell &cell : mGrid) | ||
if (const Tile *tile = cell.tile()) | ||
tilesets.insert(tile->sharedTileset()); | ||
QMapIterator< QPair<int, int>, Block* > it(mMap); | ||
while (it.hasNext()) { | ||
it.next(); | ||
|
||
mUsedTilesets.swap(tilesets); | ||
mUsedTilesetsDirty = false; | ||
for (const Cell &cell : *it.value()) | ||
if (const Tile *tile = cell.tile()) | ||
tilesets.insert(tile->sharedTileset()); | ||
|
||
mUsedTilesets.swap(tilesets); | ||
mUsedTilesetsDirty = false; | ||
} | ||
} | ||
|
||
return mUsedTilesets; | ||
} | ||
|
||
bool TileLayer::hasCell(std::function<bool (const Cell &)> condition) const | ||
{ | ||
for (const Cell &cell : mGrid) | ||
if (condition(cell)) | ||
QMapIterator< QPair<int, int>, Block* > it(mMap); | ||
while (it.hasNext()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since you don't care about the coordinates of the chunk, please use a range-based for: for (Block *block : mMap) Same for |
||
it.next(); | ||
if (it.value()->hasCell(condition)) | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
|
@@ -438,9 +507,10 @@ bool TileLayer::referencesTileset(const Tileset *tileset) const | |
|
||
void TileLayer::removeReferencesToTileset(Tileset *tileset) | ||
{ | ||
for (int i = 0, i_end = mGrid.size(); i < i_end; ++i) { | ||
if (mGrid.at(i).tileset() == tileset) | ||
mGrid.replace(i, Cell()); | ||
QMapIterator< QPair<int, int>, Block* > it(mMap); | ||
while (it.hasNext()) { | ||
it.next(); | ||
it.value()->removeReferencesToTileset(tileset); | ||
} | ||
|
||
mUsedTilesets.remove(tileset->sharedPointer()); | ||
|
@@ -449,9 +519,10 @@ void TileLayer::removeReferencesToTileset(Tileset *tileset) | |
void TileLayer::replaceReferencesToTileset(Tileset *oldTileset, | ||
Tileset *newTileset) | ||
{ | ||
for (Cell &cell : mGrid) { | ||
if (cell.tileset() == oldTileset) | ||
cell.setTile(newTileset, cell.tileId()); | ||
QMapIterator< QPair<int, int>, Block* > it(mMap); | ||
while (it.hasNext()) { | ||
it.next(); | ||
it.value()->replaceReferencesToTileset(oldTileset, newTileset); | ||
} | ||
|
||
if (mUsedTilesets.remove(oldTileset->sharedPointer())) | ||
|
@@ -478,7 +549,14 @@ void TileLayer::resize(const QSize &size, const QPoint &offset) | |
} | ||
} | ||
|
||
mGrid = newGrid; | ||
for (int y = 0; y < mHeight; ++y) { | ||
for (int x = 0; x < mWidth; ++x) { | ||
if (y < size.height() && x < size.width()) | ||
setCell(x, y, newGrid[x + y * size.width()]); | ||
else | ||
setCell(x, y, mEmptyCell); | ||
} | ||
} | ||
setSize(size); | ||
} | ||
|
||
|
@@ -524,7 +602,7 @@ void TileLayer::offsetTiles(const QPoint &offset, | |
} | ||
} | ||
|
||
mGrid = newGrid; | ||
copyGrid(newGrid); | ||
} | ||
|
||
bool TileLayer::canMergeWith(Layer *other) const | ||
|
@@ -574,9 +652,12 @@ QRegion TileLayer::computeDiffRegion(const TileLayer *other) const | |
|
||
bool TileLayer::isEmpty() const | ||
{ | ||
for (const Cell &cell : mGrid) | ||
if (!cell.isEmpty()) | ||
QMapIterator< QPair<int, int>, Block* > it(mMap); | ||
while (it.hasNext()) { | ||
it.next(); | ||
if (!it.value()->isEmpty()) | ||
return false; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. there seems to be missing a call to |
||
|
||
return true; | ||
} | ||
|
@@ -594,8 +675,19 @@ TileLayer *TileLayer::clone() const | |
TileLayer *TileLayer::initializeClone(TileLayer *clone) const | ||
{ | ||
Layer::initializeClone(clone); | ||
clone->mGrid = mGrid; | ||
for (int y = 0; y < mHeight; ++y) | ||
for (int x = 0; x < mWidth; ++x) | ||
clone->setCell(x, y, cellAt(x, y)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of copying each cell over individually, it will be much faster to iterate |
||
clone->mUsedTilesets = mUsedTilesets; | ||
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]); | ||
} | ||
} | ||
} |
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.
Either this should be
BLOCK_SIZE
or theBlock
class should be renamed toChunk
. I think I'd prefer the latter.Btw, note that a
const int
at namespace level is implicitlystatic
, so that keyword is redundant.