Skip to content
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

Add option to invert Y-axis #3679

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/libtiled/map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,24 @@ Map::~Map()
qDeleteAll(mLayers);
}

void Map::setInvertYAxis(bool invertYAxis)
{
mParameters.invertYAxis = invertYAxis;

for (auto layer : mLayers) {
if (!layer->isObjectGroup())
continue;

auto og = layer->asObjectGroup();
for (auto it = og->begin(); it != og->end(); ++it) {
auto object = *it;
// Tile objects are anchored in the lower-left already, so don't height-adjust
if (!object->isTileObject())
object->setY(object->y() + object->height() * (invertYAxis ? 1 : -1));
}
}
}

/**
* Returns the margins that have to be taken into account when figuring
* out which part of the map to repaint after changing some tiles.
Expand Down
12 changes: 11 additions & 1 deletion src/libtiled/map.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ class TILEDSHARED_EXPORT Map : public Object
TileWidthProperty,
TileHeightProperty,
InfiniteProperty,
InvertYAxisProperty,
HexSideLengthProperty,
StaggerAxisProperty,
StaggerIndexProperty,
Expand Down Expand Up @@ -158,6 +159,7 @@ class TILEDSHARED_EXPORT Map : public Object
int tileWidth = 0;
int tileHeight = 0;
bool infinite = false;
bool invertYAxis = false;
int hexSideLength = 0;
StaggerAxis staggerAxis = StaggerY;
StaggerIndex staggerIndex = StaggerOdd;
Expand Down Expand Up @@ -216,6 +218,9 @@ class TILEDSHARED_EXPORT Map : public Object
bool infinite() const;
void setInfinite(bool infinite);

bool invertYAxis() const;
void setInvertYAxis(bool invertYAxis);

int hexSideLength() const;
void setHexSideLength(int hexSideLength);

Expand Down Expand Up @@ -285,7 +290,7 @@ class TILEDSHARED_EXPORT Map : public Object

QSize chunkSize() const;
void setChunkSize(QSize size);

bool isTilesetUsed(const Tileset *tileset) const;

std::unique_ptr<Map> clone() const;
Expand Down Expand Up @@ -465,6 +470,11 @@ inline void Map::setInfinite(bool infinite)
mParameters.infinite = infinite;
}

inline bool Map::invertYAxis() const
{
return mParameters.invertYAxis;
}

inline int Map::hexSideLength() const
{
return mParameters.hexSideLength;
Expand Down
3 changes: 1 addition & 2 deletions src/libtiled/mapobject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@

#include "mapobject.h"

#include "map.h"
#include "maprenderer.h"
#include "objectgroup.h"
#include "objecttemplate.h"
Expand Down Expand Up @@ -272,7 +271,7 @@ Alignment MapObject::alignment(const Map *map) const

if (alignment == Unspecified) {
if (mCell.isEmpty())
return TopLeft;
return map && map->invertYAxis() ? BottomLeft : TopLeft;
else if (map && map->orientation() == Map::Isometric)
return Bottom;

Expand Down
3 changes: 2 additions & 1 deletion src/libtiled/mapobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

#pragma once

#include "map.h"
vinnydiehl marked this conversation as resolved.
Show resolved Hide resolved
#include "object.h"
#include "tiled.h"
#include "tilelayer.h"
Expand Down Expand Up @@ -316,7 +317,7 @@ inline qreal MapObject::y() const
{ return mPos.y(); }

/**
* Sets the x position of this object.
* Sets the y position of this object.
*/
inline void MapObject::setY(qreal y)
{ mPos.setY(y); }
Expand Down
8 changes: 7 additions & 1 deletion src/libtiled/mapreader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ std::unique_ptr<Map> MapReaderPrivate::readMap()
mapParameters.tileWidth = atts.value(QLatin1String("tilewidth")).toInt();
mapParameters.tileHeight = atts.value(QLatin1String("tileheight")).toInt();
mapParameters.infinite = atts.value(QLatin1String("infinite")).toInt();
mapParameters.invertYAxis = atts.value(QLatin1String("invertyaxis")).toInt();
mapParameters.hexSideLength = atts.value(QLatin1String("hexsidelength")).toInt();
mapParameters.staggerAxis = staggerAxisFromString(staggerAxis);
mapParameters.staggerIndex = staggerIndexFromString(staggerIndex);
Expand Down Expand Up @@ -313,18 +314,23 @@ std::unique_ptr<Map> MapReaderPrivate::readMap()
tileset->loadImage();
}

// Fix up sizes of tile objects. This is for backwards compatibility.
LayerIterator iterator(mMap.get());
while (Layer *layer = iterator.next()) {
if (ObjectGroup *objectGroup = layer->asObjectGroup()) {
for (MapObject *object : *objectGroup) {
// Fix up sizes of tile objects. This is for backwards compatibility.
if (const Tile *tile = object->cell().tile()) {
const QSizeF tileSize = tile->size();
if (object->width() == 0)
object->setWidth(tileSize.width());
if (object->height() == 0)
object->setHeight(tileSize.height());
}

// Invert Y-axis if set
if (mMap->invertYAxis())
object->setY(mMap->height() * mMap->tileHeight() - object->y());

}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/libtiled/maptovariantconverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ QVariant MapToVariantConverter::toVariant(const Map &map, const QDir &mapDir)
mapVariant[QStringLiteral("tilewidth")] = map.tileWidth();
mapVariant[QStringLiteral("tileheight")] = map.tileHeight();
mapVariant[QStringLiteral("infinite")] = map.infinite();
mapVariant[QStringLiteral("invertyaxis")] = map.invertYAxis();
mapVariant[QStringLiteral("nextlayerid")] = map.nextLayerId();
mapVariant[QStringLiteral("nextobjectid")] = map.nextObjectId();
mapVariant[QStringLiteral("compressionlevel")] = map.compressionLevel();
Expand Down
9 changes: 8 additions & 1 deletion src/libtiled/mapwriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,8 @@ void MapWriterPrivate::writeMap(QXmlStreamWriter &w, const Map &map)
QString::number(map.tileHeight()));
w.writeAttribute(QStringLiteral("infinite"),
QString::number(map.infinite()));
w.writeAttribute(QStringLiteral("invertyaxis"),
QString::number(map.invertYAxis()));

if (map.orientation() == Map::Hexagonal) {
w.writeAttribute(QStringLiteral("hexsidelength"),
Expand Down Expand Up @@ -784,7 +786,12 @@ void MapWriterPrivate::writeObject(QXmlStreamWriter &w,

if (!mapObject.isTemplateBase()) {
w.writeAttribute(QStringLiteral("x"), QString::number(pos.x()));
w.writeAttribute(QStringLiteral("y"), QString::number(pos.y()));

qreal y = pos.y();
if (mapObject.map()->invertYAxis())
y = mapObject.map()->height() * mapObject.map()->tileHeight() - y;

w.writeAttribute(QStringLiteral("y"), QString::number(y));
}

if (shouldWrite(true, isTemplateInstance, mapObject.propertyChanged(MapObject::SizeProperty))) {
Expand Down
1 change: 1 addition & 0 deletions src/libtiled/varianttomapconverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ std::unique_ptr<Map> VariantToMapConverter::toMap(const QVariant &variant,
mapParameters.tileWidth = variantMap[QStringLiteral("tilewidth")].toInt();
mapParameters.tileHeight = variantMap[QStringLiteral("tileheight")].toInt();
mapParameters.infinite = variantMap[QStringLiteral("infinite")].toInt();
mapParameters.invertYAxis = variantMap[QStringLiteral("invertyaxis")].toInt();
mapParameters.hexSideLength = variantMap[QStringLiteral("hexsidelength")].toInt();
mapParameters.staggerAxis = staggerAxisFromString(staggerAxis);
mapParameters.staggerIndex = staggerIndexFromString(staggerIndex);
Expand Down
1 change: 1 addition & 0 deletions src/plugins/lua/luaplugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ void LuaWriter::writeMap(const Map *map)
mWriter.writeKeyAndValue("height", map->height());
mWriter.writeKeyAndValue("tilewidth", map->tileWidth());
mWriter.writeKeyAndValue("tileheight", map->tileHeight());
mWriter.writeKeyAndValue("invertyaxis", map->invertYAxis());
mWriter.writeKeyAndValue("nextlayerid", map->nextLayerId());
mWriter.writeKeyAndValue("nextobjectid", map->nextObjectId());

Expand Down
6 changes: 5 additions & 1 deletion src/tiled/abstractobjecttool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include "tile.h"
#include "tmxmapformat.h"
#include "utils.h"
#include "invertyaxishelper.h"
vinnydiehl marked this conversation as resolved.
Show resolved Hide resolved

#include <QFileDialog>
#include <QGraphicsView>
Expand Down Expand Up @@ -185,7 +186,10 @@ void AbstractObjectTool::mouseMoved(const QPointF &pos,
const QPointF tilePosF = mapDocument()->renderer()->screenToTileCoords(offsetPos);
const int x = qFloor(tilePosF.x());
const int y = qFloor(tilePosF.y());
setStatusInfo(QStringLiteral("%1, %2 (%3, %4)").arg(x).arg(y).arg(pixelPos.x()).arg(pixelPos.y()));
setStatusInfo(QStringLiteral("%1, %2 (%3, %4)").arg(x)
.arg(InvertYAxisHelper(mapDocument()).tileY(y))
.arg(pixelPos.x())
.arg(InvertYAxisHelper(mapDocument()).pixelY(pixelPos.y())));
}

void AbstractObjectTool::mousePressed(QGraphicsSceneMouseEvent *event)
Expand Down
3 changes: 2 additions & 1 deletion src/tiled/abstracttiletool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "mapscene.h"
#include "tilelayer.h"
#include "tilestamp.h"
#include "invertyaxishelper.h"
vinnydiehl marked this conversation as resolved.
Show resolved Hide resolved

#include <QKeyEvent>
#include <QtMath>
Expand Down Expand Up @@ -196,7 +197,7 @@ void AbstractTileTool::updateStatusInfo()

setStatusInfo(QStringLiteral("%1, %2 [%3]")
.arg(mTilePosition.x())
.arg(mTilePosition.y())
.arg(InvertYAxisHelper(mapDocument()).tileY(mTilePosition.y()))
.arg(tileIdString));
} else {
setStatusInfo(QString());
Expand Down
10 changes: 10 additions & 0 deletions src/tiled/changemapproperty.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ ChangeMapProperty::ChangeMapProperty(MapDocument *mapDocument,
setText(QCoreApplication::translate("Undo Commands",
"Change Infinite Property"));
break;
case Map::InvertYAxisProperty:
setText(QCoreApplication::translate("Undo Commands",
"Change Invert Y-Axis Property"));
break;
case Map::HexSideLengthProperty:
setText(QCoreApplication::translate("Undo Commands",
"Change Hex Side Length"));
Expand Down Expand Up @@ -174,6 +178,12 @@ void ChangeMapProperty::swap()
mIntValue = infinite;
break;
}
case Map::InvertYAxisProperty: {
const int invertYAxis = map->invertYAxis();
map->setInvertYAxis(mIntValue);
mIntValue = invertYAxis;
break;
}
case Map::OrientationProperty: {
const Map::Orientation orientation = map->orientation();
map->setOrientation(mOrientation);
Expand Down
1 change: 1 addition & 0 deletions src/tiled/clipboardmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ bool ClipboardManager::copySelection(const MapDocument &mapDocument)
mapParameters.width = selectionBounds.width();
mapParameters.height = selectionBounds.height();
mapParameters.infinite = false;
mapParameters.invertYAxis = false;
vinnydiehl marked this conversation as resolved.
Show resolved Hide resolved
Map copyMap(mapParameters);

if (!selectedArea.isEmpty()) {
Expand Down
8 changes: 8 additions & 0 deletions src/tiled/editablemap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,14 @@ void EditableMap::setInfinite(bool value)
map()->setInfinite(value);
}

void EditableMap::setInvertYAxis(bool value)
{
if (auto doc = mapDocument())
push(new ChangeMapProperty(doc, Map::InvertYAxisProperty, value));
else if (!checkReadOnly())
map()->setInvertYAxis(value);
}

void EditableMap::setHexSideLength(int value)
{
if (auto doc = mapDocument())
Expand Down
8 changes: 8 additions & 0 deletions src/tiled/editablemap.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class EditableMap : public EditableAsset
Q_PROPERTY(int tileWidth READ tileWidth WRITE setTileWidth)
Q_PROPERTY(int tileHeight READ tileHeight WRITE setTileHeight)
Q_PROPERTY(bool infinite READ infinite WRITE setInfinite)
Q_PROPERTY(bool invertYAxis READ invertYAxis WRITE setInvertYAxis)
Q_PROPERTY(int hexSideLength READ hexSideLength WRITE setHexSideLength)
Q_PROPERTY(StaggerAxis staggerAxis READ staggerAxis WRITE setStaggerAxis)
Q_PROPERTY(StaggerIndex staggerIndex READ staggerIndex WRITE setStaggerIndex)
Expand Down Expand Up @@ -121,6 +122,7 @@ class EditableMap : public EditableAsset
int tileWidth() const;
int tileHeight() const;
bool infinite() const;
bool invertYAxis() const;
int hexSideLength() const;
StaggerAxis staggerAxis() const;
StaggerIndex staggerIndex() const;
Expand Down Expand Up @@ -184,6 +186,7 @@ class EditableMap : public EditableAsset
void setTileHeight(int value);
Q_INVOKABLE void setTileSize(int width, int height);
void setInfinite(bool value);
void setInvertYAxis(bool value);
void setHexSideLength(int value);
void setStaggerAxis(StaggerAxis value);
void setStaggerIndex(StaggerIndex value);
Expand Down Expand Up @@ -264,6 +267,11 @@ inline bool EditableMap::infinite() const
return map()->infinite();
}

inline bool EditableMap::invertYAxis() const
{
return map()->invertYAxis();
}

inline int EditableMap::hexSideLength() const
{
return map()->hexSideLength();
Expand Down
69 changes: 69 additions & 0 deletions src/tiled/invertyaxishelper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* invertyaxishelper.h
* Copyright 2013, Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
* Copyright 2018, Adrian Frances <adrianfranceslillo@gmail.com>
*
* This file is part of Tiled.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/

#pragma once

#include "mapdocument.h"
#include "maprenderer.h"
#include "preferences.h"

namespace Tiled {

class InvertYAxisHelper
{
public:
InvertYAxisHelper(MapDocument *mapDocument)
: mMapDocument(mapDocument)
{}

int tileY(int y) const;
qreal pixelY(qreal y) const;

private:
MapDocument *mMapDocument;
};

/**
* Inverts Y coordinate in tiles when applicable.
*/
inline int InvertYAxisHelper::tileY(int y) const
{
// Check if Invert Y Axis is set
if (mMapDocument->map()->invertYAxis())
return mMapDocument->map()->height() - 1 - y;
return y;
}

/**
* Inverts Y coordinate in pixels when applicable.
*/
inline qreal InvertYAxisHelper::pixelY(qreal y) const
{
// Obtain the map document
if (mMapDocument->map()->invertYAxis()) {
const MapRenderer *renderer = mMapDocument->renderer();
QRect boundingRect = renderer->boundingRect(QRect(QPoint(), mMapDocument->map()->size()));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should just call renderer->mapBoundingRect(), which is the same thing for fixed-size maps.

For infinite maps, it seems to me like in that case the origin should just stay where it is, and the only thing we want to do is to negate the Y value, so it should probably be handled explicitly (also in InvertYAxisHelper::tileY).

Another issue with this is actually that it is completely meaningless for isometric maps, since it will take the map screen pixel height and subtract the non-projected object position. I'm not sure how we can handle isometric maps properly in the case of "invert Y" actually, we may need to add a mapPixelBoundingRect (or just mapPixelHeight) function to the MapRenderer, since the mapBoundingRect is essentially a mapScreenBoundingRect function.

return boundingRect.height() - y;
}
return y;
}

} // Namespace Tiled
3 changes: 2 additions & 1 deletion src/tiled/mapobjectmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "map.h"
#include "mapdocument.h"
#include "objectgroup.h"
#include "invertyaxishelper.h"
vinnydiehl marked this conversation as resolved.
Show resolved Hide resolved

#include <QApplication>
#include <QPalette>
Expand Down Expand Up @@ -165,7 +166,7 @@ QVariant MapObjectModel::data(const QModelIndex &index, int role) const
return QLatin1Char('(')
+ QString::number(mapObject->x())
+ QLatin1String(", ")
+ QString::number(mapObject->y())
+ QString::number(InvertYAxisHelper(mapDocument()).tileY(mapObject->y()))
+ QLatin1Char(')');
}
break;
Expand Down