1 /*
2  * changetilewangid.cpp
3  * Copyright 2017, Benjamin Trotter <bdtrotte@ucsc.edu>
4  *
5  * This file is part of Tiled.
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the Free
9  * Software Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15  * more details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "changetilewangid.h"
22 
23 #include "tilesetdocument.h"
24 #include "tile.h"
25 
26 #include <QCoreApplication>
27 
28 #include "qtcompat_p.h"
29 
30 using namespace Tiled;
31 
ChangeTileWangId()32 ChangeTileWangId::ChangeTileWangId()
33     : mTilesetDocument(nullptr)
34     , mWangSet(nullptr)
35     , mMergeable(false)
36 {
37     setText(QCoreApplication::translate("Undo Commands", "Change Tile Terrain"));
38 }
39 
ChangeTileWangId(TilesetDocument * tilesetDocument,WangSet * wangSet,Tile * tile,WangId wangId)40 ChangeTileWangId::ChangeTileWangId(TilesetDocument *tilesetDocument,
41                                    WangSet *wangSet,
42                                    Tile *tile,
43                                    WangId wangId)
44     : mTilesetDocument(tilesetDocument)
45     , mWangSet(wangSet)
46     , mMergeable(true)
47 {
48     Q_ASSERT(mWangSet);
49     setText(QCoreApplication::translate("Undo Commands", "Change Tile Terrain"));
50     mChanges.append(WangIdChange(mWangSet->wangIdOfTile(tile), wangId, tile->id()));
51 }
52 
ChangeTileWangId(TilesetDocument * tilesetDocument,WangSet * wangSet,const QVector<WangIdChange> & changes,QUndoCommand * parent)53 ChangeTileWangId::ChangeTileWangId(TilesetDocument *tilesetDocument,
54                                    WangSet *wangSet,
55                                    const QVector<WangIdChange> &changes,
56                                    QUndoCommand *parent)
57     : QUndoCommand(parent)
58     , mTilesetDocument(tilesetDocument)
59     , mWangSet(wangSet)
60     , mChanges(changes)
61     , mMergeable(true)
62 {
63     setText(QCoreApplication::translate("Undo Commands", "Change Tile Terrain"));
64 }
65 
undo()66 void ChangeTileWangId::undo()
67 {
68     if (mChanges.isEmpty())
69         return;
70 
71     QList<Tile *> changedTiles;
72 
73     QVectorIterator<WangIdChange> changes(mChanges);
74     changes.toBack();
75 
76     while (changes.hasPrevious()) {
77         const WangIdChange &wangIdChange = changes.previous();
78 
79         if (Tile *tile = findTile(wangIdChange.tileId))
80             changedTiles.append(tile);
81         mWangSet->setWangId(wangIdChange.tileId, wangIdChange.from);
82     }
83 
84     emit mTilesetDocument->tileWangSetChanged(changedTiles);
85 }
86 
redo()87 void ChangeTileWangId::redo()
88 {
89     if (mChanges.isEmpty())
90         return;
91 
92     QList<Tile *> changedTiles;
93 
94     for (const WangIdChange &wangIdChange : qAsConst(mChanges)) {
95         if (Tile *tile = findTile(wangIdChange.tileId))
96             changedTiles.append(tile);
97         mWangSet->setWangId(wangIdChange.tileId, wangIdChange.to);
98     }
99 
100     emit mTilesetDocument->tileWangSetChanged(changedTiles);
101 }
102 
mergeWith(const QUndoCommand * other)103 bool ChangeTileWangId::mergeWith(const QUndoCommand *other)
104 {
105     if (!mMergeable)
106         return false;
107 
108     const ChangeTileWangId *o = static_cast<const ChangeTileWangId*>(other);
109     if (o->mTilesetDocument && !(mTilesetDocument == o->mTilesetDocument &&
110                                  mWangSet == o->mWangSet))
111         return false;
112 
113     // suboptimal, could use a map to remove any unnessesary changes if the
114     // same tile has multiple changes.
115     mChanges += o->mChanges;
116 
117     mMergeable = o->mMergeable;
118 
119     return true;
120 }
121 
changesOnSetColorCount(const WangSet * wangSet,int colorCount)122 QVector<ChangeTileWangId::WangIdChange> ChangeTileWangId::changesOnSetColorCount(
123         const WangSet *wangSet, int colorCount)
124 {
125     QVector<WangIdChange> changes;
126 
127     QHashIterator<int, WangId> it(wangSet->wangIdByTileId());
128     while (it.hasNext()) {
129         it.next();
130         WangId newWangId = it.value();
131 
132         for (int i = 0; i < WangId::NumIndexes; ++i)
133             if (newWangId.indexColor(i) > colorCount)
134                 newWangId.setIndexColor(i, 0);
135 
136         if (it.value() != newWangId)
137             changes.append(WangIdChange(it.value(), newWangId, it.key()));
138     }
139 
140     return changes;
141 }
142 
changesOnRemoveColor(const WangSet * wangSet,int removedColor)143 QVector<ChangeTileWangId::WangIdChange> ChangeTileWangId::changesOnRemoveColor(
144         const WangSet *wangSet, int removedColor)
145 {
146     QVector<WangIdChange> changes;
147 
148     QHashIterator<int, WangId> it(wangSet->wangIdByTileId());
149     while (it.hasNext()) {
150         it.next();
151         WangId newWangId = it.value();
152 
153         for (int i = 0; i < WangId::NumIndexes; ++i) {
154             const int color = newWangId.indexColor(i);
155             if (color == removedColor)
156                 newWangId.setIndexColor(i, 0);
157             else if (color > removedColor)
158                 newWangId.setIndexColor(i, color - 1);
159         }
160 
161         if (it.value() != newWangId)
162             changes.append(WangIdChange(it.value(), newWangId, it.key()));
163     }
164 
165     return changes;
166 }
167 
applyChanges(WangSet * wangSet,const QVector<WangIdChange> & changes)168 void ChangeTileWangId::applyChanges(WangSet *wangSet, const QVector<WangIdChange> &changes)
169 {
170     for (const WangIdChange &change : changes)
171         wangSet->setWangId(change.tileId, change.to);
172 }
173 
findTile(int tileId) const174 Tile *ChangeTileWangId::findTile(int tileId) const
175 {
176     return mTilesetDocument->tileset()->findTile(tileId);
177 }
178