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 &region) 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 &region);
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