From 877fe380e3ab06ef25972400525a9382ac8167c2 Mon Sep 17 00:00:00 2001 From: Roman Telezhynskyi Date: Thu, 26 Aug 2021 19:04:24 +0300 Subject: [PATCH] Follow grainline. --- src/app/puzzle/layout/layoutdef.h | 15 +++ src/app/puzzle/layout/vplayout.cpp | 17 ++++ src/app/puzzle/layout/vplayout.h | 1 + src/app/puzzle/layout/vppiece.cpp | 27 ++++- src/app/puzzle/layout/vppiece.h | 2 +- .../puzzle/scene/vpgraphicspiececontrols.cpp | 68 +++++++++---- .../puzzle/scene/vpgraphicspiececontrols.h | 1 + src/app/puzzle/scene/vpmaingraphicsview.cpp | 21 +++- src/app/puzzle/scene/vpmaingraphicsview.h | 2 + .../undocommands/vpundomovepieceonsheet.cpp | 14 +++ .../undocommands/vpundomovepieceonsheet.h | 1 + .../puzzle/undocommands/vpundopiecerotate.cpp | 98 +++++++++++++++++-- .../puzzle/undocommands/vpundopiecerotate.h | 41 ++++++-- src/app/puzzle/vpmainwindow.cpp | 44 ++++++++- src/app/puzzle/vpmainwindow.h | 2 + src/app/puzzle/vpmainwindow.ui | 2 +- src/app/puzzle/xml/vplayoutfilereader.cpp | 2 +- src/app/puzzle/xml/vplayoutfilewriter.cpp | 2 +- src/app/puzzle/xml/vplayoutliterals.cpp | 2 +- src/app/puzzle/xml/vplayoutliterals.h | 2 +- src/libs/ifc/schema/layout/v0.1.0.xsd | 1 + src/libs/vlayout/vlayoutpiece_p.h | 2 +- 22 files changed, 310 insertions(+), 57 deletions(-) diff --git a/src/app/puzzle/layout/layoutdef.h b/src/app/puzzle/layout/layoutdef.h index d23aac007..320d3cf0a 100644 --- a/src/app/puzzle/layout/layoutdef.h +++ b/src/app/puzzle/layout/layoutdef.h @@ -55,6 +55,21 @@ struct VPTransformationOrigon { QPointF origin{}; bool custom{false}; + + bool operator==(const VPTransformationOrigon &origin) const; + bool operator!=(const VPTransformationOrigon &origin) const; }; +//--------------------------------------------------------------------------------------------------------------------- +inline bool VPTransformationOrigon::operator==(const VPTransformationOrigon &origin) const +{ + return this->origin == origin.origin && custom == origin.custom; +} + +//--------------------------------------------------------------------------------------------------------------------- +inline bool VPTransformationOrigon::operator!=(const VPTransformationOrigon &origin) const +{ + return !VPTransformationOrigon::operator==(origin); +} + #endif // LAYOUTDEF_H diff --git a/src/app/puzzle/layout/vplayout.cpp b/src/app/puzzle/layout/vplayout.cpp index 2e574dee1..6c4dcf352 100644 --- a/src/app/puzzle/layout/vplayout.cpp +++ b/src/app/puzzle/layout/vplayout.cpp @@ -108,6 +108,23 @@ auto VPLayout::GetPieces() const -> QList return m_pieces.values(); } +//--------------------------------------------------------------------------------------------------------------------- +auto VPLayout::GetPlacedPieces() const -> QList +{ + QList pieces; + pieces.reserve(m_pieces.size()); + + for (const auto& piece : m_pieces) + { + if (not piece->isNull() && piece->Sheet() != VPSheetPtr() && piece->Sheet() != m_trashSheet) + { + pieces.append(piece); + } + } + + return pieces; +} + //--------------------------------------------------------------------------------------------------------------------- auto VPLayout::GetUnplacedPieces() const -> QList { diff --git a/src/app/puzzle/layout/vplayout.h b/src/app/puzzle/layout/vplayout.h index fb120725e..5ae365376 100644 --- a/src/app/puzzle/layout/vplayout.h +++ b/src/app/puzzle/layout/vplayout.h @@ -49,6 +49,7 @@ public: static void AddPiece(const VPLayoutPtr &layout, const VPPiecePtr &piece); auto GetPieces() const -> QList; + auto GetPlacedPieces() const -> QList; auto GetUnplacedPieces() const -> QList; auto GetTrashedPieces() const -> QList; diff --git a/src/app/puzzle/layout/vppiece.cpp b/src/app/puzzle/layout/vppiece.cpp index c1147f75e..8ce7f581e 100644 --- a/src/app/puzzle/layout/vppiece.cpp +++ b/src/app/puzzle/layout/vppiece.cpp @@ -76,7 +76,7 @@ auto VPPiece::GetPosition() -> QPointF } //--------------------------------------------------------------------------------------------------------------------- -void VPPiece::RotateToGrainline() +void VPPiece::RotateToGrainline(const VPTransformationOrigon &origin) { VPSheetPtr sheet = Sheet(); if (not IsGrainlineEnabled() || sheet.isNull()) @@ -105,7 +105,8 @@ void VPPiece::RotateToGrainline() atFront.setAngle(90); } - return grainline.angleTo(atFront); + qreal angleTo = grainline.angleTo(atFront); + return angleTo; }; auto DegreesAtRear = [grainline, canonical, grainlineType]() @@ -113,7 +114,8 @@ void VPPiece::RotateToGrainline() QLineF atRear = canonical; atRear.setAngle(grainlineType == GrainlineType::Vertical ? 270 : 180); - return grainline.angleTo(atRear); + qreal angleTo = grainline.angleTo(atRear); + return angleTo; }; GrainlineArrowDirection type = GrainlineArrowType(); @@ -129,10 +131,25 @@ void VPPiece::RotateToGrainline() } else { - degrees = qMin(DegreesAtFront(), DegreesAtRear()); + const qreal atFront = DegreesAtFront(); + if (atFront <= 90 || atFront >= 270) + { + degrees = atFront; + } + else + { + degrees = DegreesAtRear(); + } } - Rotate(MappedDetailBoundingRect().center(), degrees); + if (origin.custom) + { + Rotate(MappedDetailBoundingRect().center(), degrees); + } + else + { + Rotate(origin.origin, degrees); + } } //--------------------------------------------------------------------------------------------------------------------- diff --git a/src/app/puzzle/layout/vppiece.h b/src/app/puzzle/layout/vppiece.h index d1825b754..e2bf5985d 100644 --- a/src/app/puzzle/layout/vppiece.h +++ b/src/app/puzzle/layout/vppiece.h @@ -64,7 +64,7 @@ public: /** * @brief RotateToGrainline rotates the piece to follow the grainline */ - void RotateToGrainline(); + void RotateToGrainline(const VPTransformationOrigon &origin); /** * @brief SetSelected Sets wether the piece is selected diff --git a/src/app/puzzle/scene/vpgraphicspiececontrols.cpp b/src/app/puzzle/scene/vpgraphicspiececontrols.cpp index 8716b7313..0e422e1ff 100644 --- a/src/app/puzzle/scene/vpgraphicspiececontrols.cpp +++ b/src/app/puzzle/scene/vpgraphicspiececontrols.cpp @@ -71,17 +71,20 @@ enum class HandleCorner : int BottomLeft = 4 }; -auto TransformationOrigin(const VPLayoutPtr &layout, const QRectF &boundingRect) -> QPointF +auto TransformationOrigin(const VPLayoutPtr &layout, const QRectF &boundingRect) -> VPTransformationOrigon { SCASSERT(layout != nullptr) VPSheetPtr sheet = layout->GetFocusedSheet(); if (not sheet.isNull()) { - VPTransformationOrigon origin = sheet->TransformationOrigin(); - return origin.origin; + return sheet->TransformationOrigin(); } - return boundingRect.center(); + VPTransformationOrigon origin; + origin.origin = boundingRect.center(); + origin.custom = false; + + return origin; } } // namespace @@ -233,8 +236,8 @@ auto VPGraphicsTransformationOrigin::RotationCenter(QPainter *painter) const -> const qreal scale = SceneScale(scene()); qreal radius = centerRadius1/scale; - QPointF transformationOrigin = TransformationOrigin(m_layout, QRectF()); - QRectF rect(transformationOrigin.x()-radius, transformationOrigin.y()-radius, radius*2., radius*2.); + VPTransformationOrigon transformationOrigin = TransformationOrigin(m_layout, QRectF()); + QRectF rect(transformationOrigin.origin.x()-radius, transformationOrigin.origin.y()-radius, radius*2., radius*2.); QPainterPath center1; center1.addEllipse(rect); @@ -249,7 +252,7 @@ auto VPGraphicsTransformationOrigin::RotationCenter(QPainter *painter) const -> path.addPath(center1); radius = centerRadius2/scale; - rect = QRectF(transformationOrigin.x()-radius, transformationOrigin.y()-radius, radius*2., radius*2.); + rect = QRectF(transformationOrigin.origin.x()-radius, transformationOrigin.origin.y()-radius, radius*2., radius*2.); QPainterPath center2; center2.addEllipse(rect); @@ -271,8 +274,8 @@ auto VPGraphicsTransformationOrigin::Center1() const -> QPainterPath { const qreal scale = SceneScale(scene()); qreal radius = centerRadius1/scale; - QPointF transformationOrigin = TransformationOrigin(m_layout, QRectF()); - QRectF rect(transformationOrigin.x()-radius, transformationOrigin.y()-radius, radius*2., radius*2.); + VPTransformationOrigon transformationOrigin = TransformationOrigin(m_layout, QRectF()); + QRectF rect(transformationOrigin.origin.x()-radius, transformationOrigin.origin.y()-radius, radius*2., radius*2.); QPainterPath center1; center1.addEllipse(rect); @@ -285,8 +288,9 @@ auto VPGraphicsTransformationOrigin::Center2() const -> QPainterPath { const qreal scale = SceneScale(scene()); qreal radius = centerRadius2/scale; - QPointF transformationOrigin = TransformationOrigin(m_layout, QRectF()); - QRectF rect = QRectF(transformationOrigin.x()-radius, transformationOrigin.y()-radius, radius*2., radius*2.); + VPTransformationOrigon transformationOrigin = TransformationOrigin(m_layout, QRectF()); + QRectF rect = QRectF(transformationOrigin.origin.x()-radius, transformationOrigin.origin.y()-radius, radius*2., + radius*2.); QPainterPath center2; center2.addEllipse(rect); @@ -389,6 +393,7 @@ void VPGraphicsPieceControls::mousePressEvent(QGraphicsSceneMouseEvent *event) if(event->button() == Qt::LeftButton) { m_rotationStartPoint = event->scenePos(); + m_rotationSum = 0; m_controlsVisible = false; m_handleCorner = HandleCorner(event->scenePos()); m_ignorePieceTransformation = true; @@ -470,13 +475,18 @@ void VPGraphicsPieceControls::mouseMoveEvent(QGraphicsSceneMouseEvent *event) QPointF rotationNewPoint = event->scenePos(); // get the angle from the center to the initial click point - QPointF rotationOrigin = TransformationOrigin(m_layout, m_pieceRect); - QLineF initPosition(rotationOrigin, m_rotationStartPoint); - QLineF initRotationPosition(rotationOrigin, rotationNewPoint); + VPTransformationOrigon rotationOrigin = TransformationOrigin(m_layout, m_pieceRect); + QLineF initPosition(rotationOrigin.origin, m_rotationStartPoint); + QLineF initRotationPosition(rotationOrigin.origin, rotationNewPoint); - qreal angle = initPosition.angleTo(initRotationPosition); + qreal rotateOn = initPosition.angleTo(initRotationPosition); - if (not qFuzzyIsNull(angle)) + if (rotateOn > 180) + { + rotateOn = rotateOn - 360.; + } + + if (not qFuzzyIsNull(rotateOn)) { auto PreparePieces = [this]() { @@ -499,14 +509,32 @@ void VPGraphicsPieceControls::mouseMoveEvent(QGraphicsSceneMouseEvent *event) VPLayoutPtr layout = m_layout.toStrongRef(); if (not layout.isNull()) { + if (layout->LayoutSettings().GetFollowGrainline() && not rotationOrigin.custom) + { + if (m_rotationSum > 90 || m_rotationSum < -90) + { + m_rotationSum = rotateOn; + } + else + { + m_rotationSum += rotateOn; + } + } + else + { + m_rotationSum = rotateOn; + } + if (pieces.size() == 1) { - auto *command = new VPUndoPieceRotate(pieces.first(), rotationOrigin, angle, allowChangeMerge); + auto *command = new VPUndoPieceRotate(pieces.first(), rotationOrigin, rotateOn, m_rotationSum, + allowChangeMerge); layout->UndoStack()->push(command); } else if (pieces.size() > 1) { - auto *command = new VPUndoPiecesRotate(pieces, rotationOrigin, angle, allowChangeMerge); + auto *command = new VPUndoPiecesRotate(pieces, rotationOrigin, rotateOn, m_rotationSum, + allowChangeMerge); layout->UndoStack()->push(command); } } @@ -514,8 +542,8 @@ void VPGraphicsPieceControls::mouseMoveEvent(QGraphicsSceneMouseEvent *event) if (m_originSaved && m_savedOrigin.custom) { - QLineF line(rotationOrigin, m_savedOrigin.origin); - line.setAngle(line.angle()+angle); + QLineF line(rotationOrigin.origin, m_savedOrigin.origin); + line.setAngle(line.angle()+rotateOn); m_savedOrigin.origin = line.p2(); } diff --git a/src/app/puzzle/scene/vpgraphicspiececontrols.h b/src/app/puzzle/scene/vpgraphicspiececontrols.h index 07b26e655..5c6691590 100644 --- a/src/app/puzzle/scene/vpgraphicspiececontrols.h +++ b/src/app/puzzle/scene/vpgraphicspiececontrols.h @@ -108,6 +108,7 @@ private: Q_DISABLE_COPY(VPGraphicsPieceControls) QRectF m_pieceRect{}; QPointF m_rotationStartPoint{}; + qreal m_rotationSum{0}; bool m_controlsVisible{true}; VPLayoutWeakPtr m_layout{}; int m_handleCorner{0}; diff --git a/src/app/puzzle/scene/vpmaingraphicsview.cpp b/src/app/puzzle/scene/vpmaingraphicsview.cpp index e87e090ca..b91d1cd8c 100644 --- a/src/app/puzzle/scene/vpmaingraphicsview.cpp +++ b/src/app/puzzle/scene/vpmaingraphicsview.cpp @@ -371,6 +371,7 @@ void VPMainGraphicsView::keyReleaseEvent(QKeyEvent *event) m_rotationControls->SetIgnorePieceTransformation(false); m_rotationControls->on_UpdateControls(); m_rotationControls->on_HideHandles(false); + m_rotationSum = 0; } } VMainGraphicsView::keyReleaseEvent(event); @@ -558,16 +559,32 @@ void VPMainGraphicsView::RotatePiecesByAngle(qreal angle) return pieces; }; + if (layout->LayoutSettings().GetFollowGrainline() && not origin.custom) + { + if (m_rotationSum > 90 || m_rotationSum < -90) + { + m_rotationSum = angle; + } + else + { + m_rotationSum += angle; + } + } + else + { + m_rotationSum = angle; + } + QList pieces = PreparePieces(); if (pieces.size() == 1) { - auto *command = new VPUndoPieceRotate(pieces.first(), origin.origin, angle, m_allowChangeMerge); + auto *command = new VPUndoPieceRotate(pieces.first(), origin, angle, m_rotationSum, m_allowChangeMerge); layout->UndoStack()->push(command); } else if (pieces.size() > 1) { - auto *command = new VPUndoPiecesRotate(pieces, origin.origin, angle, m_allowChangeMerge); + auto *command = new VPUndoPiecesRotate(pieces, origin, angle, m_rotationSum, m_allowChangeMerge); layout->UndoStack()->push(command); } diff --git a/src/app/puzzle/scene/vpmaingraphicsview.h b/src/app/puzzle/scene/vpmaingraphicsview.h index a2d8896f9..96ef3813e 100644 --- a/src/app/puzzle/scene/vpmaingraphicsview.h +++ b/src/app/puzzle/scene/vpmaingraphicsview.h @@ -124,6 +124,8 @@ private: bool m_showGridTmp{false}; bool m_allowChangeMerge{false}; + qreal m_rotationSum{0}; + void ConnectPiece(VPGraphicsPiece *piece); void RotatePiecesByAngle(qreal angle); diff --git a/src/app/puzzle/undocommands/vpundomovepieceonsheet.cpp b/src/app/puzzle/undocommands/vpundomovepieceonsheet.cpp index fa277a1dd..f2f356694 100644 --- a/src/app/puzzle/undocommands/vpundomovepieceonsheet.cpp +++ b/src/app/puzzle/undocommands/vpundomovepieceonsheet.cpp @@ -41,6 +41,12 @@ VPUndoMovePieceOnSheet::VPUndoMovePieceOnSheet(const VPSheetPtr &sheet, const VP m_oldSheet = piece->Sheet(); + VPLayoutPtr layout = piece->Layout(); + if (not layout.isNull()) + { + m_followGrainline = layout->LayoutSettings().GetFollowGrainline(); + } + setText(tr("move piece on sheet")); } @@ -105,6 +111,14 @@ void VPUndoMovePieceOnSheet::redo() { piece->SetSheet(sourceSheet); + if (m_followGrainline) + { + VPTransformationOrigon origin; + origin.custom = true; + + piece->RotateToGrainline(origin); + } + if (not layout.isNull()) { emit layout->PieceSheetChanged(piece); diff --git a/src/app/puzzle/undocommands/vpundomovepieceonsheet.h b/src/app/puzzle/undocommands/vpundomovepieceonsheet.h index a80173c15..7ec924f26 100644 --- a/src/app/puzzle/undocommands/vpundomovepieceonsheet.h +++ b/src/app/puzzle/undocommands/vpundomovepieceonsheet.h @@ -48,6 +48,7 @@ private: VPSheetWeakPtr m_oldSheet{}; VPSheetWeakPtr m_sheet; VPPieceWeakPtr m_piece; + bool m_followGrainline{false}; }; #endif // VPUNDOMOVEPIECEONSHEET_H diff --git a/src/app/puzzle/undocommands/vpundopiecerotate.cpp b/src/app/puzzle/undocommands/vpundopiecerotate.cpp index a264b70c6..227e1ec37 100644 --- a/src/app/puzzle/undocommands/vpundopiecerotate.cpp +++ b/src/app/puzzle/undocommands/vpundopiecerotate.cpp @@ -29,18 +29,35 @@ #include "../layout/vppiece.h" #include "../layout/vplayout.h" +namespace +{ +auto RoundAngle(qreal angle) -> qreal +{ + QLineF l(10, 10, 100, 10); + l.setAngle(angle); + return l.angle(); +} +} + //--------------------------------------------------------------------------------------------------------------------- -VPUndoPieceRotate::VPUndoPieceRotate(const VPPiecePtr &piece, const QPointF &origin, qreal angle, bool allowMerge, - QUndoCommand *parent) +VPUndoPieceRotate::VPUndoPieceRotate(const VPPiecePtr &piece, const VPTransformationOrigon &origin, qreal angle, + qreal angleSum, bool allowMerge, QUndoCommand *parent) : VPUndoCommand(allowMerge, parent), m_piece(piece), m_origin(origin), - m_angle(angle) + m_angle(angle), + m_angleSum(angleSum) { SCASSERT(not piece.isNull()) m_oldTransform = piece->GetMatrix(); + VPLayoutPtr layout = piece->Layout(); + if (not layout.isNull()) + { + m_followGrainline = layout->LayoutSettings().GetFollowGrainline(); + } + setText(tr("rotate piece")); } @@ -88,8 +105,33 @@ void VPUndoPieceRotate::redo() layout->SetFocusedSheet(piece->Sheet()); } - piece->Rotate(m_origin, m_angle); + if (m_firstCall) + { + if (m_followGrainline && piece->IsGrainlineEnabled()) + { + piece->Rotate(m_origin.origin, m_angleSum); + } + else + { + piece->Rotate(m_origin.origin, m_angle); + } + } + else + { + piece->Rotate(m_origin.origin, m_angle); + } + + if (m_followGrainline) + { + piece->RotateToGrainline(m_origin); + } + emit layout->PieceTransformationChanged(piece); + + if (m_firstCall) + { + m_firstCall = false; + } } //--------------------------------------------------------------------------------------------------------------------- @@ -105,12 +147,14 @@ auto VPUndoPieceRotate::mergeWith(const QUndoCommand *command) -> bool VPPiecePtr piece = Piece(); if (not moveCommand->AllowMerge() || (moveCommand->Piece().isNull() || piece.isNull()) || - moveCommand->Piece() != piece || moveCommand->Origin() != m_origin) + moveCommand->Piece() != piece || moveCommand->Origin() != m_origin || + moveCommand->FollowGrainline() != m_followGrainline) { return false; } m_angle += moveCommand->Angle(); + m_angle = RoundAngle(m_angle); return true; } @@ -122,11 +166,12 @@ auto VPUndoPieceRotate::id() const -> int // rotate pieces //--------------------------------------------------------------------------------------------------------------------- -VPUndoPiecesRotate::VPUndoPiecesRotate(const QList &pieces, const QPointF &origin, qreal angle, - bool allowMerge, QUndoCommand *parent) +VPUndoPiecesRotate::VPUndoPiecesRotate(const QList &pieces, const VPTransformationOrigon &origin, + qreal angle, qreal angleSum, bool allowMerge, QUndoCommand *parent) : VPUndoCommand(allowMerge, parent), m_origin(origin), - m_angle(angle) + m_angle(angle), + m_angleSum(angleSum) { setText(QObject::tr("rotate pieces")); @@ -138,6 +183,12 @@ VPUndoPiecesRotate::VPUndoPiecesRotate(const QList &pieces, const QP m_oldTransforms.insert(piece->GetUniqueID(), piece->GetMatrix()); } } + + VPLayoutPtr layout = Layout(); + if (not layout.isNull()) + { + m_followGrainline = layout->LayoutSettings().GetFollowGrainline(); + } } //--------------------------------------------------------------------------------------------------------------------- @@ -199,10 +250,35 @@ void VPUndoPiecesRotate::redo() VPPiecePtr p = piece.toStrongRef(); if (not p.isNull()) { - p->Rotate(m_origin, m_angle); + if (m_firstCall) + { + if (m_followGrainline && p->IsGrainlineEnabled()) + { + p->Rotate(m_origin.origin, m_angleSum); + } + else + { + p->Rotate(m_origin.origin, m_angle); + } + } + else + { + p->Rotate(m_origin.origin, m_angle); + } + + if (m_followGrainline) + { + p->RotateToGrainline(m_origin); + } + emit layout->PieceTransformationChanged(p); } } + + if (m_firstCall) + { + m_firstCall = false; + } } //--------------------------------------------------------------------------------------------------------------------- @@ -216,12 +292,14 @@ auto VPUndoPiecesRotate::mergeWith(const QUndoCommand *command) -> bool const auto *moveCommand = dynamic_cast(command); SCASSERT(moveCommand != nullptr) - if (not moveCommand->AllowMerge() || moveCommand->PieceIds() != PieceIds() || moveCommand->Origin() != m_origin) + if (not moveCommand->AllowMerge() || moveCommand->PieceIds() != PieceIds() || moveCommand->Origin() != m_origin || + moveCommand->FollowGrainline() != m_followGrainline) { return false; } m_angle += moveCommand->Angle(); + m_angle = RoundAngle(m_angle); return true; } diff --git a/src/app/puzzle/undocommands/vpundopiecerotate.h b/src/app/puzzle/undocommands/vpundopiecerotate.h index c9f5ab25e..769862cad 100644 --- a/src/app/puzzle/undocommands/vpundopiecerotate.h +++ b/src/app/puzzle/undocommands/vpundopiecerotate.h @@ -38,8 +38,8 @@ class VPUndoPieceRotate : public VPUndoCommand { Q_OBJECT public: - VPUndoPieceRotate(const VPPiecePtr &piece, const QPointF &origin, qreal angle, bool allowMerge = false, - QUndoCommand *parent = nullptr); + VPUndoPieceRotate(const VPPiecePtr &piece, const VPTransformationOrigon &origin, qreal angle, qreal angleSum, + bool allowMerge = false, QUndoCommand *parent = nullptr); virtual ~VPUndoPieceRotate()=default; @@ -50,16 +50,21 @@ public: virtual auto id() const -> int override ; auto Piece() const -> VPPiecePtr; - auto Origin() const -> QPointF; + auto Origin() const -> VPTransformationOrigon; auto Angle() const -> qreal; + bool FollowGrainline() const; + private: Q_DISABLE_COPY(VPUndoPieceRotate) + bool m_firstCall{true}; VPPieceWeakPtr m_piece; QTransform m_oldTransform{}; - QPointF m_origin; + VPTransformationOrigon m_origin; qreal m_angle; + qreal m_angleSum; + bool m_followGrainline{false}; }; //--------------------------------------------------------------------------------------------------------------------- @@ -69,7 +74,7 @@ inline auto VPUndoPieceRotate::Piece() const -> VPPiecePtr } //--------------------------------------------------------------------------------------------------------------------- -inline auto VPUndoPieceRotate::Origin() const -> QPointF +inline auto VPUndoPieceRotate::Origin() const -> VPTransformationOrigon { return m_origin; } @@ -80,13 +85,19 @@ inline auto VPUndoPieceRotate::Angle() const -> qreal return m_angle; } +//--------------------------------------------------------------------------------------------------------------------- +inline auto VPUndoPieceRotate::FollowGrainline() const -> bool +{ + return m_followGrainline; +} + // Rotate pieces class VPUndoPiecesRotate : public VPUndoCommand { Q_OBJECT public: - explicit VPUndoPiecesRotate(const QList &pieces, const QPointF &origin, qreal angle, - bool allowMerge = false, QUndoCommand *parent = nullptr); + explicit VPUndoPiecesRotate(const QList &pieces, const VPTransformationOrigon &origin, qreal angle, + qreal angleSum, bool allowMerge = false, QUndoCommand *parent = nullptr); virtual ~VPUndoPiecesRotate()=default; virtual void undo() override; @@ -96,23 +107,27 @@ public: virtual auto id() const -> int override ; auto PieceIds() const -> QSet; - auto Origin() const -> QPointF; + auto Origin() const -> VPTransformationOrigon; auto Angle() const -> qreal; + auto FollowGrainline() const -> bool; private: Q_DISABLE_COPY(VPUndoPiecesRotate) + bool m_firstCall{true}; QVector m_pieces{}; QMap m_oldTransforms{}; - QPointF m_origin; + VPTransformationOrigon m_origin; qreal m_angle; + qreal m_angleSum; + bool m_followGrainline{false}; auto Layout() const -> VPLayoutPtr; auto Sheet() const -> VPSheetPtr; }; //--------------------------------------------------------------------------------------------------------------------- -inline auto VPUndoPiecesRotate::Origin() const -> QPointF +inline auto VPUndoPiecesRotate::Origin() const -> VPTransformationOrigon { return m_origin; } @@ -123,4 +138,10 @@ inline auto VPUndoPiecesRotate::Angle() const -> qreal return m_angle; } +//--------------------------------------------------------------------------------------------------------------------- +inline auto VPUndoPiecesRotate::FollowGrainline() const -> bool +{ + return m_followGrainline; +} + #endif // VPUNDOPIECEROTATE_H diff --git a/src/app/puzzle/vpmainwindow.cpp b/src/app/puzzle/vpmainwindow.cpp index 884f91974..e2ebe001b 100644 --- a/src/app/puzzle/vpmainwindow.cpp +++ b/src/app/puzzle/vpmainwindow.cpp @@ -862,8 +862,13 @@ void VPMainWindow::InitPropertyTabLayout() if (not m_layout.isNull()) { m_layout->LayoutSettings().SetFollowGrainline(checked); + + if (checked) + { + RotatePiecesToGrainline(); + } + LayoutWasSaved(false); - // TODO update the QGraphicView } }); @@ -1742,6 +1747,11 @@ void VPMainWindow::SheetPaperSizeChanged() ui->toolButtonSheetLandscapeOrientation->blockSignals(true); ui->toolButtonSheetLandscapeOrientation->setChecked(not portrait); ui->toolButtonSheetLandscapeOrientation->blockSignals(false); + + if (not m_layout.isNull() && m_layout->LayoutSettings().GetFollowGrainline()) + { + RotatePiecesToGrainline(); + } } //--------------------------------------------------------------------------------------------------------------------- @@ -1862,6 +1872,29 @@ void VPMainWindow::CorrectMaxMargins() CorrectTileMaxMargins(); } +//--------------------------------------------------------------------------------------------------------------------- +void VPMainWindow::RotatePiecesToGrainline() +{ + QList sheets = m_layout->GetSheets(); + for(const auto& sheet : sheets) + { + if (not sheet.isNull()) + { + QList pieces = sheet->GetPieces(); + for(const auto& piece : pieces) + { + if (not piece.isNull() && piece->IsGrainlineEnabled()) + { + VPTransformationOrigon origin; + origin.custom = true; + piece->RotateToGrainline(origin); + emit m_layout->PieceTransformationChanged(piece); + } + } + } + } +} + //--------------------------------------------------------------------------------------------------------------------- void VPMainWindow::on_actionNew_triggered() { @@ -2513,7 +2546,12 @@ void VPMainWindow::on_ApplyPieceTransformation() if (not piece.isNull()) { const QRectF rect = piece->MappedDetailBoundingRect(); - auto *command = new VPUndoPieceRotate(piece, rect.center(), angle); + + VPTransformationOrigon origin; + origin.origin = rect.center(); + origin.custom = true; + + auto *command = new VPUndoPieceRotate(piece, origin, angle, angle); m_layout->UndoStack()->push(command); } } @@ -2528,7 +2566,7 @@ void VPMainWindow::on_ApplyPieceTransformation() } VPTransformationOrigon origin = sheet->TransformationOrigin(); - auto *command = new VPUndoPiecesRotate(selectedPieces, origin.origin, angle); + auto *command = new VPUndoPiecesRotate(selectedPieces, origin, angle, angle); m_layout->UndoStack()->push(command); } } diff --git a/src/app/puzzle/vpmainwindow.h b/src/app/puzzle/vpmainwindow.h index 731916e9f..730bd3634 100644 --- a/src/app/puzzle/vpmainwindow.h +++ b/src/app/puzzle/vpmainwindow.h @@ -454,6 +454,8 @@ private: void CorrectTileMaxMargins(); void CorrectSheetMaxMargins(); void CorrectMaxMargins(); + + void RotatePiecesToGrainline(); }; #endif // VPMAINWINDOW_H diff --git a/src/app/puzzle/vpmainwindow.ui b/src/app/puzzle/vpmainwindow.ui index 4e8403308..9ca639200 100644 --- a/src/app/puzzle/vpmainwindow.ui +++ b/src/app/puzzle/vpmainwindow.ui @@ -189,7 +189,7 @@ QTabWidget::Rounded - 0 + 3 diff --git a/src/app/puzzle/xml/vplayoutfilereader.cpp b/src/app/puzzle/xml/vplayoutfilereader.cpp index a36e7ea3a..add586b6e 100644 --- a/src/app/puzzle/xml/vplayoutfilereader.cpp +++ b/src/app/puzzle/xml/vplayoutfilereader.cpp @@ -298,7 +298,7 @@ void VPLayoutFileReader::ReadControl(const VPLayoutPtr &layout) layout->LayoutSettings().SetWarningPiecesOutOfBound(ReadAttributeBool(attribs, ML::AttrWarningOutOfBound, trueStr)); layout->LayoutSettings().SetStickyEdges(ReadAttributeBool(attribs, ML::AttrStickyEdges, trueStr)); layout->LayoutSettings().SetPiecesGap(ReadAttributeDouble(attribs, ML::AttrPiecesGap, QChar('0'))); -// layout->LayoutSettings().SetFollowGrainline(ReadAttributeBool(attribs, ML::AttrFollowGrainLine, trueStr)); + layout->LayoutSettings().SetFollowGrainline(ReadAttributeBool(attribs, ML::AttrFollowGrainline, falseStr)); readElementText(); } diff --git a/src/app/puzzle/xml/vplayoutfilewriter.cpp b/src/app/puzzle/xml/vplayoutfilewriter.cpp index d6a2d7ee4..8a2adb202 100644 --- a/src/app/puzzle/xml/vplayoutfilewriter.cpp +++ b/src/app/puzzle/xml/vplayoutfilewriter.cpp @@ -176,7 +176,7 @@ void VPLayoutFileWriter::WriteProperties(const VPLayoutPtr &layout) SetAttribute(ML::AttrWarningOutOfBound, layout->LayoutSettings().GetWarningPiecesOutOfBound()); SetAttribute(ML::AttrStickyEdges, layout->LayoutSettings().GetStickyEdges()); SetAttribute(ML::AttrPiecesGap, layout->LayoutSettings().GetPiecesGap()); -// SetAttribute(ML::AttrFollowGrainLine, layout->LayoutSettings().GetFollowGrainline()); + SetAttribute(ML::AttrFollowGrainline, layout->LayoutSettings().GetFollowGrainline()); writeEndElement(); // control WriteTiles(layout); diff --git a/src/app/puzzle/xml/vplayoutliterals.cpp b/src/app/puzzle/xml/vplayoutliterals.cpp index 2be7cdcc4..959f0940a 100644 --- a/src/app/puzzle/xml/vplayoutliterals.cpp +++ b/src/app/puzzle/xml/vplayoutliterals.cpp @@ -73,7 +73,7 @@ const QString AttrRight = QStringLiteral("right"); const QString AttrBottom = QStringLiteral("bottom"); const QString AttrWidth = QStringLiteral("width"); const QString AttrLength = QStringLiteral("length"); -const QString AttrFollowGrainLine = QStringLiteral("followGrainLine"); +const QString AttrFollowGrainline = QStringLiteral("followGrainline"); const QString AttrID = QStringLiteral("id"); const QString AttrMirrored = QStringLiteral("mirrored"); const QString AttrTransform = QStringLiteral("transform"); diff --git a/src/app/puzzle/xml/vplayoutliterals.h b/src/app/puzzle/xml/vplayoutliterals.h index 3aba6adc6..ab134e3b5 100644 --- a/src/app/puzzle/xml/vplayoutliterals.h +++ b/src/app/puzzle/xml/vplayoutliterals.h @@ -78,7 +78,7 @@ extern const QString AttrRight; extern const QString AttrBottom; extern const QString AttrWidth; extern const QString AttrLength; -extern const QString AttrFollowGrainLine; +extern const QString AttrFollowGrainline; extern const QString AttrID; extern const QString AttrMirrored; extern const QString AttrTransform; diff --git a/src/libs/ifc/schema/layout/v0.1.0.xsd b/src/libs/ifc/schema/layout/v0.1.0.xsd index a2f24cd77..b7ce92caf 100644 --- a/src/libs/ifc/schema/layout/v0.1.0.xsd +++ b/src/libs/ifc/schema/layout/v0.1.0.xsd @@ -27,6 +27,7 @@ + diff --git a/src/libs/vlayout/vlayoutpiece_p.h b/src/libs/vlayout/vlayoutpiece_p.h index f3e796dfe..687b6e9f4 100644 --- a/src/libs/vlayout/vlayoutpiece_p.h +++ b/src/libs/vlayout/vlayoutpiece_p.h @@ -117,7 +117,7 @@ public: /** @brief grainlineInfo line */ QVector grainlinePoints{}; - GrainlineArrowDirection grainlineArrowType{GrainlineArrowDirection::atFront}; + GrainlineArrowDirection grainlineArrowType{GrainlineArrowDirection::atFront}; qreal grainlineAngle{0}; bool grainlineEnabled{false};