diff --git a/src/libtiled/map.cpp b/src/libtiled/map.cpp index f9f84f0354..5e39f52474 100644 --- a/src/libtiled/map.cpp +++ b/src/libtiled/map.cpp @@ -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. diff --git a/src/libtiled/map.h b/src/libtiled/map.h index 36bf729345..d54384525f 100644 --- a/src/libtiled/map.h +++ b/src/libtiled/map.h @@ -81,6 +81,7 @@ class TILEDSHARED_EXPORT Map : public Object TileWidthProperty, TileHeightProperty, InfiniteProperty, + InvertYAxisProperty, HexSideLengthProperty, StaggerAxisProperty, StaggerIndexProperty, @@ -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; @@ -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); @@ -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 clone() const; @@ -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; diff --git a/src/libtiled/mapobject.cpp b/src/libtiled/mapobject.cpp index d25b5a7b76..b9f0e73094 100644 --- a/src/libtiled/mapobject.cpp +++ b/src/libtiled/mapobject.cpp @@ -272,7 +272,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; diff --git a/src/libtiled/mapobject.h b/src/libtiled/mapobject.h index e335af8a52..1d52ec2ea4 100644 --- a/src/libtiled/mapobject.h +++ b/src/libtiled/mapobject.h @@ -316,7 +316,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); } diff --git a/src/libtiled/mapreader.cpp b/src/libtiled/mapreader.cpp index 6dbb9c0ce1..1c767954d4 100644 --- a/src/libtiled/mapreader.cpp +++ b/src/libtiled/mapreader.cpp @@ -264,6 +264,7 @@ std::unique_ptr 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); @@ -313,11 +314,11 @@ std::unique_ptr 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) @@ -325,6 +326,11 @@ std::unique_ptr MapReaderPrivate::readMap() if (object->height() == 0) object->setHeight(tileSize.height()); } + + // Invert Y-axis if set + if (mMap->invertYAxis()) + object->setY(mMap->height() * mMap->tileHeight() - object->y()); + } } } diff --git a/src/libtiled/maptovariantconverter.cpp b/src/libtiled/maptovariantconverter.cpp index 93e7fb882f..6bb5f99547 100644 --- a/src/libtiled/maptovariantconverter.cpp +++ b/src/libtiled/maptovariantconverter.cpp @@ -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(); diff --git a/src/libtiled/mapwriter.cpp b/src/libtiled/mapwriter.cpp index 63758d2208..21d95f152d 100644 --- a/src/libtiled/mapwriter.cpp +++ b/src/libtiled/mapwriter.cpp @@ -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"), @@ -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))) { diff --git a/src/libtiled/varianttomapconverter.cpp b/src/libtiled/varianttomapconverter.cpp index a2242d10e4..b80e888757 100644 --- a/src/libtiled/varianttomapconverter.cpp +++ b/src/libtiled/varianttomapconverter.cpp @@ -77,6 +77,7 @@ std::unique_ptr 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); diff --git a/src/plugins/lua/luaplugin.cpp b/src/plugins/lua/luaplugin.cpp index e8d53b2d37..1a3e3952c3 100644 --- a/src/plugins/lua/luaplugin.cpp +++ b/src/plugins/lua/luaplugin.cpp @@ -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()); diff --git a/src/tiled/abstractobjecttool.cpp b/src/tiled/abstractobjecttool.cpp index 21180fe342..70eb675a92 100644 --- a/src/tiled/abstractobjecttool.cpp +++ b/src/tiled/abstractobjecttool.cpp @@ -26,6 +26,7 @@ #include "changepolygon.h" #include "changetileobjectgroup.h" #include "documentmanager.h" +#include "invertyaxishelper.h" #include "mapdocument.h" #include "map.h" #include "mapobject.h" @@ -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) diff --git a/src/tiled/abstracttiletool.cpp b/src/tiled/abstracttiletool.cpp index 77a38062c2..23793fc967 100644 --- a/src/tiled/abstracttiletool.cpp +++ b/src/tiled/abstracttiletool.cpp @@ -21,6 +21,7 @@ #include "abstracttiletool.h" #include "brushitem.h" +#include "invertyaxishelper.h" #include "map.h" #include "mapdocument.h" #include "maprenderer.h" @@ -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()); diff --git a/src/tiled/changemapproperty.cpp b/src/tiled/changemapproperty.cpp index c52d4052d0..f93e031848 100644 --- a/src/tiled/changemapproperty.cpp +++ b/src/tiled/changemapproperty.cpp @@ -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")); @@ -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); diff --git a/src/tiled/editablemap.cpp b/src/tiled/editablemap.cpp index 95acf8bd9d..25cbf5e4f6 100644 --- a/src/tiled/editablemap.cpp +++ b/src/tiled/editablemap.cpp @@ -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()) diff --git a/src/tiled/editablemap.h b/src/tiled/editablemap.h index 543c3ff849..c1510fbbb6 100644 --- a/src/tiled/editablemap.h +++ b/src/tiled/editablemap.h @@ -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) @@ -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; @@ -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); @@ -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(); diff --git a/src/tiled/invertyaxishelper.h b/src/tiled/invertyaxishelper.h new file mode 100644 index 0000000000..255f068b03 --- /dev/null +++ b/src/tiled/invertyaxishelper.h @@ -0,0 +1,69 @@ +/* + * invertyaxishelper.h + * Copyright 2013, Thorbjørn Lindeijer + * Copyright 2018, Adrian Frances + * + * 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 . + */ + +#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())); + return boundingRect.height() - y; + } + return y; +} + +} // Namespace Tiled diff --git a/src/tiled/mapobjectmodel.cpp b/src/tiled/mapobjectmodel.cpp index 729de79e76..c91b8826f8 100644 --- a/src/tiled/mapobjectmodel.cpp +++ b/src/tiled/mapobjectmodel.cpp @@ -26,6 +26,7 @@ #include "changemapobject.h" #include "changeproperties.h" #include "grouplayer.h" +#include "invertyaxishelper.h" #include "layermodel.h" #include "map.h" #include "mapdocument.h" @@ -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; diff --git a/src/tiled/propertybrowser.cpp b/src/tiled/propertybrowser.cpp index f1f5636523..0de675ad0e 100644 --- a/src/tiled/propertybrowser.cpp +++ b/src/tiled/propertybrowser.cpp @@ -57,6 +57,7 @@ #include "wangcolormodel.h" #include "wangoverlay.h" #include "wangset.h" +#include "invertyaxishelper.h" #include @@ -739,6 +740,7 @@ void PropertyBrowser::addMapProperties() auto tileWidthProperty = addProperty(TileWidthProperty, QMetaType::Int, tr("Tile Width"), groupProperty); auto tileHeightProperty = addProperty(TileHeightProperty, QMetaType::Int, tr("Tile Height"), groupProperty); addProperty(InfiniteProperty, QMetaType::Bool, tr("Infinite"), groupProperty); + addProperty(InvertYAxisProperty, QMetaType::Bool, tr("Invert Y-Axis"), groupProperty); tileWidthProperty->setAttribute(QStringLiteral("minimum"), 1); tileHeightProperty->setAttribute(QStringLiteral("minimum"), 1); @@ -1179,6 +1181,10 @@ void PropertyBrowser::applyMapValue(PropertyId id, const QVariant &val) undoStack->endMacro(); break; } + case InvertYAxisProperty: { + command = new ChangeMapProperty(mMapDocument, Map::InvertYAxisProperty, val.toInt()); + break; + } case OrientationProperty: { Map::Orientation orientation = static_cast(val.toInt() + 1); command = new ChangeMapProperty(mMapDocument, orientation); @@ -1271,7 +1277,8 @@ QUndoCommand *PropertyBrowser::applyMapObjectValueTo(PropertyId id, const QVaria case YProperty: { command = new ChangeMapObject(mDocument, mapObject, MapObject::PositionProperty, - QPointF(mapObject->x(), val.toReal())); + QPointF(mapObject->x(), + InvertYAxisHelper(mMapDocument).pixelY(val.toReal()))); break; } case WidthProperty: { @@ -1851,6 +1858,7 @@ void PropertyBrowser::updateProperties() mIdToProperty[TileWidthProperty]->setValue(map->tileWidth()); mIdToProperty[TileHeightProperty]->setValue(map->tileHeight()); mIdToProperty[InfiniteProperty]->setValue(map->infinite()); + mIdToProperty[InvertYAxisProperty]->setValue(map->invertYAxis()); mIdToProperty[OrientationProperty]->setValue(map->orientation() - 1); mIdToProperty[HexSideLengthProperty]->setValue(map->hexSideLength()); mIdToProperty[StaggerAxisProperty]->setValue(map->staggerAxis()); @@ -1891,7 +1899,7 @@ void PropertyBrowser::updateProperties() if (auto visibleProperty = mIdToProperty[VisibleProperty]) visibleProperty->setValue(mapObject->isVisible()); mIdToProperty[XProperty]->setValue(mapObject->x()); - mIdToProperty[YProperty]->setValue(mapObject->y()); + mIdToProperty[YProperty]->setValue(InvertYAxisHelper(mMapDocument).pixelY(mapObject->y())); if (flags & ObjectHasDimensions) { mIdToProperty[WidthProperty]->setValue(mapObject->width()); diff --git a/src/tiled/propertybrowser.h b/src/tiled/propertybrowser.h index ead7b6f2aa..125fcfbf71 100644 --- a/src/tiled/propertybrowser.h +++ b/src/tiled/propertybrowser.h @@ -148,6 +148,7 @@ class PropertyBrowser : public QtTreePropertyBrowser WangColorProbabilityProperty, WangSetTypeProperty, InfiniteProperty, + InvertYAxisProperty, TemplateProperty, CompressionLevelProperty, ChunkWidthProperty, diff --git a/src/tiled/snaphelper.cpp b/src/tiled/snaphelper.cpp index 7c29e51df9..4b98f931d0 100644 --- a/src/tiled/snaphelper.cpp +++ b/src/tiled/snaphelper.cpp @@ -40,7 +40,7 @@ SnapHelper::SnapHelper(const MapRenderer *renderer, if (modifiers & Qt::ControlModifier) { if (modifiers & Qt::ShiftModifier) { toggleFineSnap(); - } else { + } else { toggleSnap(); } } @@ -58,7 +58,7 @@ void SnapHelper::toggleSnap() break; } } - + void SnapHelper::toggleFineSnap() { switch (mSnapMode) { diff --git a/src/tiled/snaphelper.h b/src/tiled/snaphelper.h index b846f1ac07..e2fe6444f7 100644 --- a/src/tiled/snaphelper.h +++ b/src/tiled/snaphelper.h @@ -31,7 +31,7 @@ class SnapHelper SnapHelper(const MapRenderer *renderer, Qt::KeyboardModifiers modifiers = {}); void toggleSnap(); - + void toggleFineSnap(); bool snaps() const { return mSnapMode != NoSnap || mSnapToPixels; }