1 /*
2 * tilelayer.h
3 * Copyright 2008-2011, Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
4 * Copyright 2009, Jeff Bland <jksb@member.fsf.org>
5 *
6 * This file is part of libtiled.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
21 * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
24 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
27 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #pragma once
31
32 #include "tiled_global.h"
33
34 #include "layer.h"
35 #include "tiled.h"
36 #include "tile.h"
37 #include "tileset.h"
38
39 #include <QHash>
40 #include <QMargins>
41 #include <QPoint>
42 #include <QSharedPointer>
43 #include <QString>
44 #include <QVector>
45
46 #include <functional>
47
48 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
49 inline uint qHash(QPoint key, uint seed = 0) Q_DECL_NOTHROW
50 {
51 uint h1 = qHash(key.x(), seed);
52 uint h2 = qHash(key.y(), seed);
53 return ((h1 << 16) | (h1 >> 16)) ^ h2 ^ seed;
54 }
55 #endif
56
57 namespace Tiled {
58
59 class Tile;
60
61 /**
62 * A cell on a tile layer grid.
63 */
64 class TILEDSHARED_EXPORT Cell
65 {
66 Q_GADGET
67
68 Q_PROPERTY(int tileId READ tileId)
69 Q_PROPERTY(bool empty READ isEmpty)
70 Q_PROPERTY(bool flippedHorizontally READ flippedHorizontally WRITE setFlippedHorizontally)
71 Q_PROPERTY(bool flippedVertically READ flippedVertically WRITE setFlippedVertically)
72 Q_PROPERTY(bool flippedAntiDiagonally READ flippedAntiDiagonally WRITE setFlippedAntiDiagonally)
73 Q_PROPERTY(bool rotatedHexagonal120 READ rotatedHexagonal120 WRITE setRotatedHexagonal120)
74
75 public:
76 static Cell empty;
77
Cell()78 Cell() :
79 _tileset(nullptr),
80 _tileId(-1),
81 _flags(0)
82 {}
83
Cell(Tile * tile)84 explicit Cell(Tile *tile) :
85 _tileset(tile ? tile->tileset() : nullptr),
86 _tileId(tile ? tile->id() : -1),
87 _flags(0)
88 {}
89
Cell(Tileset * tileset,int tileId)90 Cell(Tileset *tileset, int tileId) :
91 _tileset(tileset),
92 _tileId(tileId),
93 _flags(0)
94 {}
95
isEmpty()96 bool isEmpty() const { return _tileset == nullptr; }
97
98 bool operator == (const Cell &other) const
99 {
100 return _tileset == other._tileset
101 && _tileId == other._tileId
102 && (_flags & VisualFlags) == (other._flags & VisualFlags);
103 }
104
105 bool operator != (const Cell &other) const
106 {
107 return !(*this == other);
108 }
109
tileset()110 Tileset *tileset() const { return _tileset; }
tileId()111 int tileId() const { return _tileId; }
112
flippedHorizontally()113 bool flippedHorizontally() const { return _flags & FlippedHorizontally; }
flippedVertically()114 bool flippedVertically() const { return _flags & FlippedVertically; }
flippedAntiDiagonally()115 bool flippedAntiDiagonally() const { return _flags & FlippedAntiDiagonally; }
rotatedHexagonal120()116 bool rotatedHexagonal120() const { return _flags & RotatedHexagonal120; }
117
setFlippedHorizontally(bool v)118 void setFlippedHorizontally(bool v) { v ? _flags |= FlippedHorizontally : _flags &= ~FlippedHorizontally; }
setFlippedVertically(bool v)119 void setFlippedVertically(bool v) { v ? _flags |= FlippedVertically : _flags &= ~FlippedVertically; }
setFlippedAntiDiagonally(bool v)120 void setFlippedAntiDiagonally(bool v) { v ? _flags |= FlippedAntiDiagonally : _flags &= ~FlippedAntiDiagonally; }
setRotatedHexagonal120(bool v)121 void setRotatedHexagonal120(bool v) { v ? _flags |= RotatedHexagonal120 : _flags &= ~RotatedHexagonal120; }
122
123 void rotate(RotateDirection direction);
124
checked()125 bool checked() const { return _flags & Checked; }
setChecked(bool checked)126 void setChecked(bool checked) { checked ? _flags |= Checked : _flags &= ~Checked; }
127
128 Tile *tile() const;
129 void setTile(Tileset *tileset, int tileId);
130 void setTile(Tile *tile);
131 bool refersTile(const Tile *tile) const;
132
133 private:
134 enum Flags {
135 FlippedHorizontally = 0x01,
136 FlippedVertically = 0x02,
137 FlippedAntiDiagonally = 0x04,
138 RotatedHexagonal120 = 0x08,
139 Checked = 0x10,
140 VisualFlags = FlippedHorizontally | FlippedVertically | FlippedAntiDiagonally | RotatedHexagonal120
141 };
142
143 Tileset *_tileset;
144 int _tileId;
145 int _flags;
146 };
147
tile()148 inline Tile *Cell::tile() const
149 {
150 return _tileset ? _tileset->findTile(_tileId) : nullptr;
151 }
152
setTile(Tileset * tileset,int tileId)153 inline void Cell::setTile(Tileset *tileset, int tileId)
154 {
155 _tileset = tileset;
156 _tileId = tileId;
157 }
158
setTile(Tile * tile)159 inline void Cell::setTile(Tile *tile)
160 {
161 if (tile)
162 setTile(tile->tileset(), tile->id());
163 else
164 setTile(nullptr, -1);
165 }
166
refersTile(const Tile * tile)167 inline bool Cell::refersTile(const Tile *tile) const
168 {
169 return _tileset == tile->tileset() && _tileId == tile->id();
170 }
171
172
173 /**
174 * A Chunk is a grid of cells of size CHUNK_SIZExCHUNK_SIZE.
175 */
176 class TILEDSHARED_EXPORT Chunk
177 {
178 public:
Chunk()179 Chunk() :
180 mGrid(CHUNK_SIZE * CHUNK_SIZE)
181 {}
182
183 QRegion region(std::function<bool (const Cell &)> condition) const;
184
185 const Cell &cellAt(int x, int y) const;
186 const Cell &cellAt(QPoint point) const;
187
188 void setCell(int x, int y, const Cell &cell);
189
190 bool isEmpty() const;
191
192 bool hasCell(std::function<bool (const Cell &)> condition) const;
193
194 void removeReferencesToTileset(Tileset *tileset);
195
196 void replaceReferencesToTileset(Tileset *oldTileset, Tileset *newTileset);
197
begin()198 QVector<Cell>::iterator begin() { return mGrid.begin(); }
end()199 QVector<Cell>::iterator end() { return mGrid.end(); }
begin()200 QVector<Cell>::const_iterator begin() const { return mGrid.begin(); }
end()201 QVector<Cell>::const_iterator end() const { return mGrid.end(); }
202
203 private:
204 QVector<Cell> mGrid;
205 };
206
cellAt(int x,int y)207 inline const Cell &Chunk::cellAt(int x, int y) const
208 {
209 return mGrid.at(x + y * CHUNK_SIZE);
210 }
211
cellAt(QPoint point)212 inline const Cell &Chunk::cellAt(QPoint point) const
213 {
214 return cellAt(point.x(), point.y());
215 }
216
217 /**
218 * A tile layer is a grid of cells. Each cell refers to a specific tile, and
219 * stores how the tile is flipped.
220 *
221 * Coordinates and regions passed to function parameters are in local
222 * coordinates and do not take into account the position of the layer.
223 */
224 class TILEDSHARED_EXPORT TileLayer : public Layer
225 {
226 public:
227 class iterator
228 {
229 public:
iterator(QHash<QPoint,Chunk>::iterator it,QHash<QPoint,Chunk>::iterator end)230 iterator(QHash<QPoint, Chunk>::iterator it, QHash<QPoint, Chunk>::iterator end)
231 : mChunkPointer(it)
232 , mChunkEndPointer(end)
233 {
234 if (it != end)
235 mCellPointer = it.value().begin();
236 }
237
238 iterator operator++(int)
239 {
240 iterator it = *this;
241 advance();
242 return it;
243 }
244
245 iterator &operator++()
246 {
247 advance();
248 return *this;
249 }
250
251 Cell &operator*() { return *mCellPointer; }
252
253 QVector<Cell>::iterator operator->() const { return mCellPointer; }
254
255 friend bool operator==(const iterator& lhs, const iterator& rhs)
256 {
257 if (lhs.mChunkPointer == lhs.mChunkEndPointer || rhs.mChunkPointer == rhs.mChunkEndPointer)
258 return lhs.mChunkPointer == rhs.mChunkPointer;
259 else
260 return lhs.mCellPointer == rhs.mCellPointer;
261 }
262
263 friend bool operator!=(const iterator& lhs, const iterator& rhs)
264 {
265 if (lhs.mChunkPointer == lhs.mChunkEndPointer || rhs.mChunkPointer == rhs.mChunkEndPointer)
266 return lhs.mChunkPointer != rhs.mChunkPointer;
267 else
268 return lhs.mCellPointer != rhs.mCellPointer;
269 }
270
value()271 Cell &value() const { return *mCellPointer; }
272
273 QPoint key() const;
274
275 private:
276 void advance();
277
278 QHash<QPoint, Chunk>::iterator mChunkPointer;
279 QHash<QPoint, Chunk>::iterator mChunkEndPointer;
280 QVector<Cell>::iterator mCellPointer;
281 };
282
283 class const_iterator
284 {
285 public:
const_iterator(QHash<QPoint,Chunk>::const_iterator it,QHash<QPoint,Chunk>::const_iterator end)286 const_iterator(QHash<QPoint, Chunk>::const_iterator it, QHash<QPoint, Chunk>::const_iterator end)
287 : mChunkPointer(it)
288 , mChunkEndPointer(end)
289 {
290 if (it != end)
291 mCellPointer = it.value().begin();
292 }
293
294 const_iterator operator++(int)
295 {
296 const_iterator it = *this;
297 advance();
298 return it;
299 }
300
301 const_iterator &operator++()
302 {
303 advance();
304 return *this;
305 }
306
307 const Cell &operator*() { return *mCellPointer; }
308
309 QVector<Cell>::const_iterator operator->() const { return mCellPointer; }
310
311 friend bool operator==(const const_iterator& lhs, const const_iterator& rhs)
312 {
313 if (lhs.mChunkPointer == lhs.mChunkEndPointer || rhs.mChunkPointer == rhs.mChunkEndPointer)
314 return lhs.mChunkPointer == rhs.mChunkPointer;
315 else
316 return lhs.mCellPointer == rhs.mCellPointer;
317 }
318
319 friend bool operator!=(const const_iterator& lhs, const const_iterator& rhs)
320 {
321 if (lhs.mChunkPointer == lhs.mChunkEndPointer || rhs.mChunkPointer == rhs.mChunkEndPointer)
322 return lhs.mChunkPointer != rhs.mChunkPointer;
323 else
324 return lhs.mCellPointer != rhs.mCellPointer;
325 }
326
value()327 const Cell &value() const { return *mCellPointer; }
328
329 QPoint key() const;
330
331 private:
332 void advance();
333
334 QHash<QPoint, Chunk>::const_iterator mChunkPointer;
335 QHash<QPoint, Chunk>::const_iterator mChunkEndPointer;
336 QVector<Cell>::const_iterator mCellPointer;
337 };
338
339 /**
340 * Constructor.
341 */
342 TileLayer(const QString &name, int x, int y, int width, int height);
343
344 TileLayer(const QString &name = QString(),
345 QPoint position = QPoint(),
346 QSize size = QSize(0, 0));
347
348 /**
349 * Returns the width of this layer.
350 */
width()351 int width() const { return mWidth; }
352
353 /**
354 * Returns the height of this layer.
355 */
height()356 int height() const { return mHeight; }
357
358 /**
359 * Returns the size of this layer.
360 */
size()361 QSize size() const { return QSize(mWidth, mHeight); }
362
363 void setSize(QSize size);
364
365 /**
366 * Returns the bounds of this layer in map tile coordinates.
367 */
bounds()368 QRect bounds() const { return mBounds.translated(mX, mY); }
369
370 /**
371 * Returns the bounds of this layer in local tile coordinates.
372 */
localBounds()373 QRect localBounds() const { return mBounds; }
374
rect()375 QRect rect() const { return QRect(mX, mY, mWidth, mHeight); }
376
377 QMargins drawMargins() const;
378
379 bool contains(int x, int y) const;
380 bool contains(QPoint point) const;
381
382 Chunk &chunk(int x, int y);
383
384 const Chunk *findChunk(int x, int y) const;
385
386 QRegion region(std::function<bool (const Cell &)> condition) const;
387 QRegion region() const;
388
389 const Cell &cellAt(int x, int y) const;
390 const Cell &cellAt(QPoint point) const;
391
392 void setCell(int x, int y, const Cell &cell);
393
394 /**
395 * Returns a copy of the area specified by the given \a region. The
396 * caller is responsible for the returned tile layer.
397 */
398 std::unique_ptr<TileLayer> copy(const QRegion ®ion) const;
399
copy(int x,int y,int width,int height)400 std::unique_ptr<TileLayer> copy(int x, int y, int width, int height) const
401 { return copy(QRegion(x, y, width, height)); }
402
403 /**
404 * Merges the given \a layer onto this layer at position \a pos. Parts that
405 * fall outside of this layer will be lost and empty tiles in the given
406 * layer will have no effect.
407 */
408 void merge(QPoint pos, const TileLayer *layer);
409
410 /**
411 * Removes all cells in the specified region.
412 */
413 void erase(const QRegion ®ion);
414
415 void clear();
416
417 /**
418 * Sets the cells within the given \a area to the cells in the given
419 * \a tileLayer. The tiles in \a tileLayer are offset by \a x and \a y.
420 */
421 void setCells(int x, int y, const TileLayer *tileLayer, const QRegion &area);
422
423 /**
424 * Sets the cells starting at the given position to the cells in the given
425 * \a tileLayer.
426 */
427 void setCells(int x, int y, const TileLayer *tileLayer);
428
429 void setTiles(const QRegion &area, Tile *tile);
430
431 /**
432 * Flip this tile layer in the given \a direction. Direction must be
433 * horizontal or vertical. This doesn't change the dimensions of the
434 * tile layer.
435 */
436 void flip(FlipDirection direction);
437
438 /**
439 * Hexagonal flip this tile layer in the given \a direction. Direction must be
440 * horizontal or vertical. This doesn't change the dimensions of the
441 * tile layer.
442 */
443 void flipHexagonal(FlipDirection direction);
444
445 /**
446 * Rotate this tile layer by 90 degrees left or right. The tile positions
447 * are rotated within the layer, and the tiles themselves are rotated. The
448 * dimensions of the tile layer are swapped.
449 */
450 void rotate(RotateDirection direction);
451
452 /**
453 * Hexagonal rotate this tile layer by 60 degrees left or right. The tile positions
454 * are rotated within the layer, and the tiles themselves are rotated.
455 * As a temporary measure, a Map* is passed to give information about stagger index
456 * and axis, which affects rotation. The stagger index of this map can change.
457 */
458 void rotateHexagonal(RotateDirection direction, Map *map);
459
460 /**
461 * Computes and returns the set of tilesets used by this tile layer.
462 */
463 QSet<SharedTileset> usedTilesets() const override;
464
465 /**
466 * Returns whether this tile layer has any cell for which the given
467 * \a condition returns true.
468 */
469 bool hasCell(std::function<bool (const Cell &)> condition) const;
470
471 /**
472 * Returns whether this tile layer is referencing the given tileset.
473 */
474 bool referencesTileset(const Tileset *tileset) const override;
475
476 /**
477 * Removes all references to the given tileset. This sets all tiles on this
478 * layer that are from the given tileset to null.
479 */
480 void removeReferencesToTileset(Tileset *tileset);
481
482 /**
483 * Replaces all tiles from \a oldTileset with tiles from \a newTileset.
484 */
485 void replaceReferencesToTileset(Tileset *oldTileset,
486 Tileset *newTileset) override;
487
488 /**
489 * Resizes this tile layer to \a size, while shifting all tiles by
490 * \a offset.
491 */
492 void resize(QSize size, QPoint offset);
493
494 /**
495 * Offsets the tiles in this layer within \a bounds by \a offset,
496 * and optionally wraps them.
497 *
498 * \sa ObjectGroup::offsetObjects()
499 */
500 void offsetTiles(QPoint offset,
501 QRect bounds,
502 bool wrapX, bool wrapY);
503
504 /**
505 * Offsets the tiles in this layer by \a offset.
506 *
507 * \sa ObjectGroup::offsetObjects()
508 */
509 void offsetTiles(QPoint offset);
510
511 bool canMergeWith(const Layer *other) const override;
512 Layer *mergedWith(const Layer *other) const override;
513
514 /**
515 * Returns the region where this tile layer and the given tile layer
516 * are different. The relative positions of the layers are taken into
517 * account. The returned region is relative to this tile layer.
518 */
519 QRegion computeDiffRegion(const TileLayer *other) const;
520
521 /**
522 * Returns true if all tiles in the layer are empty.
523 */
524 bool isEmpty() const override;
525
526 TileLayer *clone() const override;
527
begin()528 iterator begin() { return iterator(mChunks.begin(), mChunks.end()); }
end()529 iterator end() { return iterator(mChunks.end(), mChunks.end()); }
begin()530 const_iterator begin() const { return const_iterator(mChunks.begin(), mChunks.end()); }
end()531 const_iterator end() const { return const_iterator(mChunks.end(), mChunks.end()); }
532
533 QVector<QRect> sortedChunksToWrite(QSize chunkSize) const;
534
535 protected:
536 TileLayer *initializeClone(TileLayer *clone) const;
537
538 private:
539 int mWidth;
540 int mHeight;
541 QHash<QPoint, Chunk> mChunks;
542 QRect mBounds;
543 mutable QSet<SharedTileset> mUsedTilesets;
544 mutable bool mUsedTilesetsDirty;
545 };
546
key()547 inline QPoint TileLayer::iterator::key() const
548 {
549 QPoint chunkStart = mChunkPointer.key();
550
551 int index = mCellPointer - mChunkPointer.value().begin();
552 chunkStart += QPoint(index & CHUNK_MASK, index / CHUNK_SIZE);
553
554 return chunkStart;
555 }
556
advance()557 inline void TileLayer::iterator::advance()
558 {
559 if (mChunkPointer != mChunkEndPointer) {
560 if (++mCellPointer == mChunkPointer.value().end()) {
561 mChunkPointer++;
562 if (mChunkPointer != mChunkEndPointer)
563 mCellPointer = mChunkPointer.value().begin();
564 }
565 }
566 }
567
key()568 inline QPoint TileLayer::const_iterator::key() const
569 {
570 QPoint chunkStart = mChunkPointer.key();
571
572 int index = mCellPointer - mChunkPointer.value().begin();
573 chunkStart += QPoint(index & CHUNK_MASK, index / CHUNK_SIZE);
574
575 return chunkStart;
576 }
577
advance()578 inline void TileLayer::const_iterator::advance()
579 {
580 if (mChunkPointer != mChunkEndPointer) {
581 if (++mCellPointer == mChunkPointer.value().end()) {
582 mChunkPointer++;
583 if (mChunkPointer != mChunkEndPointer)
584 mCellPointer = mChunkPointer.value().begin();
585 }
586 }
587 }
588
589 /**
590 * Sets the size of this layer.
591 */
setSize(QSize size)592 inline void TileLayer::setSize(QSize size)
593 {
594 mWidth = size.width();
595 mHeight = size.height();
596 }
597
contains(int x,int y)598 inline bool TileLayer::contains(int x, int y) const
599 {
600 return x >= 0 && y >= 0 && x < mWidth && y < mHeight;
601 }
602
contains(QPoint point)603 inline bool TileLayer::contains(QPoint point) const
604 {
605 return contains(point.x(), point.y());
606 }
607
chunk(int x,int y)608 inline Chunk& TileLayer::chunk(int x, int y)
609 {
610 const QPoint chunkCoordinates(x >> CHUNK_BITS, y >> CHUNK_BITS);
611 return mChunks[chunkCoordinates];
612 }
613
findChunk(int x,int y)614 inline const Chunk* TileLayer::findChunk(int x, int y) const
615 {
616 const QPoint chunkCoordinates(x >> CHUNK_BITS, y >> CHUNK_BITS);
617 auto it = mChunks.find(chunkCoordinates);
618 return it != mChunks.end() ? &it.value() : nullptr;
619 }
620
621 /**
622 * Calculates the region occupied by the tiles of this layer. Similar to
623 * Layer::bounds(), but leaves out the regions without tiles.
624 */
region()625 inline QRegion TileLayer::region() const
626 {
627 return region([] (const Cell &cell) { return !cell.isEmpty(); });
628 }
629
630 /**
631 * Returns a read-only reference to the cell at the given coordinates. The
632 * coordinates have to be within this layer.
633 */
cellAt(int x,int y)634 inline const Cell &TileLayer::cellAt(int x, int y) const
635 {
636 if (const Chunk *chunk = findChunk(x, y))
637 return chunk->cellAt(x & CHUNK_MASK, y & CHUNK_MASK);
638 else
639 return Cell::empty;
640 }
641
cellAt(QPoint point)642 inline const Cell &TileLayer::cellAt(QPoint point) const
643 {
644 return cellAt(point.x(), point.y());
645 }
646
setCells(int x,int y,const TileLayer * tileLayer)647 inline void TileLayer::setCells(int x, int y, const TileLayer *tileLayer)
648 {
649 setCells(x, y, tileLayer,
650 QRect(x, y, tileLayer->width(), tileLayer->height()));
651 }
652
653 typedef QSharedPointer<TileLayer> SharedTileLayer;
654
655 } // namespace Tiled
656
657 Q_DECLARE_METATYPE(Tiled::Cell)
658