Skip to content

Commit

Permalink
Implemented rendering of major grid lines for staggered / hexagonal maps
Browse files Browse the repository at this point in the history
  • Loading branch information
bjorn committed Feb 28, 2023
1 parent 73dd24b commit c05da7b
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 59 deletions.
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* Added file system actions also for tileset image based tilesets (#3448)
* Added custom class option to disable drawing fill for objects (with dogboydog, #3312)
* Added option to choose a custom interface font (#3589)
* Implemented rendering of major grid lines for staggered / hexagonal maps (#3583)
* Fixed new layer names to be always unique (by Logan Higinbotham, #3452)
* Fixed broken tile images after importing/exporting a tileset
* AutoMapping: Match in order by default, when input regions are defined (#3559)
Expand Down
160 changes: 101 additions & 59 deletions src/libtiled/hexagonalrenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,8 @@
#include "hexagonalrenderer.h"

#include "map.h"
#include "mapobject.h"
#include "tile.h"
#include "tilelayer.h"
#include "tileset.h"

#include <QVarLengthArray>
#include <QVector2D>
#include <QtCore/qmath.h>

Expand Down Expand Up @@ -103,6 +100,7 @@ void HexagonalRenderer::drawGrid(QPainter *painter, const QRectF &exposed,
return;

const RenderParams p(map());
const bool infinite = map()->infinite();

// Determine the tile and pixel coordinates to start at
QPoint startTile = screenToTileCoords(rect.topLeft()).toPoint();
Expand All @@ -121,107 +119,151 @@ void HexagonalRenderer::drawGrid(QPainter *painter, const QRectF &exposed,
if (inLeftHalf)
startTile.rx()--;

if (!map()->infinite()) {
if (!infinite) {
startTile.setX(qMax(0, startTile.x()));
startTile.setY(qMax(0, startTile.y()));
}

startPos = tileToScreenCoords(startTile).toPoint();

const QPoint oct[8] = {
const QPoint oct[5] = {
QPoint(0, p.tileHeight - p.sideOffsetY),
QPoint(0, p.sideOffsetY),
QPoint(p.sideOffsetX, 0),
QPoint(p.tileWidth - p.sideOffsetX, 0),
QPoint(p.tileWidth, p.sideOffsetY),
QPoint(p.tileWidth, p.tileHeight - p.sideOffsetY),
QPoint(p.tileWidth - p.sideOffsetX, p.tileHeight),
QPoint(p.sideOffsetX, p.tileHeight)
};

QVector<QLine> lines;
lines.reserve(8);
const QLine left(oct[0], oct[1]);
const QLine topLeft(oct[1], oct[2]);
const QLine top(oct[2], oct[3]);
const QLine topRight(oct[3], oct[4]);

QPen _gridPen, gridPen; // always use major grid pen for hex maps
setupGridPens(painter->device(), gridColor, _gridPen, gridPen, p.tileWidth, gridMajor);
QVarLengthArray<QLine, 8> minorLines;
QVarLengthArray<QLine, 8> majorLines;

painter->setPen(gridPen);
QPen gridPen, majorGridPen;
setupGridPens(painter->device(), gridColor, gridPen, majorGridPen, p.tileWidth, gridMajor);

if (p.staggerX) {
// Odd row shifting is applied in the rendering loop, so un-apply it here
if (p.doStaggerX(startTile.x()))
startPos.ry() -= p.rowHeight;

for (; startPos.x() <= rect.right() && (startTile.x() < map()->width() || map()->infinite()); startTile.rx()++) {
startTile.rx() -= 1;
startPos.rx() -= p.columnWidth;

for (; startPos.x() <= rect.right() && (startTile.x() <= map()->width() || infinite); startTile.rx()++) {
const bool isStaggered = p.doStaggerX(startTile.x());
const bool firstColumn = !infinite && startTile.x() == -1;
const bool lastColumn = !infinite && startTile.x() == map()->width();
const bool xIsMajor = gridMajor.width() != 0 && startTile.x() % gridMajor.width() == 0;
const bool nextXIsMajor = gridMajor.width() != 0 && (startTile.x() + 1) % gridMajor.width() == 0;

QPoint rowTile = startTile;
QPoint rowPos = startPos;

if (p.doStaggerX(startTile.x()))
if (isStaggered)
rowPos.ry() += p.rowHeight;

for (; rowPos.y() <= rect.bottom() && (rowTile.y() < map()->height() || map()->infinite()); rowTile.ry()++) {
lines.append(QLine(rowPos + oct[1], rowPos + oct[2]));
lines.append(QLine(rowPos + oct[2], rowPos + oct[3]));
lines.append(QLine(rowPos + oct[3], rowPos + oct[4]));

const bool isStaggered = p.doStaggerX(startTile.x());
const bool lastRow = rowTile.y() == map()->height() - 1;
const bool lastColumn = rowTile.x() == map()->width() - 1;
const bool bottomLeft = rowTile.x() == 0 || (lastRow && isStaggered);
const bool bottomRight = lastColumn || (lastRow && isStaggered);

if (bottomRight)
lines.append(QLine(rowPos + oct[5], rowPos + oct[6]));
if (lastRow)
lines.append(QLine(rowPos + oct[6], rowPos + oct[7]));
if (bottomLeft)
lines.append(QLine(rowPos + oct[7], rowPos + oct[0]));

painter->drawLines(lines);
lines.resize(0);
for (; rowPos.y() <= rect.bottom() && (rowTile.y() <= map()->height() || infinite); rowTile.ry()++) {
const bool yIsMajor = gridMajor.height() != 0 && rowTile.y() % gridMajor.height() == 0;
const bool firstRow = !infinite && rowTile.y() == 0;
const bool lastRow = !infinite && rowTile.y() == map()->height();

if (!firstColumn && !(lastRow && (isStaggered || rowTile.x() == 0)) && !(firstRow && lastColumn && !isStaggered)) {
if ((yIsMajor && !isStaggered) || xIsMajor)
majorLines.append(topLeft.translated(rowPos));
else
minorLines.append(topLeft.translated(rowPos));
}

if (!firstColumn && !lastColumn) {
if (yIsMajor)
majorLines.append(top.translated(rowPos));
else
minorLines.append(top.translated(rowPos));
}

if (!lastColumn && !(lastRow && (isStaggered || startTile.x() == map()->width() - 1)) && !(firstColumn && firstRow && !isStaggered)) {
if ((yIsMajor && !isStaggered) || nextXIsMajor)
majorLines.append(topRight.translated(rowPos));
else
minorLines.append(topRight.translated(rowPos));
}

rowPos.ry() += p.tileHeight + p.sideLengthY;
}

startPos.rx() += p.columnWidth;

painter->setPen(gridPen);
painter->drawLines(minorLines.constData(), minorLines.size());
painter->setPen(majorGridPen);
painter->drawLines(majorLines.constData(), majorLines.size());
minorLines.clear();
majorLines.clear();
}
} else {
// Odd row shifting is applied in the rendering loop, so un-apply it here
if (p.doStaggerY(startTile.y()))
startPos.rx() -= p.columnWidth;

for (; startPos.y() <= rect.bottom() && (startTile.y() < map()->height() || map()->infinite()); startTile.ry()++) {
for (; startPos.y() <= rect.bottom() && (startTile.y() <= map()->height() || infinite); startTile.ry()++) {
const bool yIsMajor = gridMajor.height() != 0 && startTile.y() % gridMajor.height() == 0;
const bool isStaggered = p.doStaggerY(startTile.y());
const bool firstRow = !infinite && startTile.y() == 0;
const bool lastRow = !infinite && startTile.y() == map()->height();

QPoint rowTile = startTile;
QPoint rowPos = startPos;

if (p.doStaggerY(startTile.y()))
rowPos.rx() += p.columnWidth;

for (; rowPos.x() <= rect.right() && (rowTile.x() < map()->width() || map()->infinite()); rowTile.rx()++) {
lines.append(QLine(rowPos + oct[0], rowPos + oct[1]));
lines.append(QLine(rowPos + oct[1], rowPos + oct[2]));
lines.append(QLine(rowPos + oct[3], rowPos + oct[4]));

const bool isStaggered = p.doStaggerY(startTile.y());
const bool lastRow = rowTile.y() == map()->height() - 1;
const bool lastColumn = rowTile.x() == map()->width() - 1;
const bool bottomLeft = lastRow || (rowTile.x() == 0 && !isStaggered);
const bool bottomRight = lastRow || (lastColumn && isStaggered);

if (lastColumn)
lines.append(QLine(rowPos + oct[4], rowPos + oct[5]));
if (bottomRight)
lines.append(QLine(rowPos + oct[5], rowPos + oct[6]));
if (bottomLeft)
lines.append(QLine(rowPos + oct[7], rowPos + oct[0]));
if (isStaggered) {
rowTile.rx() -= 1;
rowPos.rx() -= p.columnWidth;
}

painter->drawLines(lines);
lines.resize(0);
for (; rowPos.x() <= rect.right() && (rowTile.x() <= map()->width() || infinite); rowTile.rx()++) {
const bool xIsMajor = gridMajor.width() != 0 && rowTile.x() % gridMajor.width() == 0;
const bool nextXIsMajor = gridMajor.width() != 0 && (rowTile.x() + 1) % gridMajor.width() == 0;
const bool lastColumn = !infinite && rowTile.x() == map()->width();
const bool firstColumn = !infinite && rowTile.x() == -1;

// Left side
if (!lastRow && !(isStaggered && firstColumn)) {
if (xIsMajor)
majorLines.append(left.translated(rowPos));
else
minorLines.append(left.translated(rowPos));
}

// Top-left side
if (!firstColumn && !(lastRow && !isStaggered && rowTile.x() == 0) && !(lastColumn && isStaggered) && !(firstRow && lastColumn)) {
if (yIsMajor || (!isStaggered && xIsMajor))
majorLines.append(topLeft.translated(rowPos));
else
minorLines.append(topLeft.translated(rowPos));
}

// Top-right side
if (!lastColumn && !(firstRow && firstColumn) && !(lastRow && isStaggered && rowTile.x() == map()->width() - 1)) {
if (yIsMajor || (isStaggered && nextXIsMajor))
majorLines.append(topRight.translated(rowPos));
else
minorLines.append(topRight.translated(rowPos));
}

rowPos.rx() += p.tileWidth + p.sideLengthX;
}

startPos.ry() += p.rowHeight;

painter->setPen(gridPen);
painter->drawLines(minorLines.constData(), minorLines.size());
painter->setPen(majorGridPen);
painter->drawLines(majorLines.constData(), majorLines.size());
minorLines.clear();
majorLines.clear();
}
}
}
Expand Down

0 comments on commit c05da7b

Please sign in to comment.