1 /*
2  * wangset.h
3  * Copyright 2017, Benjamin Trotter <bdtrotte@ucsc.edu>
4  * This file is part of libtiled.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  *    1. Redistributions of source code must retain the above copyright notice,
10  *       this list of conditions and the following disclaimer.
11  *
12  *    2. Redistributions in binary form must reproduce the above copyright
13  *       notice, this list of conditions and the following disclaimer in the
14  *       documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #pragma once
29 
30 #include "tile.h"
31 #include "tileset.h"
32 #include "tilelayer.h"
33 
34 #include <QHash>
35 #include <QMultiHash>
36 #include <QString>
37 #include <QList>
38 
39 #include "qtcompat_p.h"
40 
41 namespace Tiled {
42 
43 class TILEDSHARED_EXPORT WangId
44 {
45 public:
46     constexpr static unsigned BITS_PER_INDEX = 8;
47     constexpr static quint64 INDEX_MASK = 0xFF;
48     constexpr static quint64 FULL_MASK = Q_UINT64_C(0xFFFFFFFFFFFFFFFF);
49     constexpr static int MAX_COLOR_COUNT = (1 << BITS_PER_INDEX) - 1;
50 
51     enum Index {
52         Top         = 0,
53         TopRight    = 1,
54         Right       = 2,
55         BottomRight = 3,
56         Bottom      = 4,
57         BottomLeft  = 5,
58         Left        = 6,
59         TopLeft     = 7,
60 
61         NumCorners  = 4,
62         NumEdges    = 4,
63         NumIndexes  = 8,
64     };
65 
66     enum Masks : quint64 {
67         MaskTop         = INDEX_MASK << (BITS_PER_INDEX * Top),
68         MaskTopRight    = INDEX_MASK << (BITS_PER_INDEX * TopRight),
69         MaskRight       = INDEX_MASK << (BITS_PER_INDEX * Right),
70         MaskBottomRight = INDEX_MASK << (BITS_PER_INDEX * BottomRight),
71         MaskBottom      = INDEX_MASK << (BITS_PER_INDEX * Bottom),
72         MaskBottomLeft  = INDEX_MASK << (BITS_PER_INDEX * BottomLeft),
73         MaskLeft        = INDEX_MASK << (BITS_PER_INDEX * Left),
74         MaskTopLeft     = INDEX_MASK << (BITS_PER_INDEX * TopLeft),
75 
76         MaskEdges       = MaskTop | MaskRight | MaskBottom | MaskLeft,
77         MaskCorners     = MaskTopRight | MaskBottomRight | MaskBottomLeft | MaskTopLeft,
78     };
79 
mId(id)80     constexpr WangId(quint64 id = 0) : mId(id) {}
81 
quint64()82     constexpr operator quint64() const { return mId; }
setId(quint64 id)83     inline void setId(quint64 id) { mId = id; }
84 
85     int edgeColor(int index) const;
86     int cornerColor(int index) const;
87 
88     int indexColor(int index) const;
89 
90     void setEdgeColor(int index, unsigned value);
91     void setCornerColor(int index, unsigned value);
92     void setGridColor(int x, int y, unsigned value);
93 
94     void setIndexColor(int index, unsigned value);
95 
96     void updateToAdjacent(WangId adjacent, int position);
97 
98     bool hasWildCards() const;
99     bool hasCornerWildCards() const;
100     bool hasEdgeWildCards() const;
101     quint64 mask() const;
102     quint64 mask(int value) const;
103 
104     bool hasCornerWithColor(int value) const;
105     bool hasEdgeWithColor(int value) const;
106 
107     void rotate(int rotations);
108     WangId rotated(int rotations) const;
109     void flipHorizontally();
110     void flipVertically();
111     WangId flippedHorizontally() const;
112     WangId flippedVertically() const;
113 
114     static Index indexByGrid(int x, int y);
115     static Index oppositeIndex(int index);
116     static Index nextIndex(int index);
117     static Index previousIndex(int index);
118     static bool isCorner(int index);
119 
120     static WangId fromUint(unsigned id);
121     unsigned toUint() const;
122 
123     static WangId fromString(QStringRef string, bool *ok = nullptr);
124     QString toString() const;
125 
126 private:
127     quint64 mId;
128 };
129 
oppositeIndex(int index)130 inline WangId::Index WangId::oppositeIndex(int index)
131 {
132     return static_cast<Index>((index + 4) % NumIndexes);
133 }
134 
nextIndex(int index)135 inline WangId::Index WangId::nextIndex(int index)
136 {
137     return static_cast<Index>((index + 1) % NumIndexes);
138 }
139 
previousIndex(int index)140 inline WangId::Index WangId::previousIndex(int index)
141 {
142     return static_cast<Index>((index + NumIndexes - 1) % NumIndexes);
143 }
144 
isCorner(int index)145 inline bool WangId::isCorner(int index)
146 {
147     return index & 1;
148 }
149 
150 TILEDSHARED_EXPORT QDebug operator<<(QDebug debug, WangId wangId);
151 
152 
153 /**
154  * Class for holding a tile and its WangId.
155  */
156 class TILEDSHARED_EXPORT WangTile
157 {
158 public:
WangTile(int tileId,WangId wangId)159     WangTile(int tileId, WangId wangId)
160         : mTileId(tileId)
161         , mWangId(wangId)
162     {}
163 
tileId()164     int tileId() const { return mTileId; }
wangId()165     WangId wangId() const { return mWangId; }
166 
167     bool operator< (const WangTile &other) const
168     { return mTileId < other.mTileId; }
169 
170 private:
171     int mTileId;
172     WangId mWangId;
173 };
174 
175 TILEDSHARED_EXPORT QDebug operator<<(QDebug debug, const WangTile &wangTile);
176 
177 
178 class TILEDSHARED_EXPORT WangColor : public Object
179 {
180 public:
181     WangColor();
182     WangColor(int colorIndex,
183               const QString &name,
184               const QColor &color,
185               int imageId = -1,
186               qreal probability = 1);
187 
colorIndex()188     int colorIndex() const { return mColorIndex; }
name()189     QString name() const { return mName; }
color()190     QColor color() const { return mColor; }
imageId()191     int imageId() const { return mImageId; }
probability()192     qreal probability() const { return mProbability; }
193 
setName(const QString & name)194     void setName(const QString &name) { mName = name; }
setColor(const QColor & color)195     void setColor(const QColor &color) { mColor = color; }
setImageId(int imageId)196     void setImageId(int imageId) { mImageId = imageId; }
setProbability(qreal probability)197     void setProbability(qreal probability) { mProbability = probability; }
198 
wangSet()199     WangSet *wangSet() const { return mWangSet; }
200 
201     int distanceToColor(int targetColor) const;
202 
203 private:
204     friend class WangSet;
205 
setColorIndex(int colorIndex)206     void setColorIndex(int colorIndex) { mColorIndex = colorIndex; }
207 
208     WangSet *mWangSet = nullptr;
209     int mColorIndex;
210     QString mName;
211     QColor mColor;
212     int mImageId;
213     qreal mProbability;
214 
215     QVector<int> mDistanceToColor;
216 };
217 
218 /**
219  * Returns the transition penalty(/distance) from this color to another.
220  */
distanceToColor(int targetColor)221 inline int WangColor::distanceToColor(int targetColor) const
222 {
223     return mDistanceToColor.at(targetColor);
224 }
225 
226 /**
227  * Represents a Wang set.
228  */
229 class TILEDSHARED_EXPORT WangSet : public Object
230 {
231 public:
232     enum Type {
233         Corner,
234         Edge,
235         Mixed
236     };
237 
238     WangSet(Tileset *tileset,
239             const QString &name,
240             Type type,
241             int imageTileId = -1);
242 
243     Tileset *tileset() const;
244     void setTileset(Tileset *tileset);
245 
246     QString name() const;
247     void setName(const QString &name);
248 
249     Type type() const;
250     void setType(Type type);
251 
252     int imageTileId() const;
253     void setImageTileId(int imageTileId);
254     Tile *imageTile() const;
255 
256     int colorCount() const;
257     void setColorCount(int n);
258 
259     void insertWangColor(const QSharedPointer<WangColor> &wangColor);
260     void addWangColor(const QSharedPointer<WangColor> &wangColor);
261     QSharedPointer<WangColor> takeWangColorAt(int color);
262 
263     const QSharedPointer<WangColor> &colorAt(int index) const;
colors()264     const QVector<QSharedPointer<WangColor>> &colors() const { return mColors; }
265 
266     void setWangId(int tileId, WangId wangId);
267 
wangIdByTileId()268     const QHash<int, WangId> &wangIdByTileId() const { return mTileIdToWangId; }
269 
270     struct WangIdAndCell
271     {
272         WangId wangId;
273         Cell cell;
274     };
275 
276     const QVector<WangIdAndCell> &wangIdsAndCells() const;
277 
278     QList<WangTile> sortedWangTiles() const;
279 
280     WangId wangIdFromSurrounding(const WangId surroundingWangIds[]) const;
281     WangId wangIdFromSurrounding(const Cell surroundingCells[]) const;
282 
283     WangId wangIdOfTile(const Tile *tile) const;
284     WangId wangIdOfCell(const Cell &cell) const;
285 
286     qreal wangIdProbability(WangId wangId) const;
287 
288     bool wangIdIsValid(WangId wangId) const;
289 
290     static bool wangIdIsValid(WangId wangId, int colorCount);
291 
292     bool wangIdIsUsed(WangId wangId, WangId mask = WangId::FULL_MASK) const;
293 
294     int transitionPenalty(int colorA, int colorB) const;
295     int maximumColorDistance() const;
296 
297     bool isEmpty() const;
298     bool isComplete() const;
299     quint64 completeSetSize() const;
300 
301     WangId templateWangIdAt(unsigned n) const;
302 
303     WangSet *clone(Tileset *tileset) const;
304 
305 private:
306     void removeTileId(int tileId);
307 
308     bool cellsDirty() const;
309     void recalculateCells();
310     void recalculateColorDistances();
311 
312     Tileset *mTileset;
313     QString mName;
314     Type mType;
315     int mImageTileId;
316 
317     // How many unique, full WangIds are active in this set.
318     // Where full means the id has no wildcards
319     quint64 mUniqueFullWangIdCount = 0;
320 
321     QVector<QSharedPointer<WangColor>> mColors;
322     QHash<int, WangId> mTileIdToWangId;
323 
324     QVector<WangIdAndCell> mWangIdAndCells;
325 
326     int mMaximumColorDistance = 0;
327     bool mColorDistancesDirty = true;
328     bool mCellsDirty = true;
329     Tileset::TransformationFlags mLastSeenTranslationFlags;
330 };
331 
332 
tileset()333 inline Tileset *WangSet::tileset() const
334 {
335     return mTileset;
336 }
337 
setTileset(Tileset * tileset)338 inline void WangSet::setTileset(Tileset *tileset)
339 {
340     mTileset = tileset;
341 }
342 
name()343 inline QString WangSet::name() const
344 {
345     return mName;
346 }
347 
setName(const QString & name)348 inline void WangSet::setName(const QString &name)
349 {
350     mName = name;
351 }
352 
type()353 inline WangSet::Type WangSet::type() const
354 {
355     return mType;
356 }
357 
358 /**
359  * Changes the type of this Wang set. Does not modify any WangIds to make sure
360  * they adhere to the type!
361  */
setType(WangSet::Type type)362 inline void WangSet::setType(WangSet::Type type)
363 {
364     mType = type;
365 }
366 
imageTileId()367 inline int WangSet::imageTileId() const
368 {
369     return mImageTileId;
370 }
371 
setImageTileId(int imageTileId)372 inline void WangSet::setImageTileId(int imageTileId)
373 {
374     mImageTileId = imageTileId;
375 }
376 
imageTile()377 inline Tile *WangSet::imageTile() const
378 {
379     return mTileset->findTile(mImageTileId);
380 }
381 
colorCount()382 inline int WangSet::colorCount() const
383 {
384     return mColors.size();
385 }
386 
colorAt(int index)387 inline const QSharedPointer<WangColor> &WangSet::colorAt(int index) const
388 {
389     Q_ASSERT(index > 0 && index <= colorCount());
390 
391     return mColors.at(index - 1);
392 }
393 
isEmpty()394 inline bool WangSet::isEmpty() const
395 {
396     return mTileIdToWangId.isEmpty();
397 }
398 
cellsDirty()399 inline bool WangSet::cellsDirty() const
400 {
401     return mCellsDirty || mLastSeenTranslationFlags != mTileset->transformationFlags();
402 }
403 
404 TILEDSHARED_EXPORT QString wangSetTypeToString(WangSet::Type type);
405 TILEDSHARED_EXPORT WangSet::Type wangSetTypeFromString(const QString &);
406 
407 } // namespace Tiled
408 
409 Q_DECLARE_METATYPE(Tiled::WangSet*)
410 Q_DECLARE_METATYPE(Tiled::WangId)
411