From e9565b3e758b43e31f7155a4161cb544aca5c4a1 Mon Sep 17 00:00:00 2001 From: Roman Telezhynskyi Date: Wed, 29 Nov 2023 16:40:36 +0200 Subject: [PATCH] Horizontal piece flipping. --- ChangeLog.txt | 1 + src/app/puzzle/layout/vppiece.cpp | 25 +++++- src/app/puzzle/layout/vppiece.h | 5 +- src/app/puzzle/scene/vpgraphicspiece.cpp | 97 +++++++++++++++++++-- src/app/puzzle/vpmainwindow.cpp | 30 +++++-- src/app/puzzle/vpmainwindow.ui | 21 +++-- src/app/puzzle/xml/vplayoutfilereader.cpp | 3 +- src/app/puzzle/xml/vplayoutfilewriter.cpp | 6 +- src/app/puzzle/xml/vplayoutliterals.cpp | 3 +- src/app/puzzle/xml/vplayoutliterals.h | 3 +- src/libs/ifc/schema/layout/v0.1.7.xsd | 6 +- src/libs/ifc/xml/vlayoutconverter.cpp | 51 +++++++---- src/libs/ifc/xml/vlayoutconverter.h | 2 + src/libs/vhpgl/vhpglengine.cpp | 100 ++++++++++++++++++++-- src/libs/vlayout/vlayoutpaper.cpp | 2 +- src/libs/vlayout/vlayoutpiece.cpp | 34 +++++--- src/libs/vlayout/vlayoutpiece.h | 7 +- src/libs/vlayout/vlayoutpiece_p.h | 15 +++- src/libs/vlayout/vposition.cpp | 2 +- 19 files changed, 339 insertions(+), 74 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index 99eeff106..f25c10c12 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -59,6 +59,7 @@ - New tools: Arc start point, Arc end point. - Optimize U-notch shape. - New feature. Boundary together with notches. +- Puzzle app. Horizontal piece flipping. # Valentina 0.7.52 September 12, 2022 - Fix crash when default locale is ru. diff --git a/src/app/puzzle/layout/vppiece.cpp b/src/app/puzzle/layout/vppiece.cpp index ab3b80816..b0e7024cc 100644 --- a/src/app/puzzle/layout/vppiece.cpp +++ b/src/app/puzzle/layout/vppiece.cpp @@ -144,7 +144,7 @@ VPPiece::VPPiece(const VLayoutPiece &layoutPiece) if (IsForceFlipping()) { - Flip(); + FlipVertically(); } } @@ -200,7 +200,8 @@ void VPPiece::ClearTransformations() const QPointF offset = MappedDetailBoundingRect().topLeft(); Translate(-offset.x(), -offset.y()); - SetMirror(false); + SetVerticallyFlipped(false); + SetHorizontallyFlipped(false); } //--------------------------------------------------------------------------------------------------------------------- @@ -287,7 +288,7 @@ void VPPiece::SetGrainline(const VPieceGrainline &grainline) } //--------------------------------------------------------------------------------------------------------------------- -void VPPiece::Flip() +void VPPiece::FlipVertically() { QTransform pieceMatrix = GetMatrix(); QPointF center = pieceMatrix.map(DetailBoundingRect().center()); @@ -299,7 +300,23 @@ void VPPiece::Flip() pieceMatrix *= m; SetMatrix(pieceMatrix); - SetMirror(!IsMirror()); + SetVerticallyFlipped(!IsVerticallyFlipped()); +} + +//--------------------------------------------------------------------------------------------------------------------- +void VPPiece::FlipHorizontally() +{ + QTransform pieceMatrix = GetMatrix(); + QPointF center = pieceMatrix.map(DetailBoundingRect().center()); + + QTransform m; + m.translate(0, center.y()); + m.scale(1, -1); + m.translate(0, -center.y()); + + pieceMatrix *= m; + SetMatrix(pieceMatrix); + SetHorizontallyFlipped(!IsHorizontallyFlipped()); } //--------------------------------------------------------------------------------------------------------------------- diff --git a/src/app/puzzle/layout/vppiece.h b/src/app/puzzle/layout/vppiece.h index e7c48bfba..502edb365 100644 --- a/src/app/puzzle/layout/vppiece.h +++ b/src/app/puzzle/layout/vppiece.h @@ -97,9 +97,10 @@ public: void SetGrainline(const VPieceGrainline &grainline); /** - * @brief Flip horizontally mirror around center of bounding rect + * @brief Flip verticvally mirror around center of bounding rect */ - void Flip(); + void FlipVertically(); + void FlipHorizontally(); auto OutOfBound() const -> bool; void SetOutOfBound(bool newOutOfBound); diff --git a/src/app/puzzle/scene/vpgraphicspiece.cpp b/src/app/puzzle/scene/vpgraphicspiece.cpp index 079a98c50..c92b69fe9 100644 --- a/src/app/puzzle/scene/vpgraphicspiece.cpp +++ b/src/app/puzzle/scene/vpgraphicspiece.cpp @@ -72,7 +72,7 @@ namespace { //--------------------------------------------------------------------------------------------------------------------- inline auto LineMatrix(const VPPiecePtr &piece, const QPointF &topLeft, qreal angle, const QPointF &linePos, - int maxLineWidth) -> QTransform + int maxLineWidth, qreal maxLabelHeight) -> QTransform { if (piece.isNull()) { @@ -82,11 +82,21 @@ inline auto LineMatrix(const VPPiecePtr &piece, const QPointF &topLeft, qreal an QTransform labelMatrix; labelMatrix.translate(topLeft.x(), topLeft.y()); - if (piece->IsMirror()) + if (piece->IsVerticallyFlipped() || piece->IsHorizontallyFlipped()) { - labelMatrix.scale(-1, 1); - labelMatrix.rotate(-angle); - labelMatrix.translate(-maxLineWidth, 0); + if (piece->IsVerticallyFlipped()) + { + labelMatrix.scale(-1, 1); + labelMatrix.rotate(-angle); + labelMatrix.translate(-maxLineWidth, 0); + } + + if (piece->IsHorizontallyFlipped()) + { + labelMatrix.scale(1, -1); + labelMatrix.rotate(-angle); + labelMatrix.translate(0, -maxLabelHeight); + } } else { @@ -169,6 +179,72 @@ inline auto SelectionBrush() -> QBrush { return {QColor(255, 160, 160, 60)}; } + +//--------------------------------------------------------------------------------------------------------------------- +auto LabelHeightSVGFont(const VPPiecePtr &piece, const QVector &labelLines, const VSvgFont &svgFont, + const VSvgFontDatabase *db, qreal penWidth, qreal dH, int spacing) -> qreal +{ + qreal labelHeight = 0; + if (piece->IsHorizontallyFlipped()) + { + for (int i = 0; i < labelLines.size(); ++i) + { + const VSvgFont fnt = LineFont(labelLines.at(i), svgFont); + VSvgFontEngine engine = db->FontEngine(fnt); + + const qreal lineHeight = engine.FontHeight() + penWidth; + + if (labelHeight + lineHeight > dH) + { + break; + } + + if (i < labelLines.size() - 1) + { + labelHeight += lineHeight + spacing; + } + else + { + labelHeight += lineHeight; + } + } + } + + return labelHeight; +} + +//--------------------------------------------------------------------------------------------------------------------- +auto LabelHeightOutlineFont(const VPPiecePtr &piece, const QVector &labelLines, const QFont &font, + bool textAsPaths, qreal penWidth, qreal dH, int spacing) -> qreal +{ + qreal labelHeight = 0; + if (piece->IsHorizontallyFlipped()) + { + for (int i = 0; i < labelLines.size(); ++i) + { + const QFont fnt = LineFont(labelLines.at(i), font); + QFontMetrics fm(fnt); + + const qreal lineHeight = textAsPaths ? fm.height() + penWidth : fm.height(); + + if (labelHeight + lineHeight > dH) + { + break; + } + + if (i < labelLines.size() - 1) + { + labelHeight += lineHeight + spacing; + } + else + { + labelHeight += lineHeight; + } + } + } + + return labelHeight; +} } // namespace //--------------------------------------------------------------------------------------------------------------------- @@ -441,6 +517,8 @@ void VPGraphicsPiece::InitPieceLabelSVGFont(const QVector &labelShape, const QVector labelLines = tm.GetLabelSourceLines(qFloor(dW), svgFont, penWidth); + const qreal labelHeight = LabelHeightSVGFont(piece, labelLines, svgFont, db, penWidth, dH, tm.GetSpacing()); + for (const auto &tl : labelLines) { const VSvgFont fnt = LineFont(tl, svgFont); @@ -454,7 +532,8 @@ void VPGraphicsPiece::InitPieceLabelSVGFont(const QVector &labelShape, const QString qsText = tl.m_qsText; const qreal dX = LineAlign(tl, qsText, engine, dW, penWidth); // set up the rotation around top-left corner matrix - const QTransform lineMatrix = LineMatrix(piece, labelShape.at(0), angle, QPointF(dX, dY), maxLineWidth); + const QTransform lineMatrix = + LineMatrix(piece, labelShape.at(0), angle, QPointF(dX, dY), maxLineWidth, labelHeight); auto *item = new QGraphicsPathItem(this); item->setPath(engine.DrawPath(QPointF(), qsText)); @@ -508,6 +587,9 @@ void VPGraphicsPiece::InitPieceLabelOutlineFont(const QVector &labelSha const QVector labelLines = tm.GetLabelSourceLines(qFloor(dW), tm.GetFont()); + const qreal labelHeight = + LabelHeightOutlineFont(piece, labelLines, tm.GetFont(), textAsPaths, penWidth, dH, tm.GetSpacing()); + for (const auto &tl : labelLines) { const QFont fnt = LineFont(tl, tm.GetFont()); @@ -528,7 +610,8 @@ void VPGraphicsPiece::InitPieceLabelOutlineFont(const QVector &labelSha const QString qsText = tl.m_qsText; const qreal dX = LineAlign(tl, qsText, fm, dW); // set up the rotation around top-left corner matrix - const QTransform lineMatrix = LineMatrix(piece, labelShape.at(0), angle, QPointF(dX, dY), maxLineWidth); + const QTransform lineMatrix = + LineMatrix(piece, labelShape.at(0), angle, QPointF(dX, dY), maxLineWidth, labelHeight); if (textAsPaths) { diff --git a/src/app/puzzle/vpmainwindow.cpp b/src/app/puzzle/vpmainwindow.cpp index cd4c582f8..0100f5d76 100644 --- a/src/app/puzzle/vpmainwindow.cpp +++ b/src/app/puzzle/vpmainwindow.cpp @@ -782,7 +782,7 @@ void VPMainWindow::InitPropertyTabCurrentPiece() } }); - connect(ui->checkBoxCurrentPieceMirrorPiece, &QCheckBox::toggled, this, + connect(ui->checkBoxCurrentPieceVerticallyFlipped, &QCheckBox::toggled, this, [this](bool checked) { QList selectedPieces = SelectedPieces(); @@ -791,9 +791,28 @@ void VPMainWindow::InitPropertyTabCurrentPiece() const VPPiecePtr &selectedPiece = selectedPieces.constFirst(); if (not selectedPiece.isNull()) { - if (selectedPiece->IsMirror() != checked) + if (selectedPiece->IsVerticallyFlipped() != checked) { - selectedPiece->Flip(); + selectedPiece->FlipVertically(); + LayoutWasSaved(false); + emit m_layout->PieceTransformationChanged(selectedPiece); + } + } + } + }); + + connect(ui->checkBoxCurrentPieceHorizontallyFlipped, &QCheckBox::toggled, this, + [this](bool checked) + { + QList selectedPieces = SelectedPieces(); + if (selectedPieces.size() == 1) + { + const VPPiecePtr &selectedPiece = selectedPieces.constFirst(); + if (not selectedPiece.isNull()) + { + if (selectedPiece->IsHorizontallyFlipped() != checked) + { + selectedPiece->FlipHorizontally(); LayoutWasSaved(false); emit m_layout->PieceTransformationChanged(selectedPiece); } @@ -1287,10 +1306,11 @@ void VPMainWindow::SetPropertyTabCurrentPieceData() SetLineEditValue(ui->lineEditCurrentPieceGradationId, selectedPiece->GetGradationId()); SetCheckBoxValue(ui->checkBoxCurrentPieceShowSeamline, not selectedPiece->IsHideMainPath()); - SetCheckBoxValue(ui->checkBoxCurrentPieceMirrorPiece, selectedPiece->IsMirror()); + SetCheckBoxValue(ui->checkBoxCurrentPieceVerticallyFlipped, selectedPiece->IsVerticallyFlipped()); + SetCheckBoxValue(ui->checkBoxCurrentPieceHorizontallyFlipped, selectedPiece->IsHorizontallyFlipped()); const bool disableFlipping = selectedPiece->IsForbidFlipping() || selectedPiece->IsForceFlipping(); - ui->checkBoxCurrentPieceMirrorPiece->setDisabled(disableFlipping); + ui->checkBoxCurrentPieceVerticallyFlipped->setDisabled(disableFlipping); if (not ui->checkBoxRelativeTranslation->isChecked()) { diff --git a/src/app/puzzle/vpmainwindow.ui b/src/app/puzzle/vpmainwindow.ui index b6cf5c2c7..1c67f7a8c 100644 --- a/src/app/puzzle/vpmainwindow.ui +++ b/src/app/puzzle/vpmainwindow.ui @@ -279,8 +279,8 @@ 0 0 - 392 - 700 + 378 + 707 @@ -624,9 +624,16 @@ - + - Mirror piece + Vertically flipped + + + + + + + Horizontally flipped @@ -2416,7 +2423,7 @@ lineEditCurrentPieceName plainTextEditCurrentPieceUUID checkBoxCurrentPieceShowSeamline - checkBoxCurrentPieceMirrorPiece + checkBoxCurrentPieceVerticallyFlipped doubleSpinBoxCurrentPieceAngle doubleSpinBoxCurrentPieceBoxPositionX doubleSpinBoxCurrentPieceBoxPositionY @@ -2434,8 +2441,8 @@ - - + + diff --git a/src/app/puzzle/xml/vplayoutfilereader.cpp b/src/app/puzzle/xml/vplayoutfilereader.cpp index 719e8170f..5ea37ed97 100644 --- a/src/app/puzzle/xml/vplayoutfilereader.cpp +++ b/src/app/puzzle/xml/vplayoutfilereader.cpp @@ -511,7 +511,8 @@ void VPLayoutFileReader::ReadPiece(const VPPiecePtr &piece) piece->SetXScale(ReadAttributeDouble(attribs, ML::AttrXScale, QChar('1'))); piece->SetYScale(ReadAttributeDouble(attribs, ML::AttrYScale, QChar('1'))); piece->SetZValue(ReadAttributeDouble(attribs, ML::AttrZValue, QChar('1'))); - piece->SetMirror(ReadAttributeBool(attribs, ML::AttrMirrored, falseStr)); + piece->SetVerticallyFlipped(ReadAttributeBool(attribs, ML::AttrVerticallyFlipped, falseStr)); + piece->SetHorizontallyFlipped(ReadAttributeBool(attribs, ML::AttrHorizontallyFlipped, falseStr)); piece->SetForbidFlipping(ReadAttributeBool(attribs, ML::AttrForbidFlipping, falseStr)); piece->SetForceFlipping(ReadAttributeBool(attribs, ML::AttrForceFlipping, falseStr)); piece->SetFollowGrainline(ReadAttributeBool(attribs, ML::AttrFollowGrainline, falseStr)); diff --git a/src/app/puzzle/xml/vplayoutfilewriter.cpp b/src/app/puzzle/xml/vplayoutfilewriter.cpp index 7feb206ee..5404039c5 100644 --- a/src/app/puzzle/xml/vplayoutfilewriter.cpp +++ b/src/app/puzzle/xml/vplayoutfilewriter.cpp @@ -260,8 +260,10 @@ void VPLayoutFileWriter::WritePiece(const VPPiecePtr &piece) writeStartElement(ML::TagPiece); SetAttribute(ML::AttrUID, piece->GetUUID().toString()); SetAttribute(ML::AttrName, piece->GetName()); - SetAttributeOrRemoveIf(ML::AttrMirrored, piece->IsMirror(), - [](bool mirrored) noexcept { return not mirrored; }); + SetAttributeOrRemoveIf(ML::AttrVerticallyFlipped, piece->IsVerticallyFlipped(), + [](bool flipped) noexcept { return not flipped; }); + SetAttributeOrRemoveIf(ML::AttrHorizontallyFlipped, piece->IsHorizontallyFlipped(), + [](bool flipped) noexcept { return not flipped; }); SetAttributeOrRemoveIf(ML::AttrForbidFlipping, piece->IsForbidFlipping(), [](bool forbid) noexcept { return not forbid; }); SetAttributeOrRemoveIf(ML::AttrForceFlipping, piece->IsForceFlipping(), diff --git a/src/app/puzzle/xml/vplayoutliterals.cpp b/src/app/puzzle/xml/vplayoutliterals.cpp index e72a34917..e2587551d 100644 --- a/src/app/puzzle/xml/vplayoutliterals.cpp +++ b/src/app/puzzle/xml/vplayoutliterals.cpp @@ -84,7 +84,8 @@ const QString AttrLength = QStringLiteral("length"); const QString AttrFollowGrainline = QStringLiteral("followGrainline"); // NOLINT(cert-err58-cpp) const QString AttrBoundaryTogetherWithNotches = QStringLiteral("boundaryTogetherWithNotches"); // NOLINT(cert-err58-cpp) const QString AttrUID = QStringLiteral("uid"); // NOLINT(cert-err58-cpp) -const QString AttrMirrored = QStringLiteral("mirrored"); // NOLINT(cert-err58-cpp) +const QString AttrVerticallyFlipped = QStringLiteral("verticallyFlipped"); // NOLINT(cert-err58-cpp) +const QString AttrHorizontallyFlipped = QStringLiteral("horizontallyFlipped"); // NOLINT(cert-err58-cpp) const QString AttrForbidFlipping = QStringLiteral("forbidFlipping"); // NOLINT(cert-err58-cpp) const QString AttrForceFlipping = QStringLiteral("forceFlipping"); // NOLINT(cert-err58-cpp) const QString AttrSewLineOnDrawing = QStringLiteral("sewLineOnDrawing"); // NOLINT(cert-err58-cpp) diff --git a/src/app/puzzle/xml/vplayoutliterals.h b/src/app/puzzle/xml/vplayoutliterals.h index 6b0691a0d..f8aa21c16 100644 --- a/src/app/puzzle/xml/vplayoutliterals.h +++ b/src/app/puzzle/xml/vplayoutliterals.h @@ -83,7 +83,8 @@ extern const QString AttrLength; extern const QString AttrFollowGrainline; extern const QString AttrBoundaryTogetherWithNotches; extern const QString AttrUID; -extern const QString AttrMirrored; +extern const QString AttrVerticallyFlipped; +extern const QString AttrHorizontallyFlipped; extern const QString AttrForbidFlipping; extern const QString AttrForceFlipping; extern const QString AttrSewLineOnDrawing; diff --git a/src/libs/ifc/schema/layout/v0.1.7.xsd b/src/libs/ifc/schema/layout/v0.1.7.xsd index 1652d8b54..740c98e40 100644 --- a/src/libs/ifc/schema/layout/v0.1.7.xsd +++ b/src/libs/ifc/schema/layout/v0.1.7.xsd @@ -221,7 +221,8 @@ - + + @@ -419,7 +420,8 @@ - + + diff --git a/src/libs/ifc/xml/vlayoutconverter.cpp b/src/libs/ifc/xml/vlayoutconverter.cpp index 045967f56..b29282e1c 100644 --- a/src/libs/ifc/xml/vlayoutconverter.cpp +++ b/src/libs/ifc/xml/vlayoutconverter.cpp @@ -58,21 +58,23 @@ QT_WARNING_DISABLE_CLANG("-Wunused-member-function") // The list of all string we use for conversion // Better to use global variables because repeating QStringLiteral blows up code size -Q_GLOBAL_STATIC_WITH_ARGS(const QString, strSeamLineTag, ("seamLine"_L1)) // NOLINT -Q_GLOBAL_STATIC_WITH_ARGS(const QString, strSeamAllowanceTag, ("seamAllowance"_L1)) // NOLINT -Q_GLOBAL_STATIC_WITH_ARGS(const QString, strInternalPathTag, ("internalPath"_L1)) // NOLINT -Q_GLOBAL_STATIC_WITH_ARGS(const QString, strMarkerTag, ("marker"_L1)) // NOLINT -Q_GLOBAL_STATIC_WITH_ARGS(const QString, strPointTag, ("point"_L1)) // NOLINT -Q_GLOBAL_STATIC_WITH_ARGS(const QString, strPieceTag, ("piece"_L1)) // NOLINT -Q_GLOBAL_STATIC_WITH_ARGS(const QString, strGrainlineTag, ("grainline"_L1)) // NOLINT -Q_GLOBAL_STATIC_WITH_ARGS(const QString, strAttrX, ("x"_L1)) // NOLINT -Q_GLOBAL_STATIC_WITH_ARGS(const QString, strAttrY, ("y"_L1)) // NOLINT -Q_GLOBAL_STATIC_WITH_ARGS(const QString, strAttrTurnPoint, ("turnPoint"_L1)) // NOLINT -Q_GLOBAL_STATIC_WITH_ARGS(const QString, strAttrCurvePoint, ("curvePoint"_L1)) // NOLINT -Q_GLOBAL_STATIC_WITH_ARGS(const QString, strAttrId, ("id"_L1)) // NOLINT -Q_GLOBAL_STATIC_WITH_ARGS(const QString, strAttrUId, ("uid"_L1)) // NOLINT -Q_GLOBAL_STATIC_WITH_ARGS(const QString, strAttrAngle, ("angle"_L1)) // NOLINT -Q_GLOBAL_STATIC_WITH_ARGS(const QString, strAttrArrowDirection, ("arrowDirection"_L1)) // NOLINT +Q_GLOBAL_STATIC_WITH_ARGS(const QString, strSeamLineTag, ("seamLine"_L1)) // NOLINT +Q_GLOBAL_STATIC_WITH_ARGS(const QString, strSeamAllowanceTag, ("seamAllowance"_L1)) // NOLINT +Q_GLOBAL_STATIC_WITH_ARGS(const QString, strInternalPathTag, ("internalPath"_L1)) // NOLINT +Q_GLOBAL_STATIC_WITH_ARGS(const QString, strMarkerTag, ("marker"_L1)) // NOLINT +Q_GLOBAL_STATIC_WITH_ARGS(const QString, strPointTag, ("point"_L1)) // NOLINT +Q_GLOBAL_STATIC_WITH_ARGS(const QString, strPieceTag, ("piece"_L1)) // NOLINT +Q_GLOBAL_STATIC_WITH_ARGS(const QString, strGrainlineTag, ("grainline"_L1)) // NOLINT +Q_GLOBAL_STATIC_WITH_ARGS(const QString, strAttrX, ("x"_L1)) // NOLINT +Q_GLOBAL_STATIC_WITH_ARGS(const QString, strAttrY, ("y"_L1)) // NOLINT +Q_GLOBAL_STATIC_WITH_ARGS(const QString, strAttrTurnPoint, ("turnPoint"_L1)) // NOLINT +Q_GLOBAL_STATIC_WITH_ARGS(const QString, strAttrCurvePoint, ("curvePoint"_L1)) // NOLINT +Q_GLOBAL_STATIC_WITH_ARGS(const QString, strAttrId, ("id"_L1)) // NOLINT +Q_GLOBAL_STATIC_WITH_ARGS(const QString, strAttrUId, ("uid"_L1)) // NOLINT +Q_GLOBAL_STATIC_WITH_ARGS(const QString, strAttrAngle, ("angle"_L1)) // NOLINT +Q_GLOBAL_STATIC_WITH_ARGS(const QString, strAttrArrowDirection, ("arrowDirection"_L1)) // NOLINT +Q_GLOBAL_STATIC_WITH_ARGS(const QString, strAttrMirrored, ("mirrored"_L1)) // NOLINT +Q_GLOBAL_STATIC_WITH_ARGS(const QString, strAttrVerticallyFlipped, ("verticallyFlipped"_L1)) // NOLINT QT_WARNING_POP @@ -380,6 +382,24 @@ void VLayoutConverter::ConvertPiecesToV0_1_5() } } +//--------------------------------------------------------------------------------------------------------------------- +void VLayoutConverter::ConvertPiecesToV0_1_7() +{ + // TODO. Delete if minimal supported version is 0.1.7 + Q_STATIC_ASSERT_X(VLayoutConverter::LayoutMinVer < FormatVersion(0, 1, 7), "Time to refactor the code."); + + QDomNodeList pieceTags = elementsByTagName(*strPieceTag); + for (int i = 0; i < pieceTags.size(); ++i) + { + QDomElement node = pieceTags.at(i).toElement(); + if (node.isElement() && node.hasAttribute(*strAttrMirrored)) + { + node.setAttribute(*strAttrVerticallyFlipped, node.attribute(*strAttrMirrored)); + node.removeAttribute(*strAttrMirrored); + } + } +} + //--------------------------------------------------------------------------------------------------------------------- void VLayoutConverter::ToV0_1_3() { @@ -407,6 +427,7 @@ void VLayoutConverter::ToV0_1_7() // TODO. Delete if minimal supported version is 0.1.7 Q_STATIC_ASSERT_X(VLayoutConverter::LayoutMinVer < FormatVersion(0, 1, 7), "Time to refactor the code."); + ConvertPiecesToV0_1_7(); SetVersion(QStringLiteral("0.1.7")); Save(); } diff --git a/src/libs/ifc/xml/vlayoutconverter.h b/src/libs/ifc/xml/vlayoutconverter.h index bc96ba530..0f429239a 100644 --- a/src/libs/ifc/xml/vlayoutconverter.h +++ b/src/libs/ifc/xml/vlayoutconverter.h @@ -72,6 +72,8 @@ protected: void ConvertPiecesToV0_1_5(); + void ConvertPiecesToV0_1_7(); + void ToV0_1_3(); void ToV0_1_5(); void ToV0_1_7(); diff --git a/src/libs/vhpgl/vhpglengine.cpp b/src/libs/vhpgl/vhpglengine.cpp index f7ab713b7..a1adb1d45 100644 --- a/src/libs/vhpgl/vhpglengine.cpp +++ b/src/libs/vhpgl/vhpglengine.cpp @@ -162,16 +162,26 @@ inline auto LineAlign(const TextLine &tl, const QString &text, const QFontMetric //--------------------------------------------------------------------------------------------------------------------- auto LineMatrix(const VLayoutPiece &piece, const QPointF &topLeft, qreal angle, const QPointF &linePos, - int maxLineWidth) -> QTransform + int maxLineWidth, qreal maxLabelHeight) -> QTransform { QTransform labelMatrix; labelMatrix.translate(topLeft.x(), topLeft.y()); - if (piece.IsMirror()) + if (piece.IsVerticallyFlipped() || piece.IsHorizontallyFlipped()) { - labelMatrix.scale(-1, 1); - labelMatrix.rotate(-angle); - labelMatrix.translate(-maxLineWidth, 0); + if (piece.IsVerticallyFlipped()) + { + labelMatrix.scale(-1, 1); + labelMatrix.rotate(-angle); + labelMatrix.translate(-maxLineWidth, 0); + } + + if (piece.IsHorizontallyFlipped()) + { + labelMatrix.scale(1, -1); + labelMatrix.rotate(-angle); + labelMatrix.translate(0, -maxLabelHeight); + } } else { @@ -220,6 +230,72 @@ auto NextPattern(int patternIndex, const QVector &pattern) -> int { return (patternIndex + 2) % static_cast(pattern.size()); } + +//--------------------------------------------------------------------------------------------------------------------- +auto LabelHeightSVGFont(const VLayoutPiece &detail, const QVector &labelLines, const VSvgFont &svgFont, + const VSvgFontDatabase *db, qreal penWidth, qreal dH, int spacing) -> qreal +{ + qreal labelHeight = 0; + if (detail.IsHorizontallyFlipped()) + { + for (int i = 0; i < labelLines.size(); ++i) + { + const VSvgFont fnt = LineFont(labelLines.at(i), svgFont); + VSvgFontEngine engine = db->FontEngine(fnt); + + const qreal lineHeight = engine.FontHeight() + penWidth; + + if (labelHeight + lineHeight > dH) + { + break; + } + + if (i < labelLines.size() - 1) + { + labelHeight += lineHeight + spacing; + } + else + { + labelHeight += lineHeight; + } + } + } + + return labelHeight; +} + +//--------------------------------------------------------------------------------------------------------------------- +auto LabelHeightOutlineFont(const VLayoutPiece &detail, const QVector &labelLines, const QFont &font, + qreal penWidth, qreal dH, int spacing) -> qreal +{ + qreal labelHeight = 0; + if (detail.IsHorizontallyFlipped()) + { + for (int i = 0; i < labelLines.size(); ++i) + { + const QFont fnt = LineFont(labelLines.at(i), font); + QFontMetrics fm(fnt); + + const qreal lineHeight = fm.height() + penWidth; + + if (labelHeight + lineHeight > dH) + { + break; + } + + if (i < labelLines.size() - 1) + { + labelHeight += lineHeight + spacing; + } + else + { + labelHeight += lineHeight; + } + } + } + + return labelHeight; +} } // namespace //--------------------------------------------------------------------------------------------------------------------- @@ -475,7 +551,8 @@ void VHPGLEngine::PlotInternalPaths(QTextStream &out, const VLayoutPiece &detail QVector internalPaths = detail.GetInternalPaths(); for (const auto &path : internalPaths) { - QVector points = VLayoutPiece::MapVector(path.Points(), detail.GetMatrix(), detail.IsMirror()); + QVector points = VLayoutPiece::MapVector( + path.Points(), detail.GetMatrix(), detail.IsVerticallyFlipped() || detail.IsHorizontallyFlipped()); PlotPath(out, CastToPoint(ConvertPath(points)), path.PenStyle()); } } @@ -579,6 +656,8 @@ void VHPGLEngine::PlotLabelSVGFont(QTextStream &out, const VLayoutPiece &detail, const QVector labelLines = tm.GetLabelSourceLines(qFloor(dW), svgFont, m_penWidthPx); + const qreal labelHeight = LabelHeightSVGFont(detail, labelLines, svgFont, db, m_penWidthPx, dH, tm.GetSpacing()); + for (const auto &tl : labelLines) { const VSvgFont fnt = LineFont(tl, svgFont); @@ -592,7 +671,8 @@ void VHPGLEngine::PlotLabelSVGFont(QTextStream &out, const VLayoutPiece &detail, const QString qsText = tl.m_qsText; const qreal dX = LineAlign(tl, qsText, engine, dW, m_penWidthPx); // set up the rotation around top-left corner matrix - const QTransform lineMatrix = LineMatrix(detail, labelShape.at(0), angle, QPointF(dX, dY), maxLineWidth); + const QTransform lineMatrix = + LineMatrix(detail, labelShape.at(0), angle, QPointF(dX, dY), maxLineWidth, labelHeight); QPainterPath path = lineMatrix.map(engine.DrawPath(QPointF(), qsText)); PlotPainterPath(out, path, Qt::SolidLine); @@ -626,6 +706,9 @@ void VHPGLEngine::PlotLabelOutlineFont(QTextStream &out, const VLayoutPiece &det const QVector labelLines = tm.GetLabelSourceLines(qFloor(dW), tm.GetFont()); + const qreal labelHeight = + LabelHeightOutlineFont(detail, labelLines, tm.GetFont(), m_penWidthPx, dH, tm.GetSpacing()); + for (const auto &tl : labelLines) { const QFont fnt = LineFont(tl, tm.GetFont()); @@ -646,7 +729,8 @@ void VHPGLEngine::PlotLabelOutlineFont(QTextStream &out, const VLayoutPiece &det const QString qsText = tl.m_qsText; const qreal dX = LineAlign(tl, qsText, fm, dW); // set up the rotation around top-left corner matrix - const QTransform lineMatrix = LineMatrix(detail, labelShape.at(0), angle, QPointF(dX, dY), maxLineWidth); + const QTransform lineMatrix = + LineMatrix(detail, labelShape.at(0), angle, QPointF(dX, dY), maxLineWidth, labelHeight); QPainterPath path; diff --git a/src/libs/vlayout/vlayoutpaper.cpp b/src/libs/vlayout/vlayoutpaper.cpp index 785f5752d..890f3e957 100644 --- a/src/libs/vlayout/vlayoutpaper.cpp +++ b/src/libs/vlayout/vlayoutpaper.cpp @@ -284,7 +284,7 @@ auto VLayoutPaper::SaveResult(const VBestSquare &bestResult, const VLayoutPiece { VLayoutPiece workDetail = detail; workDetail.SetMatrix(bestResult.Matrix()); // Don't forget set matrix - workDetail.SetMirror(bestResult.Mirror()); + workDetail.SetVerticallyFlipped(bestResult.Mirror()); if (d->saveLength) { diff --git a/src/libs/vlayout/vlayoutpiece.cpp b/src/libs/vlayout/vlayoutpiece.cpp index f3e4da4f6..b65fe7b70 100644 --- a/src/libs/vlayout/vlayoutpiece.cpp +++ b/src/libs/vlayout/vlayoutpiece.cpp @@ -667,7 +667,7 @@ template <> auto VLayoutPiece::Map(QVector points) c point.ry() = p.y(); return point; }); - if (d->m_mirror) + if (d->m_verticallyFlipped || d->m_horizontallyFlipped) { std::reverse(points.begin(), points.end()); } @@ -1087,7 +1087,7 @@ void VLayoutPiece::Mirror(const QLineF &edge) m.translate(-p2.x(), -p2.y()); d->m_matrix *= m; - d->m_mirror = !d->m_mirror; + d->m_verticallyFlipped = !d->m_verticallyFlipped; } //--------------------------------------------------------------------------------------------------------------------- @@ -1096,7 +1096,7 @@ void VLayoutPiece::Mirror() QTransform m; m.scale(-1, 1); d->m_matrix *= m; - d->m_mirror = !d->m_mirror; + d->m_verticallyFlipped = !d->m_verticallyFlipped; } //--------------------------------------------------------------------------------------------------------------------- @@ -1607,7 +1607,7 @@ void VLayoutPiece::LabelStringsSVGFont(QGraphicsItem *parent, const QVectorm_mirror) + if (d->m_verticallyFlipped) { labelMatrix.scale(-1, 1); labelMatrix.rotate(-angle); @@ -1704,7 +1704,7 @@ void VLayoutPiece::LabelStringsOutlineFont(QGraphicsItem *parent, const QVector< // set up the rotation around top-left corner matrix QTransform labelMatrix; labelMatrix.translate(labelShape.at(0).x(), labelShape.at(0).y()); - if (d->m_mirror) + if (d->m_verticallyFlipped) { labelMatrix.scale(-1, 1); labelMatrix.rotate(-angle); @@ -1844,15 +1844,27 @@ auto VLayoutPiece::GetMainPathItem() const -> QGraphicsPathItem * } //--------------------------------------------------------------------------------------------------------------------- -auto VLayoutPiece::IsMirror() const -> bool +auto VLayoutPiece::IsVerticallyFlipped() const -> bool { - return d->m_mirror; + return d->m_verticallyFlipped; } //--------------------------------------------------------------------------------------------------------------------- -void VLayoutPiece::SetMirror(bool value) +void VLayoutPiece::SetVerticallyFlipped(bool value) { - d->m_mirror = value; + d->m_verticallyFlipped = value; +} + +//--------------------------------------------------------------------------------------------------------------------- +auto VLayoutPiece::IsHorizontallyFlipped() const -> bool +{ + return d->m_horizontallyFlipped; +} + +//--------------------------------------------------------------------------------------------------------------------- +void VLayoutPiece::SetHorizontallyFlipped(bool value) +{ + d->m_horizontallyFlipped = value; } //--------------------------------------------------------------------------------------------------------------------- @@ -1911,7 +1923,7 @@ auto VLayoutPiece::Edge(const QVector &path, int i) const -> QLineF i2 = 0; } - if (d->m_mirror) + if (d->m_verticallyFlipped || d->m_horizontallyFlipped) { QVector newPath = Map(path); return {newPath.at(i1), newPath.at(i2)}; @@ -1940,7 +1952,7 @@ auto VLayoutPiece::EdgeByPoint(const QVector &path, const QPointF &p1) //--------------------------------------------------------------------------------------------------------------------- template auto VLayoutPiece::Map(QVector points) const -> QVector { - return MapVector(points, d->m_matrix, d->m_mirror); + return MapVector(points, d->m_matrix, d->m_verticallyFlipped || d->m_horizontallyFlipped); } //--------------------------------------------------------------------------------------------------------------------- diff --git a/src/libs/vlayout/vlayoutpiece.h b/src/libs/vlayout/vlayoutpiece.h index 6494dbaef..f13c58d33 100644 --- a/src/libs/vlayout/vlayoutpiece.h +++ b/src/libs/vlayout/vlayoutpiece.h @@ -156,8 +156,11 @@ public: auto GetId() const -> vidtype; void SetId(vidtype id); - auto IsMirror() const -> bool; - void SetMirror(bool value); + auto IsVerticallyFlipped() const -> bool; + void SetVerticallyFlipped(bool value); + + auto IsHorizontallyFlipped() const -> bool; + void SetHorizontallyFlipped(bool value); void SetGradationId(const QString &id); auto GetGradationId() const -> QString; diff --git a/src/libs/vlayout/vlayoutpiece_p.h b/src/libs/vlayout/vlayoutpiece_p.h index 55e4937d6..df5bfbebd 100644 --- a/src/libs/vlayout/vlayoutpiece_p.h +++ b/src/libs/vlayout/vlayoutpiece_p.h @@ -83,7 +83,8 @@ public: /** @brief layoutWidth value layout allowance width in pixels. */ qreal m_layoutWidth{0}; // NOLINT(misc-non-private-member-variables-in-classes) - bool m_mirror{false}; // NOLINT(misc-non-private-member-variables-in-classes) + bool m_verticallyFlipped{false}; // NOLINT(misc-non-private-member-variables-in-classes) + bool m_horizontallyFlipped{false}; // NOLINT(misc-non-private-member-variables-in-classes) /** @brief detailLabel detail label rectangle */ QVector m_detailLabel{}; // NOLINT(misc-non-private-member-variables-in-classes) @@ -118,7 +119,7 @@ private: Q_DISABLE_ASSIGN_MOVE(VLayoutPieceData) // NOLINT static constexpr quint32 streamHeader{0x80D7D009}; // CRC-32Q string "VLayoutPieceData" - static constexpr quint16 classVersion{5}; + static constexpr quint16 classVersion{6}; }; QT_WARNING_POP @@ -142,7 +143,7 @@ inline auto operator<<(QDataStream &dataStream, const VLayoutPieceData &piece) - dataStream << piece.m_internalPaths; dataStream << piece.m_matrix; dataStream << piece.m_layoutWidth; - dataStream << piece.m_mirror; + dataStream << piece.m_verticallyFlipped; dataStream << piece.m_detailLabel; dataStream << piece.m_patternInfo; dataStream << piece.m_placeLabels; @@ -155,6 +156,7 @@ inline auto operator<<(QDataStream &dataStream, const VLayoutPieceData &piece) - dataStream << piece.m_xScale; dataStream << piece.m_yScale; dataStream << piece.m_grainline; + dataStream << piece.m_horizontallyFlipped; return dataStream; } @@ -211,7 +213,7 @@ inline auto operator>>(QDataStream &dataStream, VLayoutPieceData &piece) -> QDat dataStream >> piece.m_internalPaths; dataStream >> piece.m_matrix; dataStream >> piece.m_layoutWidth; - dataStream >> piece.m_mirror; + dataStream >> piece.m_verticallyFlipped; dataStream >> piece.m_detailLabel; dataStream >> piece.m_patternInfo; @@ -268,6 +270,11 @@ inline auto operator>>(QDataStream &dataStream, VLayoutPieceData &piece) -> QDat } } + if (actualClassVersion >= 6) + { + dataStream >> piece.m_horizontallyFlipped; + } + return dataStream; } diff --git a/src/libs/vlayout/vposition.cpp b/src/libs/vlayout/vposition.cpp index 3fcd48b3a..bd586adb0 100644 --- a/src/libs/vlayout/vposition.cpp +++ b/src/libs/vlayout/vposition.cpp @@ -281,7 +281,7 @@ void VPosition::SaveCandidate(VBestSquare &bestResult, const VLayoutPiece &detai data.globalI = globalI; // Edge of global contour data.detJ = detJ; // Edge of detail data.resMatrix = detail.GetMatrix(); // Matrix for rotation and translation detail - data.resMirror = detail.IsMirror(); + data.resMirror = detail.IsVerticallyFlipped(); data.type = type; data.depthPosition = depthPosition; data.sidePosition = sidePosition;