diff options
| author | Samu Laaksonen <laaksonen.sj@gmail.com> | 2012-09-25 20:00:41 +0300 |
|---|---|---|
| committer | Samu Laaksonen <laaksonen.sj@gmail.com> | 2012-09-25 20:00:41 +0300 |
| commit | 685fe05def77b039221edf06c74af74915d536c5 (patch) | |
| tree | 9c1a14b8f68bc0f801bdec3edc447d04fdbf7a4c /libtiled/orthogonalrenderer.cpp | |
| parent | 29eabac0670574efd384182c065f53d08c42a483 (diff) | |
| download | prism-685fe05def77b039221edf06c74af74915d536c5.tar.gz prism-685fe05def77b039221edf06c74af74915d536c5.zip | |
Initial code commit
Added some stuff for project base
- tiled sources
- few Qt based classes for gfx
Diffstat (limited to 'libtiled/orthogonalrenderer.cpp')
| -rw-r--r-- | libtiled/orthogonalrenderer.cpp | 372 |
1 files changed, 372 insertions, 0 deletions
diff --git a/libtiled/orthogonalrenderer.cpp b/libtiled/orthogonalrenderer.cpp new file mode 100644 index 0000000..6f82590 --- /dev/null +++ b/libtiled/orthogonalrenderer.cpp @@ -0,0 +1,372 @@ +/* + * orthogonalrenderer.cpp + * Copyright 2009-2011, Thorbjørn Lindeijer <thorbjorn@lindeijer.nl> + * + * This file is part of libtiled. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * 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. + */ + +#include "orthogonalrenderer.h" + +#include "map.h" +#include "mapobject.h" +#include "tile.h" +#include "tilelayer.h" +#include "tileset.h" + +#include <cmath> + +using namespace Tiled; + +QSize OrthogonalRenderer::mapSize() const +{ + return QSize(map()->width() * map()->tileWidth(), + map()->height() * map()->tileHeight()); +} + +QRect OrthogonalRenderer::boundingRect(const QRect &rect) const +{ + const int tileWidth = map()->tileWidth(); + const int tileHeight = map()->tileHeight(); + + return QRect(rect.x() * tileWidth, + rect.y() * tileHeight, + rect.width() * tileWidth, + rect.height() * tileHeight); +} + +QRectF OrthogonalRenderer::boundingRect(const MapObject *object) const +{ + const QRectF bounds = object->bounds(); + const QRectF rect(tileToPixelCoords(bounds.topLeft()), + tileToPixelCoords(bounds.bottomRight())); + + QRectF boundingRect; + + if (object->tile()) { + const QPointF bottomLeft = rect.topLeft(); + const QPixmap &img = object->tile()->image(); + boundingRect = QRectF(bottomLeft.x(), + bottomLeft.y() - img.height(), + img.width(), + img.height()).adjusted(-1, -1, 1, 1); + } else { + // The -2 and +3 are to account for the pen width and shadow + switch (object->shape()) { + case MapObject::Rectangle: + if (rect.isNull()) { + boundingRect = rect.adjusted(-10 - 2, -10 - 2, 10 + 3, 10 + 3); + } else { + const int nameHeight = object->name().isEmpty() ? 0 : 15; + boundingRect = rect.adjusted(-2, -nameHeight - 2, 3, 3); + } + break; + + case MapObject::Polygon: + case MapObject::Polyline: { + const QPointF &pos = object->position(); + const QPolygonF polygon = object->polygon().translated(pos); + QPolygonF screenPolygon = tileToPixelCoords(polygon); + boundingRect = screenPolygon.boundingRect().adjusted(-2, -2, 3, 3); + break; + } + } + } + + return boundingRect; +} + +QPainterPath OrthogonalRenderer::shape(const MapObject *object) const +{ + QPainterPath path; + + if (object->tile()) { + path.addRect(boundingRect(object)); + } else { + switch (object->shape()) { + case MapObject::Rectangle: { + const QRectF bounds = object->bounds(); + const QRectF rect(tileToPixelCoords(bounds.topLeft()), + tileToPixelCoords(bounds.bottomRight())); + + if (rect.isNull()) { + path.addEllipse(rect.topLeft(), 20, 20); + } else { + path.addRoundedRect(rect, 10, 10); + } + break; + } + case MapObject::Polygon: + case MapObject::Polyline: { + const QPointF &pos = object->position(); + const QPolygonF polygon = object->polygon().translated(pos); + const QPolygonF screenPolygon = tileToPixelCoords(polygon); + if (object->shape() == MapObject::Polygon) { + path.addPolygon(screenPolygon); + } else { + for (int i = 1; i < screenPolygon.size(); ++i) { + path.addPolygon(lineToPolygon(screenPolygon[i - 1], + screenPolygon[i])); + } + path.setFillRule(Qt::WindingFill); + } + break; + } + } + } + + return path; +} + +void OrthogonalRenderer::drawGrid(QPainter *painter, const QRectF &rect) const +{ + const int tileWidth = map()->tileWidth(); + const int tileHeight = map()->tileHeight(); + + if (tileWidth <= 0 || tileHeight <= 0) + return; + + const int startX = qMax(0, (int) (rect.x() / tileWidth) * tileWidth); + const int startY = qMax(0, (int) (rect.y() / tileHeight) * tileHeight); + const int endX = qMin((int) std::ceil(rect.right()), + map()->width() * tileWidth + 1); + const int endY = qMin((int) std::ceil(rect.bottom()), + map()->height() * tileHeight + 1); + + QColor gridColor(Qt::black); + gridColor.setAlpha(128); + + QPen gridPen(gridColor); + gridPen.setDashPattern(QVector<qreal>() << 2 << 2); + + if (startY < endY) { + gridPen.setDashOffset(startY); + painter->setPen(gridPen); + for (int x = startX; x < endX; x += tileWidth) + painter->drawLine(x, startY, x, endY - 1); + } + + if (startX < endX) { + gridPen.setDashOffset(startX); + painter->setPen(gridPen); + for (int y = startY; y < endY; y += tileHeight) + painter->drawLine(startX, y, endX - 1, y); + } +} + +void OrthogonalRenderer::drawTileLayer(QPainter *painter, + const TileLayer *layer, + const QRectF &exposed) const +{ + QTransform savedTransform = painter->transform(); + + const int tileWidth = map()->tileWidth(); + const int tileHeight = map()->tileHeight(); + const QPointF layerPos(layer->x() * tileWidth, + layer->y() * tileHeight); + + painter->translate(layerPos); + + int startX = 0; + int startY = 0; + int endX = layer->width(); + int endY = layer->height(); + + if (!exposed.isNull()) { + QMargins drawMargins = layer->drawMargins(); + drawMargins.setTop(drawMargins.top() - tileHeight); + drawMargins.setRight(drawMargins.right() - tileWidth); + + QRectF rect = exposed.adjusted(-drawMargins.right(), + -drawMargins.bottom(), + drawMargins.left(), + drawMargins.top()); + + rect.translate(-layerPos); + + startX = qMax((int) rect.x() / tileWidth, 0); + startY = qMax((int) rect.y() / tileHeight, 0); + endX = qMin((int) std::ceil(rect.right()) / tileWidth + 1, endX); + endY = qMin((int) std::ceil(rect.bottom()) / tileHeight + 1, endY); + } + + QTransform baseTransform = painter->transform(); + + for (int y = startY; y < endY; ++y) { + for (int x = startX; x < endX; ++x) { + const Cell &cell = layer->cellAt(x, y); + if (cell.isEmpty()) + continue; + + const QPixmap &img = cell.tile->image(); + const QPoint offset = cell.tile->tileset()->tileOffset(); + + qreal m11 = 1; // Horizontal scaling factor + qreal m12 = 0; // Vertical shearing factor + qreal m21 = 0; // Horizontal shearing factor + qreal m22 = 1; // Vertical scaling factor + qreal dx = offset.x() + x * tileWidth; + qreal dy = offset.y() + (y + 1) * tileHeight - img.height(); + + if (cell.flippedAntiDiagonally) { + // Use shearing to swap the X/Y axis + m11 = 0; + m12 = 1; + m21 = 1; + m22 = 0; + + // Compensate for the swap of image dimensions + dy += img.height() - img.width(); + } + if (cell.flippedHorizontally) { + m11 = -m11; + m21 = -m21; + dx += cell.flippedAntiDiagonally ? img.height() : img.width(); + } + if (cell.flippedVertically) { + m12 = -m12; + m22 = -m22; + dy += cell.flippedAntiDiagonally ? img.width() : img.height(); + } + + const QTransform transform(m11, m12, m21, m22, dx, dy); + painter->setTransform(transform * baseTransform); + + painter->drawPixmap(0, 0, img); + } + } + + painter->setTransform(savedTransform); +} + +void OrthogonalRenderer::drawTileSelection(QPainter *painter, + const QRegion ®ion, + const QColor &color, + const QRectF &exposed) const +{ + foreach (const QRect &r, region.rects()) { + const QRectF toFill = QRectF(boundingRect(r)).intersected(exposed); + if (!toFill.isEmpty()) + painter->fillRect(toFill, color); + } +} + +void OrthogonalRenderer::drawMapObject(QPainter *painter, + const MapObject *object, + const QColor &color) const +{ + painter->save(); + + const QRectF bounds = object->bounds(); + QRectF rect(tileToPixelCoords(bounds.topLeft()), + tileToPixelCoords(bounds.bottomRight())); + + painter->translate(rect.topLeft()); + rect.moveTopLeft(QPointF(0, 0)); + + if (object->tile()) { + const QPixmap &img = object->tile()->image(); + const QPoint paintOrigin(0, -img.height()); + painter->drawPixmap(paintOrigin, img); + + QPen pen(Qt::SolidLine); + painter->setPen(pen); + painter->drawRect(QRect(paintOrigin, img.size())); + pen.setStyle(Qt::DotLine); + pen.setColor(color); + painter->setPen(pen); + painter->drawRect(QRect(paintOrigin, img.size())); + } else { + const QPen linePen(color, 2); + const QPen shadowPen(Qt::black, 2); + + QColor brushColor = color; + brushColor.setAlpha(50); + const QBrush fillBrush(brushColor); + + painter->setRenderHint(QPainter::Antialiasing); + + switch (object->shape()) { + case MapObject::Rectangle: { + if (rect.isNull()) + rect = QRectF(QPointF(-10, -10), QSizeF(20, 20)); + + const QFontMetrics fm = painter->fontMetrics(); + QString name = fm.elidedText(object->name(), Qt::ElideRight, + rect.width() + 2); + + // Draw the shadow + painter->setPen(shadowPen); + painter->drawRect(rect.translated(QPointF(1, 1))); + if (!name.isEmpty()) + painter->drawText(QPoint(1, -5 + 1), name); + + painter->setPen(linePen); + painter->setBrush(fillBrush); + painter->drawRect(rect); + if (!name.isEmpty()) + painter->drawText(QPoint(0, -5), name); + + break; + } + + case MapObject::Polyline: { + QPolygonF screenPolygon = tileToPixelCoords(object->polygon()); + + painter->setPen(shadowPen); + painter->drawPolyline(screenPolygon.translated(1, 1)); + + painter->setPen(linePen); + painter->setBrush(fillBrush); + painter->drawPolyline(screenPolygon); + break; + } + + case MapObject::Polygon: { + QPolygonF screenPolygon = tileToPixelCoords(object->polygon()); + + painter->setPen(shadowPen); + painter->drawPolygon(screenPolygon.translated(1, 1)); + + painter->setPen(linePen); + painter->setBrush(fillBrush); + painter->drawPolygon(screenPolygon); + break; + } + } + } + + painter->restore(); +} + +QPointF OrthogonalRenderer::pixelToTileCoords(qreal x, qreal y) const +{ + return QPointF(x / map()->tileWidth(), + y / map()->tileHeight()); +} + +QPointF OrthogonalRenderer::tileToPixelCoords(qreal x, qreal y) const +{ + return QPointF(x * map()->tileWidth(), + y * map()->tileHeight()); +} |
