-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Wang Tiles #1582
Wang Tiles #1582
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was a bit puzzled by the implementation of WangSet::getAllTiles
. Essentially, it seems to be searching for tiles by creating all possible variations of a given WangId
, when taking each 0 as a wildcard.
I would suggest a different approach, which will probably be more efficient even though it will not be using a map lookup:
- Compute a mask instead of the list of wildcards. This is an unsigned value that has 0's where the values of the WangId do not matter and 1's where the values do matter (or
0xF
, since all 4 bits need to be 1). - Loop over
mTileIdToWangId
and compare(tileWangId & mask) == wangId
to see if thetileId
is a match.
See other comments inline.
In general, for your project the same goes as for thabetx, in that it will probably make sense to look for several "checkpoints" at which we will merge into a work-in-progress branch, to avoid building up a huge single PR that will become difficult to review.
The first checkpoint should probably be to complete the data structures, integrating it with the Tileset
class (probably as a list of WangSet
instances) and implementing saving/loading of this information. Afterwards, the initial testing data to use while implementing the functionality of tiling an area can then be manually set up.
src/libtiled/wangset.h
Outdated
* @param edges requesting edge color (corners if false) | ||
* @return | ||
*/ | ||
int getColor(int index, bool edges) const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method should really be split up in edgeColor(int index)
and cornerColor(int index)
. It will be easier to read and perform slightly better.
src/libtiled/wangset.h
Outdated
{ | ||
public: | ||
WangId(unsigned id){ mId = id; } | ||
WangId() { mId = 0; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Coding style: please use the following notation to initialize members: WangId() : mId(0) {}
(similarly for the constructor taking an id).
src/libtiled/wangset.h
Outdated
WangId() { mId = 0; } | ||
|
||
inline unsigned id() const { return mId; } | ||
inline void setId(unsigned id) { mId = id; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could consider doing this instead:
operator unsigned() const { return mId; }
This way, we allow implicit conversion between WangId
and unsigned
in both directions (you already allow implicit conversion from unsigned
to WangId
by not marking the constructor as explicit
).
src/libtiled/wangset.h
Outdated
return color; | ||
} | ||
|
||
void WangId::rotate(int rotations) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing inline
(but maybe should move to cpp file anyway).
src/libtiled/wangset.h
Outdated
* @param wangId | ||
* @return | ||
*/ | ||
QList<Tile*> getAllTiles(WangId wangId) const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably should be called findMatchingTiles
.
src/libtiled/wangset.cpp
Outdated
|
||
WangId WangSet::getWangIdOfTile(Tile *tile) const | ||
{ | ||
if (tile->tileset() == mTileSet && mTileIdToWangId.contains(tile->id())) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These checks are not necessary, you can just return mTileIdToWangId.value(tile->id())
, since when mTileIdToWangId
does not contain the tile it will anyway return a default-constructed WangId
.
src/libtiled/wangset.h
Outdated
* @brief wangIdOfTile returns the wangId of a given tileId | ||
* @param tileId | ||
*/ | ||
WangId getWangIdOfTile(Tile *tile) const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Naming style: should be just wangIdOfTile
.
src/libtiled/wangset.cpp
Outdated
QList<Tile*> list; | ||
|
||
//Stores the space of a wild card, followed by how many colors that space can have. | ||
QVector<QPoint> wildCards; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use a custom struct instead of QPoint
for readability sake.
src/libtiled/wangset.cpp
Outdated
|
||
for (int i = 0; i < wildCards.size(); ++i) { | ||
stack.push(wildCards[i]); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could use stack.append(wildCards)
instead.
src/libtiled/wangset.h
Outdated
QString mName; | ||
int mImageTileId; | ||
QMultiMap<WangId, Tile*> mWangIdToTile; | ||
QMap<int, WangId> mTileIdToWangId; //This could be stored in the tile object. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider using a QHash
instead, which has O(1) lookup rather than the O(log(n)) of QMap
. The difference is equivalent with std::map
vs. std::unordered_map
.
In the most recent push, I've fixed the changes requested in your first review, as well as added wangsets to Tilesets in the way you suggested (as a list). For the most part I modeled after the Terrains list, with the major exception of not tracking the id within the wangsets themselves. I don't think it will be necessary, though I've yet to look to far into how the interface will be built (which may need more certain ids?). For now I've kept my method of finding wang tile variations as it was, as I still think despite the overhead of setting it up, in most cases it will run faster than searching through all the tiles (as most sets should be full), though this could require further investigation by me. On that note further testing is still required, though Ill get file IO, and wang assignment working before I test too rigorously. (I did some small tests and all worked as expected). Sorry for the delay in making progress, but it should be more steady from here on out! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, I've provided some more comments.
I'll also change the target branch to the new wip/wangtiles
branch I've set up in my repository, so we can incrementally work on the project there until it is ready to be merged to master
.
src/libtiled/wangset.cpp
Outdated
typedef struct WangWildCard | ||
{ | ||
int index, colorCount; | ||
} WangWildCard; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This way of defining a struct was necessary in C times, to avoid needing to repeat the struct
keyword whenever you would use WangWildCard
. In C++, we can just write:
struct WangWildCard
{
int index;
int colorCount;
};
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What a wonderful time we live in! Thanks for informing me
src/libtiled/wangset.cpp
Outdated
|
||
for (int i = 0; i < max; ++i) { | ||
idVariation |= stack[i].colorCount << stack[i].index; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Coding style: please leave out the braces for one-line bodies (also below).
src/libtiled/wangset.h
Outdated
inline void setId(unsigned id) { mId = id; } | ||
|
||
/** | ||
* @brief getColor returns the color of a desired edge/corner of a given wang id |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Docs still need updating.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, for all the docs I just use qt's auto formatting. Though its a bit burdensome. Ill go through and update all those.
src/libtiled/wangset.h
Outdated
int cornerColor(int index) const; | ||
|
||
/** | ||
* @brief rotateWangId rotates the wang id 90 * rotations degrees ccw |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Docs seem to mention old function name? I'd suggest to leave out the @brief
and the function name.
src/libtiled/wangset.h
Outdated
/** | ||
* @brief rotateWangId rotates the wang id 90 * rotations degrees ccw | ||
* @param rotations 1-3 | ||
* @return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Returns nothing, so just leave out the @return
.
src/libtiled/wangset.h
Outdated
* @param rotations 1-3 | ||
* @return | ||
*/ | ||
void rotate(int rotations);\ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stray \
at end of line.
src/libtiled/wangset.h
Outdated
WangSet *clone(Tileset *tileset) const; | ||
|
||
private: | ||
Tileset *mTileSet; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Naming: "tileset" is always a single word in Tiled source code, so this should should be mTileset
.
src/libtiled/wangset.h
Outdated
|
||
/** | ||
* @brief wangIdOfTile returns the wangId of a given tileId | ||
* @param tileId |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All docs seem outdated. Also, if you don't provide any comment on a parameter, there is no need to mention it with @param
.
src/libtiled/wangset.h
Outdated
int mEdgeColors; | ||
int mCornerColors; | ||
QMultiHash<WangId, Tile*> mWangIdToTile; | ||
QHash<int, WangId> mTileIdToWangId; //This could be stored in the tile object. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whether it could be stored in the tile object depends on whether we allow a tile to be part of multiple wang sets. Would limiting a tile to a single wang set be useful in some way, beyond the small possible performance improvement?
src/libtiled/wangset.cpp
Outdated
mEdgeColors(edgeColors), | ||
mCornerColors(cornerColors)/*, | ||
mWangIdToTile(QMultiHash<WangId, Tile*>()), | ||
mTileIdToWangId(QHash<int, WangId>())*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just remove these lines.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Knew I forgot something...
src/libtiled/wangset.cpp
Outdated
for (int i = 0; i < wildCards.size(); ++i) { | ||
stack.push(wildCards[i]); | ||
} | ||
stack.append(wildCards); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I checked the documentation, check the following link.
http://doc.qt.io/qt-5/qvector.html#append-2
The method to append another qvector was introduced in Qt 5.5 so there is a difference in the Qt version that you are using and the one that the Travis CI uses, I would suggest you to get Qt 5.4.2 which is the officially minimal supported to build Tiled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You might be able to use += or << operators instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess we could also just raise the minimum Qt version to 5.5. I wanted to quickly look up if those operators would work as an alternative, but http://doc.qt.io/archives/index.html does not even host Qt 5.4 documentation anymore.
In general, it does not matter which version is used for development but I'd recommend simply using the latest version, since that is likely the version the next Tiled release is going to be made against. We can check the minimum supported version using Travis CI.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So for the time being, should I change that part of the code to comply with the older version? Or just leave it as is?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bjorn has more a word on this than I, he is the product owner, but I would say that if += work in older versions and in newers aswell, then just use the operator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, looking at the Qt 5.4 docs in Qt Creator, the operator+= does work with another QVector, so please just use that instead of append, so we can have it in a single line without raising the Qt dependency.
I've implemented file saving and loading, though I've not yet tested rigorously... I wanted to first make sure I'm doing things correctly, and that the format I've used is good, and is in line with current formatting. |
Please change the naming style to keep the tags all lowercase. About the format in general, I found it somewhat unexpected that there is a |
Yes, that was my first thought for how to do it, but figured it would be easier for it to be read right after the tile is created. Also I wanted to avoid reprinting the tile Id and such for each wangset, though it probably ends up being about the same number of lines. I mostly liked that it maintains all the info for a given tile in the single block. Did you think one way was better than the other? |
I think keeping all the information together in the |
Not yet in a "working" state, but the foundation for displaying, creating, and editing wangSets. |
The latest commit is ready for review! It completes the support for creating and editing the basic properties of a wangset from the UI. Features still needed for this are assigning the image for a wang set. Next to come is of course actually assigning the wangIds to the tiles. I see this as an iterative thing; Ill get a very basic implementation working first, then build it up into a more elegant UI. This will be done over the next week or so. Before I do that Ill get a basic implementation of the wang bucket fill working to test out, and show off those methods. Now that this is done, I feel progress will continue more quickly! |
…les) stays grouped together
"addremocewangset"? Did you mean "remove"? |
A working test of the wang fill bucket is in this pr! Bdtrotte#1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried out your Wang filling bucket tool and I think it looks very promising. You have made very good progress this week!
Unfortunately, it will take until Monday before I can do a detailed review of your patches. Please take a chance tomorrow to clean up the work so far and maybe fill in some more bits of functionality (also I think you should make the test tileset part of this PR). The main goal should be to have something we can merge into the work-in-progress branch early next week. Then we can plan the next steps and you can open a new pull request to wip/wangtiles
.
src/libtiled/mapreader.cpp
Outdated
int tileId = tileAtts.value(QLatin1String("tileid")).toInt(); | ||
int wangId = tileAtts.value(QLatin1String("wangid")).toInt(); | ||
|
||
wangSet->addTile(tileset.tileAt(tileId), wangId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be findOrCreateTile
, since you can't assume the tile is already allocated. In my case this was crashing with your test tileset, since the tiles are usually allocated later, when the image is loaded.
…gId based on surrounding tiles.
Pending further review of everything, I believe the work so far is mergable into the work in progress branch. Currently all the base data, and core UI is set up and should be ready to be used in further expansion of the feature. My next step is going to be to focus on making the wangsets available in the map editor, and a tool bar button for activating wangfilling methods. After which I will focus back on the assignment of tiles to a wangId. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The file qtc_Desktop__bdef64c0-debug.bg
should not be committed, please remove it again.
In general, it's impressive how much code you've already written, though of course it was largely duplicating the closely related code for terrains. With all these similarities, I really hope we can eventually base the terrain tool on this one. It should be possible, given that the terrain tool is just a special case, being a WangSet with corner count equal to the number of terrain types.
I also quite liked to see the implementation of WangSet::wangIdFromSurrounding
. I think it could be a little more readable if we added WangId::setCornerColor
and setEdgeColor
. And I think eventually we'll want to have a version that works without copying, for performance reasons. But this is fine for now.
In any case, I think I left a rather large amount of small comments. I hope you can make them tomorrow, so that we can merge this and continue with a new pull request.
Thanks for the work so far!
src/libtiled/mapreader.cpp
Outdated
@@ -564,6 +568,52 @@ void MapReaderPrivate::readTilesetTerrainTypes(Tileset &tileset) | |||
} | |||
} | |||
|
|||
void MapReaderPrivate::readTilesetWangSet(Tileset &tileset) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Naming: should be readTilesetWangSets
, since it will read multiple sets.
src/libtiled/mapreader.cpp
Outdated
|
||
WangSet *wangSet = new WangSet(&tileset, edges, corners, name, tile); | ||
|
||
tileset.insertWangSet(wangSet); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Naming: this should be called addWangSet
, since "insertion" generally is done at a certain index.
src/libtiled/mapreader.cpp
Outdated
tileset.insertWangSet(wangSet); | ||
|
||
while (xml.readNextStartElement()) { | ||
if (xml.name() == QLatin1String("properties")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Coding style: should have {
and }
because the else if
part has them too (it's a rule from the Qt coding style).
src/libtiled/mapreader.cpp
Outdated
unsigned wangId = tileAtts.value(QLatin1String("wangid")).toUInt(); | ||
bool fH = tileAtts.value(QLatin1String("flippedhorizontally")).toInt(); | ||
bool fV = tileAtts.value(QLatin1String("flippedvertically")).toInt(); | ||
bool fA = tileAtts.value(QLatin1String("flippedantidiagonally")).toInt(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the interest of avoiding really long names, I wonder if these should be "hflip", "vflip" and "dflip"?
src/libtiled/mapreader.cpp
Outdated
wangSet->addWangTile(wangTile); | ||
|
||
xml.skipCurrentElement(); | ||
} else |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Coding style: Should have {
and }
because because the previous part of the condition has them.
src/tiled/tileseteditor.cpp
Outdated
void TilesetEditor::addWangSet() | ||
{ | ||
Tileset *tileset = currentTileset(); | ||
if(!tileset) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Coding style: space after if
.
src/tiled/tileseteditor.cpp
Outdated
if(!tileset) | ||
return; | ||
|
||
//2 and 0 are default values for number of edges and corners TODO define this some where better? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fine for now, though we should probably add a "NewWangSetDialog" class so that the user can set up his wang set upon creation (where there would be space to explain the corner/edges count a bit and show the presets).
|
||
void TilesetEditor::removeWangSet() | ||
{ | ||
WangSet *wangSet = mWangDock->currentWangSet(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please do check that there is a current wang set, even if the button to trigger this action should be disabled at this point.
src/tiled/tilesetwangsetmodel.h
Outdated
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | ||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please make sure your files use the right copyright header. This is the header used for libtiled, but this file is part of Tiled itself and should have the GPL header.
src/tiled/tilesetwangsetmodel.h
Outdated
WangSetRole = Qt::UserRole | ||
}; | ||
|
||
TilesetWangSetModel(TilesetDocument *mapDocument, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This constructor should be marked explicit
, to avoid potential implicit conversion from TilesetDocument *
to TilesetWangSetModel
(however unlikely to run into).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A view more adjustments needed.
src/libtiled/wangset.cpp
Outdated
|
||
Cell WangTile::makeCell() const | ||
{ | ||
if(!mTile) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please make coding style adjustments in all needed places, not just where I point them out. This is still one of 10 places where there is no space after the if
.
src/tiled/tilesetwangsetmodel.h
Outdated
|
||
WangSet *wangSetAt(const QModelIndex &index) const; | ||
|
||
void insertWangSet(WangSet *wangSet, int index); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All the other functions take index
as first parameter, please be consistent and do the same here.
src/tiled/mapdocument.h
Outdated
@@ -315,6 +328,8 @@ private slots: | |||
MapRenderer *mRenderer; | |||
Layer* mCurrentLayer; | |||
MapObjectModel *mMapObjectModel; | |||
TerrainModel *mTerrainModel; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You've removed the getter, but this member should not be added either, nor should the forward declaration and the tilesetTerrain...
set of signals.
Since your WangSetModel
now follows the same logic as the TerrainModel
, it should also not be part of the MapDocument
, nor should the tilesetWangSet...
set of signals.
src/tiled/changewangsetdata.cpp
Outdated
|
||
ChangeWangSetCorners::ChangeWangSetCorners(TilesetDocument *tilesetDocument, | ||
int index, | ||
int newValue) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indentation off by one.
Thanks for your work so far! Please open a new pull request today or tomorrow for the next steps. You should decide on a work package that you estimate will take about one week, so that we can aim for another merge on Monday or Tuesday next week. Please don't add merge commits to the pull request. It is confusing on GitHub and makes the history less clean. I had to forward the I think the general set up for the wang tiles is really promising, so I'm really looking forward to see the rest of the UI and the tools materialize! |
An iterative pr for code review on the GSOC wang tiles project.