Skip to content

Commit

Permalink
Support 60 degree rotation for hexagonal maps (mapeditor#1447)
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolaichuk authored and bjorn committed Mar 7, 2017
1 parent 2ea0b28 commit fae6ebd
Show file tree
Hide file tree
Showing 10 changed files with 191 additions and 11 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Makefile*
moc_*.cpp
ui_*.h
qrc_*
default/

# Build artifacts on Windows
tiled.exe
Expand Down
Binary file added examples/test_hexagonal_tile_60x60x30.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 30 additions & 0 deletions examples/test_hexagonal_tile_60x60x30.tmx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.0" orientation="hexagonal" renderorder="right-down" width="20" height="20" tilewidth="60" tileheight="60" hexsidelength="30" staggeraxis="x" staggerindex="odd" nextobjectid="1">
<tileset firstgid="1" name="test_hexagonal_tile_60x60x30" tilewidth="60" tileheight="60" tilecount="1" columns="1">
<image source="test_hexagonal_tile_60x60x30.png" width="60" height="60"/>
</tileset>
<layer name="Tile Layer 1" width="20" height="20">
<data encoding="csv">
1,536870913,268435457,3221225473,3758096385,3489660929,1,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2147483649,2684354561,2415919105,1073741825,1610612737,1342177281,2147483649,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
</data>
</layer>
</map>
9 changes: 8 additions & 1 deletion src/libtiled/gidmapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ const int FlippedHorizontallyFlag = 0x80000000;
const int FlippedVerticallyFlag = 0x40000000;
const int FlippedAntiDiagonallyFlag = 0x20000000;

const int RotatedHexagonal120Flag = 0x10000000;

/**
* Default constructor. Use \l insert to initialize the gid mapper
* incrementally.
Expand Down Expand Up @@ -74,10 +76,13 @@ Cell GidMapper::gidToCell(unsigned gid, bool &ok) const
result.setFlippedVertically(gid & FlippedVerticallyFlag);
result.setFlippedAntiDiagonally(gid & FlippedAntiDiagonallyFlag);

result.setRotatedHexagonal120(gid & RotatedHexagonal120Flag);

// Clear the flags
gid &= ~(FlippedHorizontallyFlag |
FlippedVerticallyFlag |
FlippedAntiDiagonallyFlag);
FlippedAntiDiagonallyFlag |
RotatedHexagonal120Flag);

if (gid == 0) {
ok = true;
Expand Down Expand Up @@ -130,6 +135,8 @@ unsigned GidMapper::cellToGid(const Cell &cell) const
gid |= FlippedVerticallyFlag;
if (cell.flippedAntiDiagonally())
gid |= FlippedAntiDiagonallyFlag;
if (cell.rotatedHexagonal120())
gid |= RotatedHexagonal120Flag;

return gid;
}
Expand Down
2 changes: 1 addition & 1 deletion src/libtiled/hexagonalrenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ void HexagonalRenderer::drawTileLayer(QPainter *painter,
if (inLeftHalf)
startTile.rx()--;

CellRenderer renderer(painter);
CellRenderer renderer(painter, CellRenderer::HexagonalCells);

if (p.staggerX) {
startTile.setX(qMax(-1, startTile.x()));
Expand Down
14 changes: 12 additions & 2 deletions src/libtiled/maprenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,11 @@ static bool hasOpenGLEngine(const QPainter *painter)
type == QPaintEngine::OpenGL2);
}

CellRenderer::CellRenderer(QPainter *painter)
CellRenderer::CellRenderer(QPainter *painter, const CellType cellType)
: mPainter(painter)
, mTile(nullptr)
, mIsOpenGL(hasOpenGLEngine(painter))
, mCellType(cellType)
{
}

Expand Down Expand Up @@ -164,7 +165,16 @@ void CellRenderer::render(const Cell &cell, const QPointF &pos, const QSizeF &si
if (origin == BottomCenter)
fragment.x -= sizeHalf.x();

if (cell.flippedAntiDiagonally()) {
if (mCellType == HexagonalCells) {

if (cell.flippedAntiDiagonally())
fragment.rotation += 60;

if (cell.rotatedHexagonal120())
fragment.rotation += 120;

} else if (cell.flippedAntiDiagonally()) {
Q_ASSERT(mCellType == OrthogonalCells);
fragment.rotation = 90;

flippedHorizontally = cell.flippedVertically();
Expand Down
8 changes: 7 additions & 1 deletion src/libtiled/maprenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,12 @@ class CellRenderer
BottomCenter
};

explicit CellRenderer(QPainter *painter);
enum CellType {
OrthogonalCells,
HexagonalCells
};

explicit CellRenderer(QPainter *painter, CellType cellType = OrthogonalCells);

~CellRenderer() { flush(); }

Expand All @@ -270,6 +275,7 @@ class CellRenderer
const Tile *mTile;
QVector<QPainter::PixmapFragment> mFragments;
const bool mIsOpenGL;
const CellType mCellType;
};

} // namespace Tiled
Expand Down
96 changes: 96 additions & 0 deletions src/libtiled/tilelayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,42 @@ void TileLayer::flip(FlipDirection direction)
mGrid = newGrid;
}

void TileLayer::flipHexagonal(FlipDirection direction)
{
QVector<Cell> newGrid(mWidth * mHeight);

Q_ASSERT(direction == FlipHorizontally || direction == FlipVertically);

// for more info see impl "void TileLayer::rotateHexagonal(RotateDirection direction)"
static const char flipMaskH[16] = { 8, 6, 5, 4, 12, 2, 1, 0, 0, 14, 13, 12, 4, 10, 9, 8 }; // [0,15]<=>[8,7]; 2<=>5; 1<=>6; [12,3]<=>[4,11]; 14<=>9; 13<=>10;
static const char flipMaskV[16] = { 4, 10, 9, 8, 0, 14, 13, 12, 12, 2, 1, 0, 8, 6, 5, 4 }; // [0,15]<=>[4,11]; 2<=>9; 1<=>10; [12,3]<=>[8,7]; 14<=>5; 13<=>6;

const char (&flipMask)[16] = (direction == FlipHorizontally ? flipMaskH : flipMaskV);

for (int y = 0; y < mHeight; ++y) {
for (int x = 0; x < mWidth; ++x) {
const Cell &source = (direction == FlipHorizontally ? cellAt(mWidth - x - 1, y) : cellAt(x, mHeight - y - 1));
Cell &dest = newGrid[x + y * mWidth];
dest = source;

unsigned char mask =
(static_cast<unsigned char>(dest.flippedHorizontally()) << 3) |
(static_cast<unsigned char>(dest.flippedVertically()) << 2) |
(static_cast<unsigned char>(dest.flippedAntiDiagonally()) << 1) |
(static_cast<unsigned char>(dest.rotatedHexagonal120()) << 0);

mask = flipMask[mask];

dest.setFlippedHorizontally((mask & 8) != 0);
dest.setFlippedVertically((mask & 4) != 0);
dest.setFlippedAntiDiagonally((mask & 2) != 0);
dest.setRotatedHexagonal120((mask & 1) != 0);
}
}

mGrid = newGrid;
}

void TileLayer::rotate(RotateDirection direction)
{
static const char rotateRightMask[8] = { 5, 4, 1, 0, 7, 6, 3, 2 };
Expand Down Expand Up @@ -276,6 +312,66 @@ void TileLayer::rotate(RotateDirection direction)
mGrid = newGrid;
}

void TileLayer::rotateHexagonal(RotateDirection direction)
{
int newWidth = mHeight;
int newHeight = mWidth;
QVector<Cell> newGrid(newWidth * newHeight);

/* https://github.com/bjorn/tiled/pull/1447
0 or 15 0: None or (Rotated60 | Rotated120 | FlippedVertically | FlippedHorizontally)
2 60: Rotated60
1 120: Rotated120
12 or 3 180: (FlippedHorizontally | FlippedVertically) or (Rotated60 | Rotated120)
14 240: Rotated60 | FlippedHorizontally | FlippedVertically
13 300: Rotated120 | FlippedHorizontally | FlippedVertically
8 or 7 0: FlippedHorizontally or (Rotated60 | Rotated120 | FlippedVertically)
10 60: Rotated60 | FlippedHorizontally
9 120: Rotated120 | FlippedHorizontally
4 or 11 180: (FlippedVertically) or (Rotated60 | Rotated120 | FlippedHorizontally)
6 240: Rotated60 | FlippedVertically
5 300: Rotated120 | FlippedVertically
*/

static const char rotateRightMask[16] = { 2, 12, 1, 14, 6, 8, 5, 10, 10, 4, 9, 0, 14, 0, 13, 2 }; // [0,15]->2->1->[12,3]->14->13; [8,7]->10->9->[4,11]->6->5;
static const char rotateLeftMask[16] = { 13, 2, 0, 1, 9, 6, 4, 5, 5, 10, 8, 9, 1, 14, 12, 13 }; // [0,15]->13->14->[12,3]->1->2; [8,7]->5->6->[4,11]->9->10;

const char (&rotateMask)[16] =
(direction == RotateRight) ? rotateRightMask : rotateLeftMask;

for (int y = 0; y < mHeight; ++y) {
for (int x = 0; x < mWidth; ++x) {
const Cell &source = cellAt(x, y);
Cell dest = source;

unsigned char mask =
(static_cast<unsigned char>(dest.flippedHorizontally()) << 3) |
(static_cast<unsigned char>(dest.flippedVertically()) << 2) |
(static_cast<unsigned char>(dest.flippedAntiDiagonally()) << 1) |
(static_cast<unsigned char>(dest.rotatedHexagonal120()) << 0);

mask = rotateMask[mask];

dest.setFlippedHorizontally((mask & 8) != 0);
dest.setFlippedVertically((mask & 4) != 0);
dest.setFlippedAntiDiagonally((mask & 2) != 0);
dest.setRotatedHexagonal120((mask & 1) != 0);

if (direction == RotateRight)
newGrid[x * newWidth + (mHeight - y - 1)] = dest;
else
newGrid[(mWidth - x - 1) * newWidth + y] = dest;
}
}

mWidth = newWidth;
mHeight = newHeight;
mGrid = newGrid;
}


QSet<SharedTileset> TileLayer::usedTilesets() const
{
Expand Down
32 changes: 28 additions & 4 deletions src/libtiled/tilelayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,17 @@ class Cell
_tileId(-1),
_flippedHorizontally(false),
_flippedVertically(false),
_flippedAntiDiagonally(false)
_flippedAntiDiagonally(false),
_rotatedHexagonal120(false)
{}

explicit Cell(Tile *tile) :
_tileset(tile ? tile->tileset() : nullptr),
_tileId(tile ? tile->id() : -1),
_flippedHorizontally(false),
_flippedVertically(false),
_flippedAntiDiagonally(false)
_flippedAntiDiagonally(false),
_rotatedHexagonal120(false)
{}

bool isEmpty() const { return _tileset == nullptr; }
Expand All @@ -77,7 +79,8 @@ class Cell
&& _tileId == other._tileId
&& _flippedHorizontally == other._flippedHorizontally
&& _flippedVertically == other._flippedVertically
&& _flippedAntiDiagonally == other._flippedAntiDiagonally;
&& _flippedAntiDiagonally == other._flippedAntiDiagonally
&& _rotatedHexagonal120 == other._rotatedHexagonal120;
}

bool operator != (const Cell &other) const
Expand All @@ -86,7 +89,8 @@ class Cell
|| _tileId != other._tileId
|| _flippedHorizontally != other._flippedHorizontally
|| _flippedVertically != other._flippedVertically
|| _flippedAntiDiagonally != other._flippedAntiDiagonally;
|| _flippedAntiDiagonally != other._flippedAntiDiagonally
|| _rotatedHexagonal120 != other._rotatedHexagonal120;
}

Tileset *tileset() const { return _tileset; }
Expand All @@ -96,10 +100,14 @@ class Cell
bool flippedVertically() const { return _flippedVertically; }
bool flippedAntiDiagonally() const { return _flippedAntiDiagonally; }

bool rotatedHexagonal120() const { return _rotatedHexagonal120; }

void setFlippedHorizontally(bool f) { _flippedHorizontally = f; }
void setFlippedVertically(bool f) { _flippedVertically = f; }
void setFlippedAntiDiagonally(bool f) { _flippedAntiDiagonally = f; }

void setRotatedHexagonal120(bool f) { _rotatedHexagonal120 = f; }

Tile *tile() const;
void setTile(Tile *tile);
void setTile(Tileset *tileset, int tileId);
Expand All @@ -111,6 +119,8 @@ class Cell
bool _flippedHorizontally;
bool _flippedVertically;
bool _flippedAntiDiagonally;

bool _rotatedHexagonal120;
};

inline Tile *Cell::tile() const
Expand Down Expand Up @@ -237,13 +247,27 @@ class TILEDSHARED_EXPORT TileLayer : public Layer
*/
void flip(FlipDirection direction);

/**
* Hexagonal flip this tile layer in the given \a direction. Direction must be
* horizontal or vertical. This doesn't change the dimensions of the
* tile layer.
*/
void flipHexagonal(FlipDirection direction);

/**
* Rotate this tile layer by 90 degrees left or right. The tile positions
* are rotated within the layer, and the tiles themselves are rotated. The
* dimensions of the tile layer are swapped.
*/
void rotate(RotateDirection direction);

/**
* Hexagonal rotate this tile layer by 60 degrees left or right. The tile positions
* are rotated within the layer, and the tiles themselves are rotated. The
* dimensions of the tile layer are swapped.
*/
void rotateHexagonal(RotateDirection direction);

/**
* Computes and returns the set of tilesets used by this tile layer.
*/
Expand Down
10 changes: 8 additions & 2 deletions src/tiled/tilestamp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,10 @@ TileStamp TileStamp::flipped(FlipDirection direction) const

for (const TileStampVariation &variation : flipped.variations()) {
TileLayer *layer = variation.tileLayer();
layer->flip(direction);
if (variation.map->orientation() == Map::Hexagonal)
layer->flipHexagonal(direction);
else
layer->flip(direction);
}

return flipped;
Expand All @@ -249,7 +252,10 @@ TileStamp TileStamp::rotated(RotateDirection direction) const

for (const TileStampVariation &variation : rotated.variations()) {
TileLayer *layer = variation.tileLayer();
layer->rotate(direction);
if (variation.map->orientation() == Map::Hexagonal)
layer->rotateHexagonal(direction);
else
layer->rotate(direction);

variation.map->setWidth(layer->width());
variation.map->setHeight(layer->height());
Expand Down

0 comments on commit fae6ebd

Please sign in to comment.