Skip to content

Commit

Permalink
Flipping a group of objects now works as expected (mapeditor#1481)
Browse files Browse the repository at this point in the history
This also affects flipping of single objects, which now takes the rotation into account.

Closes mapeditor#1475
  • Loading branch information
Vitek1425 authored and bjorn committed Mar 21, 2017
1 parent 83b26c4 commit 0d27be0
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 31 deletions.
79 changes: 63 additions & 16 deletions src/libtiled/mapobject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* mapobject.cpp
* Copyright 2008-2013, Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
* Copyright 2008, Roderic Morris <roderic@ccs.neu.edu>
* Copyright 2017, Klimov Viktor <vitek.fomino@bk.ru>
*
* This file is part of libtiled.
*
Expand Down Expand Up @@ -34,6 +35,7 @@
#include "tile.h"

#include <QFontMetricsF>
#include <qmath.h>

namespace Tiled {

Expand Down Expand Up @@ -200,26 +202,31 @@ void MapObject::setMapObjectProperty(Property property, const QVariant &value)
* Flip this object in the given \a direction. This doesn't change the size
* of the object.
*/
void MapObject::flip(FlipDirection direction)
void MapObject::flip(FlipDirection direction, const QPointF &origin)
{
if (!mCell.isEmpty()) {
if (direction == FlipHorizontally)
mCell.setFlippedHorizontally(!mCell.flippedHorizontally());
else if (direction == FlipVertically)
mCell.setFlippedVertically(!mCell.flippedVertically());
//computing new rotation and flip transform
QTransform flipTransform;
flipTransform.translate(origin.x(), origin.y());
qreal newRotation = 0;
if (direction == FlipHorizontally) {
newRotation = 180.0 - rotation();
flipTransform.scale(-1, 1);
} else { //direction == FlipVertically
flipTransform.scale(1, -1);
newRotation = -rotation();
}
flipTransform.translate(-origin.x(), -origin.y());

if (!mPolygon.isEmpty()) {
const QPointF center2 = mPolygon.boundingRect().center() * 2;

if (direction == FlipHorizontally) {
for (int i = 0; i < mPolygon.size(); ++i)
mPolygon[i].setX(center2.x() - mPolygon[i].x());
} else if (direction == FlipVertically) {
for (int i = 0; i < mPolygon.size(); ++i)
mPolygon[i].setY(center2.y() - mPolygon[i].y());
}
}
if (!mCell.isEmpty())
flipTileObject(flipTransform);
else if (!mPolygon.isEmpty())
flipPolygonObject(flipTransform);
else
flipRectObject(flipTransform);

//installing new rotation after computing new position
setRotation(newRotation);
}

/**
Expand All @@ -240,4 +247,44 @@ MapObject *MapObject::clone() const
return o;
}

void MapObject::flipRectObject(const QTransform &flipTransform)
{
QPointF oldBottomLeftPoint = QPointF(cos(qDegreesToRadians(rotation() + 90)) * height() + x(),
sin(qDegreesToRadians(rotation() + 90)) * height() + y());
QPointF newPos = flipTransform.map(oldBottomLeftPoint);

setPosition(newPos);
}

void MapObject::flipPolygonObject(const QTransform &flipTransform)
{
QTransform polygonToMapTransform;
polygonToMapTransform.translate(x(), y());
polygonToMapTransform.rotate(rotation());

QPointF localPolygonCenter = mPolygon.boundingRect().center();
QTransform polygonFlip;
polygonFlip.translate(localPolygonCenter.x(), localPolygonCenter.y());
polygonFlip.scale(1, -1);
polygonFlip.translate(-localPolygonCenter.x(), -localPolygonCenter.y());

QPointF oldBottomLeftPoint = polygonToMapTransform.map(polygonFlip.map(QPointF(0, 0)));
QPointF newPos = flipTransform.map(oldBottomLeftPoint);

mPolygon = polygonFlip.map(mPolygon);
setPosition(newPos);
}

void MapObject::flipTileObject(const QTransform &flipTransform)
{
mCell.setFlippedVertically(!mCell.flippedVertically());

//old tile position is bottomLeftPoint
QPointF topLeftTilePoint = QPointF(cos(qDegreesToRadians(rotation() - 90)) * height() + x(),
sin(qDegreesToRadians(rotation() - 90)) * height() + y());
QPointF newPos = flipTransform.map(topLeftTilePoint);

setPosition(newPos);
}

} // namespace Tiled
6 changes: 5 additions & 1 deletion src/libtiled/mapobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,11 +179,15 @@ class TILEDSHARED_EXPORT MapObject : public Object
QVariant mapObjectProperty(Property property) const;
void setMapObjectProperty(Property property, const QVariant &value);

void flip(FlipDirection direction);
void flip(FlipDirection direction, const QPointF &origin);

MapObject *clone() const;

private:
void flipRectObject(const QTransform &flipTransform);
void flipPolygonObject(const QTransform &flipTransform);
void flipTileObject(const QTransform &flipTransform);

int mId;
QString mName;
QString mType;
Expand Down
34 changes: 30 additions & 4 deletions src/tiled/flipmapobjects.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*
* flipmapobjects.cpp
* Copyright 2013, Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
* Copyright 2017, Klimov Viktor <vitek.fomino@bk.ru>
*
* This file is part of Tiled.
*
Expand Down Expand Up @@ -39,14 +40,39 @@ FlipMapObjects::FlipMapObjects(MapDocument *mapDocument,
setText(QCoreApplication::translate("Undo Commands",
"Flip %n Object(s)",
nullptr, mapObjects.size()));

//computing objects center
QRectF boundaryObjectsRect;
for (MapObject *object : mMapObjects) {
QTransform objectTransform;
objectTransform.translate(object->x(), object->y());
objectTransform.rotate(object->rotation());
objectTransform.translate(-object->x(), -object->y());

if (!object->cell().isEmpty()) { //computing bound rect for cell
QRectF cellRect = QRectF(object->x(),
object->y(),
object->width(), -object->height()).normalized();
boundaryObjectsRect = boundaryObjectsRect.united(objectTransform.mapRect(cellRect));
} else if (!object->polygon().empty()) { //computing bound rect for polygon
const QPolygonF &objectPolygon = object->polygon();
QTransform polygonToMapTransform;
polygonToMapTransform.translate(object->x(),
object->y());
polygonToMapTransform.rotate(object->rotation());
boundaryObjectsRect = boundaryObjectsRect.united(polygonToMapTransform.mapRect(QRectF(objectPolygon.boundingRect())));
} else { //computing bound rect for other
boundaryObjectsRect = boundaryObjectsRect.united(objectTransform.mapRect(object->bounds()));
}
}
mObjectsCenter = boundaryObjectsRect.center();
}

void FlipMapObjects::flip()
{
// TODO: Flip them properly as a group
const auto &objects = mMapObjects;
for (MapObject *object : objects)
object->flip(mFlipDirection);
//flip objects
for (MapObject *object : mMapObjects)
object->flip(mFlipDirection, mObjectsCenter);

emit mMapDocument->mapObjectModel()->objectsChanged(mMapObjects);
}
3 changes: 2 additions & 1 deletion src/tiled/flipmapobjects.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ class FlipMapObjects : public QUndoCommand
void flip();

MapDocument *mMapDocument;
QList<MapObject *> mMapObjects;
const QList<MapObject *> mMapObjects;
QPointF mObjectsCenter;
FlipDirection mFlipDirection;
};

Expand Down
19 changes: 10 additions & 9 deletions src/tiled/propertybrowser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -947,15 +947,16 @@ QUndoCommand *PropertyBrowser::applyMapObjectValueTo(PropertyId id, const QVaria
const bool flippedVertically = flippingFlags & 2;

// You can only change one checkbox at a time
if (mapObject->cell().flippedHorizontally() != flippedHorizontally) {
command = new FlipMapObjects(mMapDocument,
QList<MapObject*>() << mapObject,
FlipHorizontally);
} else if (mapObject->cell().flippedVertically() != flippedVertically) {
command = new FlipMapObjects(mMapDocument,
QList<MapObject*>() << mapObject,
FlipVertically);
}
Cell newCell = mapObject->cell();
newCell.setFlippedHorizontally(flippedHorizontally);
newCell.setFlippedVertically(flippedVertically);

MapObjectCell mapObjectCell;
mapObjectCell.object = mapObject;
mapObjectCell.cell = newCell;

command = new ChangeMapObjectCells(mMapDocument,
QVector<MapObjectCell> () << mapObjectCell);
break;
}
}
Expand Down

0 comments on commit 0d27be0

Please sign in to comment.