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