diff --git a/sources/editor/editorcommands.cpp b/sources/editor/editorcommands.cpp index 7646ab88d..62bc2a013 100644 --- a/sources/editor/editorcommands.cpp +++ b/sources/editor/editorcommands.cpp @@ -16,6 +16,7 @@ along with QElectroTech. If not, see . */ #include "editorcommands.h" +#include "../diagram.h" /** @brief ElementEditionCommand::ElementEditionCommand @@ -666,10 +667,32 @@ void RotateFineElementsCommand::redo() } +/** + @brief selectionCenter + @param items + @return the center of the united scene bounding rect of items, + snapped to the nearest half of the diagram grid. Mirroring across a + half-grid line keeps points that were on the grid on the grid. +*/ +static QPointF selectionCenter(const QList &items) +{ + QRectF bounding; + for (auto *item : items) { + bounding = bounding.united(item->sceneBoundingRect()); + } + QPointF center = bounding.center(); + const qreal half_x = Diagram::xGrid / 2.0; + const qreal half_y = Diagram::yGrid / 2.0; + center.setX(qRound(center.x() / half_x) * half_x); + center.setY(qRound(center.y() / half_y) * half_y); + return center; +} + MirrorElementsCommand::MirrorElementsCommand(ElementScene *scene, QUndoCommand *parent) : ElementEditionCommand(QObject::tr("Miroir de sélection", "undo caption"), scene, nullptr, parent) { m_items = scene->selectedItems(); + m_axis_x = selectionCenter(m_items).x(); } /** @@ -680,28 +703,28 @@ void MirrorElementsCommand::redo() foreach (auto *item, m_items) { if (item->type() == PartText::Type) { PartText* staticText = qgraphicsitem_cast(item); - staticText->mirror(); + staticText->mirror(m_axis_x); } else if (item->type() == PartDynamicTextField::Type) { PartDynamicTextField* dyntext = qgraphicsitem_cast(item); - dyntext->mirror(); + dyntext->mirror(m_axis_x); } else if (item->type() == PartArc::Type) { PartArc* arc = qgraphicsitem_cast(item); - arc->mirror(); + arc->mirror(m_axis_x); } else if (item->type() == PartEllipse::Type) { PartEllipse* ellipse = qgraphicsitem_cast(item); - ellipse->mirror(); + ellipse->mirror(m_axis_x); } else if (item->type() == PartLine::Type) { PartLine* line = qgraphicsitem_cast(item); - line->mirror(); + line->mirror(m_axis_x); } else if (item->type() == PartPolygon::Type) { PartPolygon* poly = qgraphicsitem_cast(item); - poly->mirror(); + poly->mirror(m_axis_x); } else if (item->type() == PartRectangle::Type) { PartRectangle* rect = qgraphicsitem_cast(item); - rect->mirror(); + rect->mirror(m_axis_x); } else if (item->type() == PartTerminal::Type) { PartTerminal* term = qgraphicsitem_cast(item); - term->mirror(); + term->mirror(m_axis_x); } } } @@ -718,6 +741,7 @@ FlipElementsCommand::FlipElementsCommand(ElementScene *scene, QUndoCommand *pare ElementEditionCommand(QObject::tr("Retourner la sélection", "undo caption"), scene, nullptr, parent) { m_items = scene->selectedItems(); + m_axis_y = selectionCenter(m_items).y(); } /** @@ -728,28 +752,28 @@ void FlipElementsCommand::redo() foreach (auto *item, m_items) { if (item->type() == PartText::Type) { PartText* staticText = qgraphicsitem_cast(item); - staticText->flip(); + staticText->flip(m_axis_y); } else if (item->type() == PartDynamicTextField::Type) { PartDynamicTextField* dyntext = qgraphicsitem_cast(item); - dyntext->flip(); + dyntext->flip(m_axis_y); } else if (item->type() == PartArc::Type) { PartArc* arc = qgraphicsitem_cast(item); - arc->flip(); + arc->flip(m_axis_y); } else if (item->type() == PartEllipse::Type) { PartEllipse* ellipse = qgraphicsitem_cast(item); - ellipse->flip(); + ellipse->flip(m_axis_y); } else if (item->type() == PartLine::Type) { PartLine* line = qgraphicsitem_cast(item); - line->flip(); + line->flip(m_axis_y); } else if (item->type() == PartPolygon::Type) { PartPolygon* poly = qgraphicsitem_cast(item); - poly->flip(); + poly->flip(m_axis_y); } else if (item->type() == PartRectangle::Type) { PartRectangle* rect = qgraphicsitem_cast(item); - rect->flip(); + rect->flip(m_axis_y); } else if (item->type() == PartTerminal::Type) { PartTerminal* term = qgraphicsitem_cast(item); - term->flip(); + term->flip(m_axis_y); } } } diff --git a/sources/editor/editorcommands.h b/sources/editor/editorcommands.h index b920b47d9..e5a811a83 100644 --- a/sources/editor/editorcommands.h +++ b/sources/editor/editorcommands.h @@ -279,6 +279,12 @@ private: }; +/** + @brief The MirrorElementsCommand class + Mirror the selected parts horizontally (left <-> right) across the + vertical line through the center of the selection, so the selection + keeps its place in the scene. +*/ class MirrorElementsCommand : public ElementEditionCommand { public: @@ -288,8 +294,15 @@ public: private: ElementScene *m_scene =nullptr; QList m_items; + qreal m_axis_x = 0; }; +/** + @brief The FlipElementsCommand class + Flip the selected parts vertically (top <-> bottom) across the + horizontal line through the center of the selection, so the selection + keeps its place in the scene. +*/ class FlipElementsCommand : public ElementEditionCommand { public: @@ -299,6 +312,7 @@ public: private: ElementScene *m_scene =nullptr; QList m_items; + qreal m_axis_y = 0; }; #endif diff --git a/sources/editor/graphicspart/partarc.cpp b/sources/editor/graphicspart/partarc.cpp index 596f19f37..fb06bdb83 100644 --- a/sources/editor/graphicspart/partarc.cpp +++ b/sources/editor/graphicspart/partarc.cpp @@ -197,13 +197,13 @@ qreal PartArc::rotation() const { return qRound(m_rot * 100.0) / 100.0; } -void PartArc::flip() { +void PartArc::flip(qreal axis_y) { m_start_angle = (-1) * m_start_angle; m_span_angle = (-1) * m_span_angle; while (m_start_angle < 0) { m_start_angle += (360*16); } while (m_start_angle >= (360*16)) { m_start_angle -= (360*16); } auto p1 = mapToScene(m_rect.x(),m_rect.y()); - p1.setY(((-1.0) * p1.y()) - m_rect.height()); + p1.setY(2 * axis_y - p1.y() - m_rect.height()); p1 = mapFromScene(p1.x(),p1.y()); m_rect = QRectF(m_rect.x(), p1.y(), m_rect.width(), m_rect.height()); prepareGeometryChange(); @@ -211,13 +211,13 @@ void PartArc::flip() { emit rectChanged(); } -void PartArc::mirror() { +void PartArc::mirror(qreal axis_x) { m_start_angle = (180.0 * 16) - m_start_angle; m_span_angle = (-1) * m_span_angle; while (m_start_angle < 0) { m_start_angle += (360*16); } while (m_start_angle >= (360*16)) { m_start_angle -= (360*16); } auto p1 = mapToScene(m_rect.x(),m_rect.y()); - p1.setX(((-1.0) * p1.x()) - m_rect.width()); + p1.setX(2 * axis_x - p1.x() - m_rect.width()); p1 = mapFromScene(p1.x(), p1.y()); m_rect = QRectF(p1.x(), m_rect.y(), m_rect.width(), m_rect.height()); prepareGeometryChange(); diff --git a/sources/editor/graphicspart/partarc.h b/sources/editor/graphicspart/partarc.h index bc6f51301..cd0878120 100644 --- a/sources/editor/graphicspart/partarc.h +++ b/sources/editor/graphicspart/partarc.h @@ -62,8 +62,8 @@ class PartArc : public AbstractPartEllipse QRectF sceneGeometricRect() const override; void setRotation(qreal angle); qreal rotation() const; - void flip(); - void mirror(); + void flip(qreal axis_y = 0); + void mirror(qreal axis_x = 0); void addHandler() override; void removeHandler() override; diff --git a/sources/editor/graphicspart/partdynamictextfield.cpp b/sources/editor/graphicspart/partdynamictextfield.cpp index d04ac6f5b..c65c955df 100644 --- a/sources/editor/graphicspart/partdynamictextfield.cpp +++ b/sources/editor/graphicspart/partdynamictextfield.cpp @@ -74,19 +74,19 @@ void PartDynamicTextField::setRotation(qreal angle) { setPos(QTransform().rotate(diffAngle).map(pos())); } -void PartDynamicTextField::mirror() { +void PartDynamicTextField::mirror(qreal axis_x) { // at first: rotate the text: QGraphicsObject::setRotation(QET::correctAngle(360-rotation(), true)); // then see, where we need to re-position depending on the angle! qreal rot = qRound(QET::correctAngle(rotation(), true)); qreal c = qCos(qDegreesToRadians(rot)); qreal s = qSin(qDegreesToRadians(rot)); - qreal x = (-1) * pos().x() - c * boundingRect().width(); + qreal x = 2 * axis_x - pos().x() - c * boundingRect().width(); qreal y = pos().y() - s * boundingRect().width(); setPos(x, y); } -void PartDynamicTextField::flip() { +void PartDynamicTextField::flip(qreal axis_y) { // at first: rotate the text: QGraphicsObject::setRotation(QET::correctAngle(360-rotation(), true)); // then see, where we need to re-position depending on the angle! @@ -94,7 +94,7 @@ void PartDynamicTextField::flip() { qreal c = qCos(qDegreesToRadians(rot)); qreal s = qSin(qDegreesToRadians(rot)); qreal x = pos().x() + s * boundingRect().height(); - qreal y = (-1) * pos().y() - c * boundingRect().height(); + qreal y = 2 * axis_y - pos().y() - c * boundingRect().height(); setPos(x, y); } diff --git a/sources/editor/graphicspart/partdynamictextfield.h b/sources/editor/graphicspart/partdynamictextfield.h index 4fdf0d700..1b730639b 100644 --- a/sources/editor/graphicspart/partdynamictextfield.h +++ b/sources/editor/graphicspart/partdynamictextfield.h @@ -106,8 +106,8 @@ class PartDynamicTextField : public QGraphicsTextItem, public CustomElementPart bool rotationPointCenter() const; void setRotation(qreal angle); - void mirror(); - void flip(); + void mirror(qreal axis_x = 0); + void flip(qreal axis_y = 0); protected: diff --git a/sources/editor/graphicspart/partellipse.cpp b/sources/editor/graphicspart/partellipse.cpp index 4cf3a4f10..dc19d6102 100644 --- a/sources/editor/graphicspart/partellipse.cpp +++ b/sources/editor/graphicspart/partellipse.cpp @@ -256,9 +256,9 @@ qreal PartEllipse::rotation() const { return qRound(m_rot * 100.0) / 100.0; } -void PartEllipse::flip() { +void PartEllipse::flip(qreal axis_y) { auto p1 = mapToScene(m_rect.x(), m_rect.y()); - p1.setY(((-1.0) * p1.y()) - m_rect.height()); + p1.setY(2 * axis_y - p1.y() - m_rect.height()); p1 = mapFromScene(p1.x(), p1.y()); m_rect = QRectF(p1.x(), p1.y(), m_rect.width(), m_rect.height()); prepareGeometryChange(); @@ -266,9 +266,9 @@ void PartEllipse::flip() { emit rectChanged(); } -void PartEllipse::mirror() { +void PartEllipse::mirror(qreal axis_x) { auto p1 = mapToScene(m_rect.x(), m_rect.y()); - p1.setX(((-1.0) * p1.x()) - m_rect.width()); + p1.setX(2 * axis_x - p1.x() - m_rect.width()); p1 = mapFromScene(p1.x(), p1.y()); m_rect = QRectF(p1.x(), p1.y(), m_rect.width(), m_rect.height()); prepareGeometryChange(); diff --git a/sources/editor/graphicspart/partellipse.h b/sources/editor/graphicspart/partellipse.h index e566a1f16..477aa3e8c 100644 --- a/sources/editor/graphicspart/partellipse.h +++ b/sources/editor/graphicspart/partellipse.h @@ -62,8 +62,8 @@ class PartEllipse : public AbstractPartEllipse void setRect(const QRectF &rect) override {AbstractPartEllipse::setRect(rect); adjustHandlerPos();} void setRotation(qreal angle); qreal rotation() const; - void flip(); - void mirror(); + void flip(qreal axis_y = 0); + void mirror(qreal axis_x = 0); void addHandler() override; void removeHandler() override; diff --git a/sources/editor/graphicspart/partline.cpp b/sources/editor/graphicspart/partline.cpp index c0d86bc57..cf71406b1 100644 --- a/sources/editor/graphicspart/partline.cpp +++ b/sources/editor/graphicspart/partline.cpp @@ -590,11 +590,11 @@ qreal PartLine::rotation() const { return qRound(m_rot * 100.0) / 100.0; } -void PartLine::flip() { +void PartLine::flip(qreal axis_y) { auto p1 = mapToScene(m_line.p1()); auto p2 = mapToScene(m_line.p2()); - p1 = QPointF(p1.x(), (-1) * p1.y()); - p2 = QPointF(p2.x(), (-1) * p2.y()); + p1 = QPointF(p1.x(), 2 * axis_y - p1.y()); + p2 = QPointF(p2.x(), 2 * axis_y - p2.y()); m_line.setP1(mapFromScene(p1)); m_line.setP2(mapFromScene(p2)); setLine(m_line); @@ -603,11 +603,11 @@ void PartLine::flip() { emit lineChanged(); } -void PartLine::mirror() { +void PartLine::mirror(qreal axis_x) { auto p1 = mapToScene(m_line.p1()); auto p2 = mapToScene(m_line.p2()); - p1 = QPointF((-1) * p1.x(), p1.y()); - p2 = QPointF((-1) * p2.x(), p2.y()); + p1 = QPointF(2 * axis_x - p1.x(), p1.y()); + p2 = QPointF(2 * axis_x - p2.x(), p2.y()); m_line.setP1(mapFromScene(p1)); m_line.setP2(mapFromScene(p2)); setLine(m_line); diff --git a/sources/editor/graphicspart/partline.h b/sources/editor/graphicspart/partline.h index dfbb361d5..1e3082a53 100644 --- a/sources/editor/graphicspart/partline.h +++ b/sources/editor/graphicspart/partline.h @@ -96,8 +96,8 @@ class PartLine : public CustomElementGraphicPart void setSecondEndLength(const qreal &l); void setRotation(qreal angle); qreal rotation() const; - void flip(); - void mirror(); + void flip(qreal axis_y = 0); + void mirror(qreal axis_x = 0); void addHandler() override; void removeHandler() override; diff --git a/sources/editor/graphicspart/partpolygon.cpp b/sources/editor/graphicspart/partpolygon.cpp index eceae2bb2..eb96837e4 100644 --- a/sources/editor/graphicspart/partpolygon.cpp +++ b/sources/editor/graphicspart/partpolygon.cpp @@ -307,10 +307,10 @@ qreal PartPolygon::rotation() const { return qRound(m_rot * 100.0) / 100.0; } -void PartPolygon::flip() { +void PartPolygon::flip(qreal axis_y) { for (auto &pt : m_polygon) { pt = mapToScene(pt.x(), pt.y()); - pt = QPointF(pt.x(), (-1) * pt.y()); + pt = QPointF(pt.x(), 2 * axis_y - pt.y()); pt = mapFromScene(pt.x(), pt.y()); } setPolygon(m_polygon); @@ -319,10 +319,10 @@ void PartPolygon::flip() { emit polygonChanged(); } -void PartPolygon::mirror() { +void PartPolygon::mirror(qreal axis_x) { for (auto &pt : m_polygon) { pt = mapToScene(pt.x(), pt.y()); - pt = QPointF((-1) * pt.x(), pt.y()); + pt = QPointF(2 * axis_x - pt.x(), pt.y()); pt = mapFromScene(pt.x(), pt.y()); } setPolygon(m_polygon); diff --git a/sources/editor/graphicspart/partpolygon.h b/sources/editor/graphicspart/partpolygon.h index f8a67f18c..bee786be4 100644 --- a/sources/editor/graphicspart/partpolygon.h +++ b/sources/editor/graphicspart/partpolygon.h @@ -89,8 +89,8 @@ class PartPolygon : public CustomElementGraphicPart void setRotation (qreal angle); qreal rotation () const; - void flip(); - void mirror(); + void flip(qreal axis_y = 0); + void mirror(qreal axis_x = 0); void addHandler() override; void removeHandler() override; diff --git a/sources/editor/graphicspart/partrectangle.cpp b/sources/editor/graphicspart/partrectangle.cpp index 78fa726e3..e1955b1b9 100644 --- a/sources/editor/graphicspart/partrectangle.cpp +++ b/sources/editor/graphicspart/partrectangle.cpp @@ -187,11 +187,11 @@ qreal PartRectangle::rotation() const { return qRound(m_rot * 100.0) / 100.0; } -void PartRectangle::flip() { +void PartRectangle::flip(qreal axis_y) { auto height = m_rect.height(); auto p1 = mapToScene(m_rect.x(),m_rect.y()); qreal x = p1.x(); - qreal y = ((-1.0) * p1.y()) - height; + qreal y = 2 * axis_y - p1.y() - height; p1 = mapFromScene(x, y); m_rect.setX(p1.x()); m_rect.setY(p1.y()); @@ -201,10 +201,10 @@ void PartRectangle::flip() { emit rectChanged(); } -void PartRectangle::mirror() { +void PartRectangle::mirror(qreal axis_x) { auto width = m_rect.width(); auto p1 = mapToScene(m_rect.x(),m_rect.y()); - qreal x = ((-1.0) * p1.x()) - width; + qreal x = 2 * axis_x - p1.x() - width; qreal y = p1.y(); p1 = mapFromScene(x, y); m_rect.setX(p1.x()); diff --git a/sources/editor/graphicspart/partrectangle.h b/sources/editor/graphicspart/partrectangle.h index 00ed2bc39..c2d259f77 100644 --- a/sources/editor/graphicspart/partrectangle.h +++ b/sources/editor/graphicspart/partrectangle.h @@ -73,8 +73,8 @@ class PartRectangle : public CustomElementGraphicPart void setYRadius(qreal Y); void setRotation(qreal angle); qreal rotation() const; - void flip(); - void mirror(); + void flip(qreal axis_y = 0); + void mirror(qreal axis_x = 0); QRectF sceneGeometricRect() const override; virtual QPointF sceneTopLeft() const; diff --git a/sources/editor/graphicspart/partterminal.cpp b/sources/editor/graphicspart/partterminal.cpp index 194ae0aec..0b85ba026 100644 --- a/sources/editor/graphicspart/partterminal.cpp +++ b/sources/editor/graphicspart/partterminal.cpp @@ -293,9 +293,9 @@ qreal PartTerminal::rotation() const { @brief PartTerminal::flip turn part upside down */ -void PartTerminal::flip() { +void PartTerminal::flip(qreal axis_y) { d->m_pos.setX( pos().x()); - d->m_pos.setY((-1.0) * pos().y()); + d->m_pos.setY(2 * axis_y - pos().y()); switch (d->m_orientation) { case Qet::North : setOrientation(Qet::South); break; @@ -314,8 +314,8 @@ void PartTerminal::flip() { @brief PartTerminal::mirror turn part from left to right */ -void PartTerminal::mirror() { - d->m_pos.setX((-1.0) * pos().x()); +void PartTerminal::mirror(qreal axis_x) { + d->m_pos.setX(2 * axis_x - pos().x()); d->m_pos.setY( pos().y()); switch (d->m_orientation) { case Qet::North : break; diff --git a/sources/editor/graphicspart/partterminal.h b/sources/editor/graphicspart/partterminal.h index d0ba05da9..1ef413cd6 100644 --- a/sources/editor/graphicspart/partterminal.h +++ b/sources/editor/graphicspart/partterminal.h @@ -96,8 +96,8 @@ class PartTerminal : public CustomElementGraphicPart void setRotation(qreal angle); qreal rotation() const; - void flip(); - void mirror(); + void flip(qreal axis_y = 0); + void mirror(qreal axis_x = 0); void nextOrientation(); void previousOrientation(); diff --git a/sources/editor/graphicspart/parttext.cpp b/sources/editor/graphicspart/parttext.cpp index ae6d97277..47d3007af 100644 --- a/sources/editor/graphicspart/parttext.cpp +++ b/sources/editor/graphicspart/parttext.cpp @@ -75,7 +75,7 @@ void PartText::setRotation(qreal angle) { setPos(QTransform().rotate(diffAngle).map(pos())); } -void PartText::mirror() { +void PartText::mirror(qreal axis_x) { // at first: rotate the text: QGraphicsObject::setRotation(QET::correctAngle((360-rotation()), true)); // then see, where we need to re-position depending on text, font ... @@ -86,12 +86,12 @@ void PartText::mirror() { qreal c = qCos(qDegreesToRadians(rot)); qreal s = qSin(qDegreesToRadians(rot)); // Now: Move! - qreal x = (-1) * pos().x() - c * (textwidth); + qreal x = 2 * axis_x - pos().x() - c * (textwidth); qreal y = pos().y() - s * (textwidth); setPos(x, y); } -void PartText::flip() { +void PartText::flip(qreal axis_y) { // at first: rotate the text: QGraphicsObject::setRotation(QET::correctAngle((360-rotation()), true)); // then see, where we need to re-position depending on text, font ... @@ -103,7 +103,7 @@ void PartText::flip() { qreal s = qSin(qDegreesToRadians(rot)); // Now: Move! qreal x = pos().x() - s * (textheight); - qreal y = (-1) * pos().y() + c * (textheight); + qreal y = 2 * axis_y - pos().y() + c * (textheight); setPos(x, y); } diff --git a/sources/editor/graphicspart/parttext.h b/sources/editor/graphicspart/parttext.h index 4ba66f7c8..29609440e 100644 --- a/sources/editor/graphicspart/parttext.h +++ b/sources/editor/graphicspart/parttext.h @@ -64,8 +64,8 @@ class PartText : public QGraphicsTextItem, public CustomElementPart { void fromXml(const QDomElement &) override; const QDomElement toXml(QDomDocument &) const override; void setRotation(qreal angle); - void mirror(); - void flip(); + void mirror(qreal axis_x = 0); + void flip(qreal axis_y = 0); bool isUseless() const override; QRectF sceneGeometricRect() const override; void startUserTransformation(const QRectF &) override;