/************************************************************************ ** ** @file vlayoutdetail.cpp ** @author Roman Telezhynskyi ** @date 2 1, 2015 ** ** @brief ** @copyright ** This source code is part of the Valentina project, a pattern making ** program, whose allow create and modeling patterns of clothing. ** Copyright (C) 2013-2015 Valentina project ** All Rights Reserved. ** ** Valentina 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 3 of the License, or ** (at your option) any later version. ** ** Valentina 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 Valentina. If not, see . ** *************************************************************************/ #include "vlayoutpiece.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../vpatterndb/floatItemData/vpatternlabeldata.h" #include "../vpatterndb/floatItemData/vpiecelabeldata.h" #include "../vmisc/vmath.h" #include "../vmisc/vabstractvalapplication.h" #include "../vmisc/compatibility.h" #include "../vmisc/literals.h" #include "../vpatterndb/vcontainer.h" #include "../vpatterndb/calculator.h" #include "../vpatterndb/vpassmark.h" #include "../vpatterndb/vpiecenode.h" #include "../vgeometry/vpointf.h" #include "../vgeometry/vplacelabelitem.h" #include "vlayoutdef.h" #include "vlayoutpiece_p.h" #include "vtextmanager.h" #include "vgraphicsfillitem.h" const quint32 VLayoutPieceData::streamHeader = 0x80D7D009; // CRC-32Q string "VLayoutPieceData" const quint16 VLayoutPieceData::classVersion = 3; namespace { //--------------------------------------------------------------------------------------------------------------------- QVector ConvertInternalPaths(const VPiece &piece, const VContainer *pattern) { SCASSERT(pattern != nullptr) QVector paths; const QVector pathsId = piece.GetInternalPaths(); const QVector cuttingPath = piece.CuttingPathPoints(pattern); paths.reserve(pathsId.size()); for (auto id : pathsId) { const VPiecePath path = pattern->GetPiecePath(id); if (path.GetType() == PiecePathType::InternalPath && path.IsVisible(pattern->DataVariables())) { VLayoutPiecePath convertedPath = VLayoutPiecePath(path.PathPoints(pattern, cuttingPath)); convertedPath.SetCutPath(path.IsCutPath()); convertedPath.SetPenStyle(path.GetPenType()); paths.append(convertedPath); } } return paths; } //--------------------------------------------------------------------------------------------------------------------- bool FindLabelGeometry(const VPatternLabelData &labelData, const VContainer *pattern, qreal &rotationAngle, qreal &labelWidth, qreal &labelHeight, QPointF &pos) { SCASSERT(pattern != nullptr) try { Calculator cal1; rotationAngle = cal1.EvalFormula(pattern->DataVariables(), labelData.GetRotation()); } catch(qmu::QmuParserError &e) { Q_UNUSED(e); return false; } const quint32 topLeftPin = labelData.TopLeftPin(); const quint32 bottomRightPin = labelData.BottomRightPin(); if (topLeftPin != NULL_ID && bottomRightPin != NULL_ID) { try { const auto topLeftPinPoint = pattern->GeometricObject(topLeftPin); const auto bottomRightPinPoint = pattern->GeometricObject(bottomRightPin); const QRectF labelRect = QRectF(static_cast(*topLeftPinPoint), static_cast(*bottomRightPinPoint)); labelWidth = qAbs(labelRect.width()); labelHeight = qAbs(labelRect.height()); pos = labelRect.topLeft(); return true; } catch(const VExceptionBadId &) { // do nothing. } } try { Calculator cal1; labelWidth = cal1.EvalFormula(pattern->DataVariables(), labelData.GetLabelWidth()); labelWidth = ToPixel(labelWidth, *pattern->GetPatternUnit()); Calculator cal2; labelHeight = cal2.EvalFormula(pattern->DataVariables(), labelData.GetLabelHeight()); labelHeight = ToPixel(labelHeight, *pattern->GetPatternUnit()); } catch(qmu::QmuParserError &e) { Q_UNUSED(e); return false; } const quint32 centerPin = labelData.CenterPin(); if (centerPin != NULL_ID) { try { const auto centerPinPoint = pattern->GeometricObject(centerPin); pos = static_cast(*centerPinPoint) - QRectF(0, 0, labelWidth, labelHeight).center(); } catch(const VExceptionBadId &) { pos = labelData.GetPos(); } } else { pos = labelData.GetPos(); } return true; } //--------------------------------------------------------------------------------------------------------------------- QVector PrepareAllowance(const QVector &points) { QVector allowancePoints; allowancePoints.reserve(points.size()); for(auto &point : points) { allowancePoints.append(VSAPoint(point)); } return allowancePoints; } //--------------------------------------------------------------------------------------------------------------------- /** * @brief VLayoutDetail::RotatePoint rotates a point around the center for given angle * @param ptCenter center around which the point is rotated * @param pt point, which is rotated around the center * @param dAng angle of rotation * @return position of point pt after rotating it around the center for dAng radians */ QPointF RotatePoint(const QPointF &ptCenter, const QPointF& pt, qreal dAng) { QPointF ptDest; QPointF ptRel = pt - ptCenter; ptDest.setX(cos(dAng)*ptRel.x() - sin(dAng)*ptRel.y()); ptDest.setY(sin(dAng)*ptRel.x() + cos(dAng)*ptRel.y()); return ptDest + ptCenter; } //--------------------------------------------------------------------------------------------------------------------- QStringList PieceLabelText(const QVector &labelShape, const VTextManager &tm) { QStringList text; if (labelShape.count() > 2) { int sourceCount = tm.GetSourceLinesCount(); text.reserve(sourceCount); for (int i = 0; i < sourceCount; ++i) { text.append(tm.GetSourceLine(i).m_qsText); } } return text; } //--------------------------------------------------------------------------------------------------------------------- QVector ConvertPlaceLabels(const VPiece &piece, const VContainer *pattern) { QVector labels; const auto placeLabels = piece.GetPlaceLabels(); labels.reserve(placeLabels.size()); for(auto &placeLabel : placeLabels) { const auto label = pattern->GeometricObject(placeLabel); if (label->IsVisible()) { QT_WARNING_PUSH QT_WARNING_DISABLE_GCC("-Wnoexcept") // noexcept-expression evaluates to 'false' because of a call to 'constexpr QPointF::QPointF()' VLayoutPlaceLabel layoutLabel; layoutLabel.shape = label->LabelShape(); layoutLabel.rotationMatrix = label->RotationMatrix(); layoutLabel.box = label->Box(); layoutLabel.center = label->toQPointF(); layoutLabel.type = label->GetLabelType(); labels.append(layoutLabel); QT_WARNING_POP } } return labels; } //--------------------------------------------------------------------------------------------------------------------- QVector ConvertPassmarks(const VPiece &piece, const VContainer *pattern) { const QVector passmarks = piece.Passmarks(pattern); QVector layoutPassmarks; for(auto &passmark : passmarks) { if (not passmark.IsNull()) { VPiecePassmarkData pData = passmark.Data(); auto PreapreBuiltInSAPassmark = [pData, passmark, piece, &layoutPassmarks, pattern]() { QT_WARNING_PUSH QT_WARNING_DISABLE_GCC("-Wnoexcept") // noexcept-expression evaluates to 'false' because of a call to 'constexpr QPointF::QPointF()' VLayoutPassmark layoutPassmark; QT_WARNING_POP const QVector path = piece.GetUnitedPath(pattern); const int nodeIndex = VPiecePath::indexOfNode(path, pData.id); if (nodeIndex != -1) { const QVector lines = passmark.BuiltInSAPassmark(piece, pattern); if (lines.isEmpty()) { const QString errorMsg = QObject::tr("Cannot prepare builtin passmark '%1' for piece '%2'. Passmark is empty.") .arg(pData.nodeName, piece.GetName()); VAbstractApplication::VApp()->IsPedantic() ? throw VException(errorMsg) : qWarning() << VAbstractValApplication::warningMessageSignature + errorMsg; return; } layoutPassmark.lines = lines; const QVector baseLines = passmark.BuiltInSAPassmarkBaseLine(piece); if (baseLines.isEmpty()) { const QString errorMsg = QObject::tr("Cannot prepare builtin passmark '%1' for piece '%2'. Passmark base line is " "empty.") .arg(pData.nodeName, piece.GetName()); VAbstractApplication::VApp()->IsPedantic() ? throw VException(errorMsg) : qWarning() << VAbstractValApplication::warningMessageSignature + errorMsg; return; } layoutPassmark.baseLine = ConstFirst (baseLines); layoutPassmark.type = pData.passmarkLineType; layoutPassmark.isBuiltIn = true; layoutPassmarks.append(layoutPassmark); } else { const QString errorMsg = QObject::tr("Passmark '%1' is not part of piece '%2'.") .arg(pData.nodeName, piece.GetName()); VAbstractApplication::VApp()->IsPedantic() ? throw VException(errorMsg) : qWarning() << VAbstractValApplication::warningMessageSignature + errorMsg; } }; auto PrepareSAPassmark = [pData, passmark, piece, &layoutPassmarks, pattern](PassmarkSide side) { QT_WARNING_PUSH QT_WARNING_DISABLE_GCC("-Wnoexcept") // noexcept-expression evaluates to 'false' because of a call to 'constexpr QPointF::QPointF()' VLayoutPassmark layoutPassmark; QT_WARNING_POP const QVector path = piece.GetUnitedPath(pattern); const int nodeIndex = VPiecePath::indexOfNode(path, pData.id); if (nodeIndex != -1) { QVector baseLines = passmark.SAPassmarkBaseLine(piece, pattern, static_cast(side)); if (baseLines.isEmpty()) { const QString errorMsg = QObject::tr("Cannot prepare passmark '%1' for piece '%2'. Passmark base line is empty.") .arg(pData.nodeName, piece.GetName()); VAbstractApplication::VApp()->IsPedantic() ? throw VException(errorMsg) : qWarning() << VAbstractValApplication::warningMessageSignature + errorMsg; return; } if (side == PassmarkSide::All || side == PassmarkSide::Right) { layoutPassmark.baseLine = ConstFirst(baseLines); } else if (side == PassmarkSide::Right) { layoutPassmark.baseLine = ConstLast(baseLines); } const QVector lines = passmark.SAPassmark(piece, pattern, side); if (lines.isEmpty()) { const QString errorMsg = QObject::tr("Cannot prepare passmark '%1' for piece '%2'. Passmark is empty.") .arg(pData.nodeName, piece.GetName()); VAbstractApplication::VApp()->IsPedantic() ? throw VException(errorMsg) : qWarning() << VAbstractValApplication::warningMessageSignature + errorMsg; return; } layoutPassmark.lines = lines; layoutPassmark.type = pData.passmarkLineType; layoutPassmark.isBuiltIn = false; layoutPassmarks.append(layoutPassmark); } else { const QString errorMsg = QObject::tr("Passmark '%1' is not part of piece '%2'.") .arg(pData.nodeName, piece.GetName()); VAbstractApplication::VApp()->IsPedantic() ? throw VException(errorMsg) : qWarning() << VAbstractValApplication::warningMessageSignature + errorMsg; } }; if (not piece.IsSeamAllowanceBuiltIn()) { if (pData.passmarkAngleType == PassmarkAngleType::Straightforward || pData.passmarkAngleType == PassmarkAngleType::Bisector) { PrepareSAPassmark(PassmarkSide::All); } else if (pData.passmarkAngleType == PassmarkAngleType::Intersection || pData.passmarkAngleType == PassmarkAngleType::IntersectionOnlyLeft || pData.passmarkAngleType == PassmarkAngleType::IntersectionOnlyRight || pData.passmarkAngleType == PassmarkAngleType::Intersection2 || pData.passmarkAngleType == PassmarkAngleType::Intersection2OnlyLeft || pData.passmarkAngleType == PassmarkAngleType::Intersection2OnlyRight) { if (pData.passmarkAngleType == PassmarkAngleType::Intersection || pData.passmarkAngleType == PassmarkAngleType::Intersection2) { PrepareSAPassmark(PassmarkSide::Left); PrepareSAPassmark(PassmarkSide::Right); } else if (pData.passmarkAngleType == PassmarkAngleType::IntersectionOnlyLeft || pData.passmarkAngleType == PassmarkAngleType::Intersection2OnlyLeft) { PrepareSAPassmark(PassmarkSide::Left); } else if (pData.passmarkAngleType == PassmarkAngleType::IntersectionOnlyRight || pData.passmarkAngleType == PassmarkAngleType::Intersection2OnlyRight) { PrepareSAPassmark(PassmarkSide::Right); } } if (VAbstractApplication::VApp()->Settings()->IsDoublePassmark() && (VAbstractApplication::VApp()->Settings()->IsPieceShowMainPath() || not piece.IsHideMainPath()) && pData.isMainPathNode && pData.passmarkAngleType != PassmarkAngleType::Intersection && pData.passmarkAngleType != PassmarkAngleType::IntersectionOnlyLeft && pData.passmarkAngleType != PassmarkAngleType::IntersectionOnlyRight && pData.passmarkAngleType != PassmarkAngleType::Intersection2 && pData.passmarkAngleType != PassmarkAngleType::Intersection2OnlyLeft && pData.passmarkAngleType != PassmarkAngleType::Intersection2OnlyRight && pData.isShowSecondPassmark) { PreapreBuiltInSAPassmark(); } } else { PreapreBuiltInSAPassmark(); } } } return layoutPassmarks; } //--------------------------------------------------------------------------------------------------------------------- auto PrepareGradationPlaceholders(const VContainer *data) -> QMap { SCASSERT(data != nullptr) QMap placeholders; QString heightValue = QString::number(VAbstractValApplication::VApp()->GetDimensionHeight()); placeholders.insert(pl_height, heightValue); placeholders.insert(pl_dimensionX, heightValue); QString sizeValue = QString::number(VAbstractValApplication::VApp()->GetDimensionSize()); placeholders.insert(pl_size, sizeValue); placeholders.insert(pl_dimensionY, sizeValue); QString hipValue = QString::number(VAbstractValApplication::VApp()->GetDimensionHip()); placeholders.insert(pl_hip, hipValue); placeholders.insert(pl_dimensionZ, hipValue); QString waistValue = QString::number(VAbstractValApplication::VApp()->GetDimensionWaist()); placeholders.insert(pl_waist, waistValue); placeholders.insert(pl_dimensionW, waistValue); { QString label = VAbstractValApplication::VApp()->GetDimensionHeightLabel(); placeholders.insert(pl_heightLabel, not label.isEmpty() ? label : heightValue); placeholders.insert(pl_dimensionXLabel, not label.isEmpty() ? label : heightValue); label = VAbstractValApplication::VApp()->GetDimensionSizeLabel(); placeholders.insert(pl_sizeLabel, not label.isEmpty() ? label : sizeValue); placeholders.insert(pl_dimensionYLabel, not label.isEmpty() ? label : heightValue); label = VAbstractValApplication::VApp()->GetDimensionHipLabel(); placeholders.insert(pl_hipLabel, not label.isEmpty() ? label : hipValue); placeholders.insert(pl_dimensionZLabel, not label.isEmpty() ? label : heightValue); label = VAbstractValApplication::VApp()->GetDimensionWaistLabel(); placeholders.insert(pl_waistLabel, not label.isEmpty() ? label : waistValue); placeholders.insert(pl_dimensionWLabel, not label.isEmpty() ? label : heightValue); } { const QMap > measurements = data->DataMeasurements(); auto i = measurements.constBegin(); while (i != measurements.constEnd()) { placeholders.insert(pl_measurement + i.key(), QString::number(*i.value()->GetValue())); ++i; } } return placeholders; } //--------------------------------------------------------------------------------------------------------------------- auto ReplacePlaceholders(const QMap &placeholders, QString line) -> QString { QChar per('%'); auto TestDimension = [per, placeholders, line](const QString &placeholder, const QString &errorMsg) { if (line.contains(per+placeholder+per) && placeholders.value(placeholder) == QChar('0')) { VAbstractApplication::VApp()->IsPedantic() ? throw VException(errorMsg) : qWarning() << VAbstractValApplication::warningMessageSignature + errorMsg; } }; TestDimension(pl_height, QObject::tr("No data for the height dimension.")); TestDimension(pl_size, QObject::tr("No data for the size dimension.")); TestDimension(pl_hip, QObject::tr("No data for the hip dimension.")); TestDimension(pl_waist, QObject::tr("No data for the waist dimension.")); TestDimension(pl_dimensionX, QObject::tr("No data for the X dimension.")); TestDimension(pl_dimensionY, QObject::tr("No data for the Y dimension.")); TestDimension(pl_dimensionZ, QObject::tr("No data for the Z dimension.")); TestDimension(pl_dimensionW, QObject::tr("No data for the W dimension.")); auto i = placeholders.constBegin(); while (i != placeholders.constEnd()) { line.replace(per+i.key()+per, i.value()); ++i; } return line; } //--------------------------------------------------------------------------------------------------------------------- auto PrepareGradationId(const QString &label, const VContainer *pattern) -> QString { const QMap placeholders = PrepareGradationPlaceholders(pattern); return ReplacePlaceholders(placeholders, label); } } // Friend functions //--------------------------------------------------------------------------------------------------------------------- QDataStream &operator<<(QDataStream &dataStream, const VLayoutPiece &piece) { dataStream << static_cast(piece); dataStream << *piece.d; return dataStream; } //--------------------------------------------------------------------------------------------------------------------- QDataStream &operator>>(QDataStream &dataStream, VLayoutPiece &piece) { dataStream >> static_cast(piece); dataStream >> *piece.d; return dataStream; } //--------------------------------------------------------------------------------------------------------------------- VLayoutPiece::VLayoutPiece() :VAbstractPiece(), d(new VLayoutPieceData) {} //--------------------------------------------------------------------------------------------------------------------- VLayoutPiece::VLayoutPiece(const VLayoutPiece &detail) :VAbstractPiece(detail), d(detail.d) {} //--------------------------------------------------------------------------------------------------------------------- VLayoutPiece &VLayoutPiece::operator=(const VLayoutPiece &detail) { if ( &detail == this ) { return *this; } VAbstractPiece::operator=(detail); d = detail.d; return *this; } #ifdef Q_COMPILER_RVALUE_REFS //--------------------------------------------------------------------------------------------------------------------- VLayoutPiece::VLayoutPiece(const VLayoutPiece &&detail) Q_DECL_NOTHROW :VAbstractPiece(detail), d(detail.d) {} //--------------------------------------------------------------------------------------------------------------------- VLayoutPiece &VLayoutPiece::operator=(VLayoutPiece &&detail) Q_DECL_NOTHROW { VAbstractPiece::operator=(detail); std::swap(d, detail.d); return *this; } #endif //--------------------------------------------------------------------------------------------------------------------- VLayoutPiece::~VLayoutPiece() {} //--------------------------------------------------------------------------------------------------------------------- VLayoutPiece VLayoutPiece::Create(const VPiece &piece, vidtype id, const VContainer *pattern) { QFuture > futureSeamAllowance = QtConcurrent::run(piece, &VPiece::SeamAllowancePoints, pattern); QFuture futureSeamAllowanceValid = QtConcurrent::run(piece, &VPiece::IsSeamAllowanceValid, pattern); QFuture > futureMainPath = QtConcurrent::run(piece, &VPiece::MainPathPoints, pattern); QFuture > futureInternalPaths = QtConcurrent::run(ConvertInternalPaths, piece, pattern); QFuture > futurePassmarks = QtConcurrent::run(ConvertPassmarks, piece, pattern); QFuture > futurePlaceLabels = QtConcurrent::run(ConvertPlaceLabels, piece, pattern); VLayoutPiece det; det.SetMx(piece.GetMx()); det.SetMy(piece.GetMy()); det.SetName(piece.GetName()); det.SetUUID(piece.GetUUID()); det.SetGradationId(PrepareGradationId(piece.GetGradationLabel(), pattern)); det.SetSAWidth(VAbstractValApplication::VApp()->toPixel(piece.GetSAWidth())); det.SetForbidFlipping(piece.IsForbidFlipping()); det.SetForceFlipping(piece.IsForceFlipping()); det.SetId(id); if (not futureSeamAllowanceValid.result()) { const QString errorMsg = QObject::tr("Piece '%1'. Seam allowance is not valid.") .arg(piece.GetName()); VAbstractApplication::VApp()->IsPedantic() ? throw VException(errorMsg) : qWarning() << VAbstractValApplication::warningMessageSignature + errorMsg; } det.SetCountourPoints(futureMainPath.result(), VAbstractApplication::VApp()->Settings()->IsPieceShowMainPath() ? false : piece.IsHideMainPath()); det.SetSeamAllowancePoints(futureSeamAllowance.result(), piece.IsSeamAllowance(), piece.IsSeamAllowanceBuiltIn()); det.SetInternalPaths(futureInternalPaths.result()); det.SetPassmarks(futurePassmarks.result()); det.SetPlaceLabels(futurePlaceLabels.result()); det.SetPriority(piece.GetPriority()); // Very important to set main path first! if (det.MappedContourPath().isEmpty()) { throw VException (tr("Piece %1 doesn't have shape.").arg(piece.GetName())); } const VPieceLabelData& data = piece.GetPatternPieceData(); det.SetQuantity(data.GetQuantity()); if (data.IsVisible()) { det.SetPieceText(piece.GetName(), data, VAbstractApplication::VApp()->Settings()->GetLabelFont(), pattern); } const VPatternLabelData& geom = piece.GetPatternInfo(); if (geom.IsVisible()) { VAbstractPattern* pDoc = VAbstractValApplication::VApp()->getCurrentDocument(); det.SetPatternInfo(pDoc, geom, VAbstractApplication::VApp()->Settings()->GetLabelFont(), pattern); } const VGrainlineData& grainlineGeom = piece.GetGrainlineGeometry(); if (grainlineGeom.IsVisible()) { det.SetGrainline(grainlineGeom, pattern); } return det; } //--------------------------------------------------------------------------------------------------------------------- auto VLayoutPiece::GetUniqueID() const -> QString { QString id = VAbstractPiece::GetUniqueID(); if (not d->m_gradationId.isEmpty()) { id = id + '_' + d->m_gradationId; } return id; } //--------------------------------------------------------------------------------------------------------------------- template auto VLayoutPiece::Map(QVector points) const -> QVector { std::transform(points.begin(), points.end(), points.begin(), [this](const T &point) { return d->matrix.map(point); }); if (d->mirror) { std::reverse(points.begin(), points.end()); } return points; } //--------------------------------------------------------------------------------------------------------------------- template <> QVector VLayoutPiece::Map(QVector points) const { for (int i = 0; i < points.size(); ++i) { points[i].shape = Map(points.at(i).shape); } return points; } //--------------------------------------------------------------------------------------------------------------------- template <> QVector VLayoutPiece::Map(QVector passmarks) const { for (int i = 0; i < passmarks.size(); ++i) { passmarks[i].lines = Map(passmarks.at(i).lines); passmarks[i].baseLine = d->matrix.map(passmarks.at(i).baseLine); } return passmarks; } //--------------------------------------------------------------------------------------------------------------------- // cppcheck-suppress unusedFunction QVector VLayoutPiece::GetMappedContourPoints() const { return Map(d->contour); } //--------------------------------------------------------------------------------------------------------------------- QVector VLayoutPiece::GetContourPoints() const { return d->contour; } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::SetCountourPoints(const QVector &points, bool hideMainPath) { d->contour = RemoveDublicates(points, false); SetHideMainPath(hideMainPath); } //--------------------------------------------------------------------------------------------------------------------- // cppcheck-suppress unusedFunction QVector VLayoutPiece::GetMappedSeamAllowancePoints() const { return Map(d->seamAllowance); } //--------------------------------------------------------------------------------------------------------------------- QVector VLayoutPiece::GetSeamAllowancePoints() const { return d->seamAllowance; } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::SetSeamAllowancePoints(const QVector &points, bool seamAllowance, bool seamAllowanceBuiltIn) { if (seamAllowance) { SetSeamAllowance(seamAllowance); SetSeamAllowanceBuiltIn(seamAllowanceBuiltIn); d->seamAllowance = points; if (not d->seamAllowance.isEmpty()) { d->seamAllowance = RemoveDublicates(d->seamAllowance, false); } else if (not IsSeamAllowanceBuiltIn()) { qWarning()<<"Seam allowance is empty."; SetSeamAllowance(false); } } } //--------------------------------------------------------------------------------------------------------------------- QVector VLayoutPiece::GetMappedLayoutAllowancePoints() const { return Map(d->layoutAllowance); } //--------------------------------------------------------------------------------------------------------------------- QVector VLayoutPiece::GetLayoutAllowancePoints() const { return d->layoutAllowance; } //--------------------------------------------------------------------------------------------------------------------- QPointF VLayoutPiece::GetPieceTextPosition() const { if (d->detailLabel.count() > 2) { return d->matrix.map(ConstFirst(d->detailLabel)); } else { return QPointF(); } } //--------------------------------------------------------------------------------------------------------------------- QStringList VLayoutPiece::GetPieceText() const { return PieceLabelText(d->detailLabel, d->m_tmDetail); } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::SetPieceText(const QString& qsName, const VPieceLabelData& data, const QFont &font, const VContainer *pattern) { QPointF ptPos; qreal labelWidth = 0; qreal labelHeight = 0; qreal labelAngle = 0; if (not FindLabelGeometry(data, pattern, labelAngle, labelWidth, labelHeight, ptPos)) { return; } QVector v; v << ptPos << QPointF(ptPos.x() + labelWidth, ptPos.y()) << QPointF(ptPos.x() + labelWidth, ptPos.y() + labelHeight) << QPointF(ptPos.x(), ptPos.y() + labelHeight); const qreal dAng = qDegreesToRadians(-labelAngle); const QPointF ptCenter(ptPos.x() + labelWidth/2, ptPos.y() + labelHeight/2); for (int i = 0; i < v.count(); ++i) { v[i] = RotatePoint(ptCenter, v.at(i), dAng); } QScopedPointer item(GetMainPathItem()); d->detailLabel = CorrectPosition(item->boundingRect(), v); // generate text d->m_tmDetail.SetFont(font); d->m_tmDetail.SetFontSize(data.GetFontSize()); d->m_tmDetail.Update(qsName, data, pattern); // this will generate the lines of text d->m_tmDetail.SetFontSize(data.GetFontSize()); d->m_tmDetail.FitFontSize(labelWidth, labelHeight); } //--------------------------------------------------------------------------------------------------------------------- auto VLayoutPiece::GetPieceLabelRect() const -> QVector { return d->detailLabel; } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::SetPieceLabelRect(const QVector &rect) { d->detailLabel = rect; } //--------------------------------------------------------------------------------------------------------------------- auto VLayoutPiece::GetPieceLabelData() const -> VTextManager { return d->m_tmDetail; } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::SetPieceLabelData(const VTextManager &data) { d->m_tmDetail = data; } //--------------------------------------------------------------------------------------------------------------------- QPointF VLayoutPiece::GetPatternTextPosition() const { if (d->patternInfo.count() > 2) { return d->matrix.map(ConstFirst(d->patternInfo)); } else { return QPointF(); } } //--------------------------------------------------------------------------------------------------------------------- QStringList VLayoutPiece::GetPatternText() const { return PieceLabelText(d->patternInfo, d->m_tmPattern); } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::SetPatternInfo(VAbstractPattern* pDoc, const VPatternLabelData& geom, const QFont &font, const VContainer *pattern) { QPointF ptPos; qreal labelWidth = 0; qreal labelHeight = 0; qreal labelAngle = 0; if (not FindLabelGeometry(geom, pattern, labelAngle, labelWidth, labelHeight, ptPos)) { return; } QVector v; v << ptPos << QPointF(ptPos.x() + labelWidth, ptPos.y()) << QPointF(ptPos.x() + labelWidth, ptPos.y() + labelHeight) << QPointF(ptPos.x(), ptPos.y() + labelHeight); const qreal dAng = qDegreesToRadians(-labelAngle); const QPointF ptCenter(ptPos.x() + labelWidth/2, ptPos.y() + labelHeight/2); for (int i = 0; i < v.count(); ++i) { v[i] = RotatePoint(ptCenter, v.at(i), dAng); } QScopedPointer item(GetMainPathItem()); d->patternInfo = CorrectPosition(item->boundingRect(), v); // Generate text d->m_tmPattern.SetFont(font); d->m_tmPattern.SetFontSize(geom.GetFontSize()); d->m_tmPattern.Update(pDoc, pattern); // generate lines of text d->m_tmPattern.SetFontSize(geom.GetFontSize()); d->m_tmPattern.FitFontSize(labelWidth, labelHeight); } //--------------------------------------------------------------------------------------------------------------------- auto VLayoutPiece::GetPatternLabelRect() const -> QVector { return d->patternInfo; } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::SetPatternLabelRect(const QVector &rect) { d->patternInfo = rect; } //--------------------------------------------------------------------------------------------------------------------- auto VLayoutPiece::GetPatternLabelData() const -> VTextManager { return d->m_tmPattern; } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::SetPatternLabelData(const VTextManager &data) { d->m_tmPattern = data; } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::SetGrainline(const VGrainlineData& geom, const VContainer* pattern) { qreal dAng = 0; QScopedPointer item(GetMainPathItem()); const QVector v = GrainlinePoints(geom, pattern, item->boundingRect(), dAng); if (v.isEmpty()) { return; } d->grainlineEnabled = true; d->grainlineArrowType = geom.GetArrowType(); d->grainlineAngle = qRadiansToDegrees(dAng); d->grainlinePoints = v; } //--------------------------------------------------------------------------------------------------------------------- QVector VLayoutPiece::GetMappedGrainline() const { return Map(d->grainlinePoints); } //--------------------------------------------------------------------------------------------------------------------- QVector VLayoutPiece::GetGrainline() const { return d->grainlinePoints; } //--------------------------------------------------------------------------------------------------------------------- bool VLayoutPiece::IsGrainlineEnabled() const { return d->grainlineEnabled; } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::SetGrainlineEnabled(bool enabled) { d->grainlineEnabled = enabled; } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::SetGrainlineAngle(qreal angle) { d->grainlineAngle = angle; } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::SetGrainlineArrowType(GrainlineArrowDirection type) { d->grainlineArrowType = type; } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::SetGrainlinePoints(const QVector &points) { d->grainlinePoints = points; } //--------------------------------------------------------------------------------------------------------------------- qreal VLayoutPiece::GrainlineAngle() const { return d->grainlineAngle; } //--------------------------------------------------------------------------------------------------------------------- GrainlineArrowDirection VLayoutPiece::GrainlineArrowType() const { return d->grainlineArrowType; } //--------------------------------------------------------------------------------------------------------------------- QTransform VLayoutPiece::GetMatrix() const { return d->matrix; } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::SetMatrix(const QTransform &matrix) { d->matrix = matrix; } //--------------------------------------------------------------------------------------------------------------------- qreal VLayoutPiece::GetLayoutWidth() const { return d->layoutWidth; } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::SetLayoutWidth(qreal value) { d->layoutWidth = value; } //--------------------------------------------------------------------------------------------------------------------- quint16 VLayoutPiece::GetQuantity() const { return d->m_quantity; } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::SetQuantity(quint16 value) { d->m_quantity = qMax(static_cast(1), value); } //--------------------------------------------------------------------------------------------------------------------- vidtype VLayoutPiece::GetId() const { return d->m_id; } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::SetId(vidtype id) { d->m_id = id; } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::Translate(qreal dx, qreal dy) { Translate(QPointF(dx, dy)); } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::Translate(const QPointF &p) { QTransform m; m.translate(p.x(), p.y()); d->matrix *= m; } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::Scale(qreal sx, qreal sy) { d->m_xScale *= sx; d->m_yScale *= sy; QTransform m; m.scale(sx, sy); d->matrix *= m; } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::Rotate(const QPointF &originPoint, qreal degrees) { QTransform m; m.translate(originPoint.x(), originPoint.y()); m.rotate(-degrees); m.translate(-originPoint.x(), -originPoint.y()); d->matrix *= m; } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::Mirror(const QLineF &edge) { if (edge.isNull()) { return; } const QLineF axis = QLineF(edge.x2(), edge.y2(), edge.x2() + 100, edge.y2()); // Ox axis const qreal angle = edge.angleTo(axis); const QPointF p2 = edge.p2(); QTransform m; m.translate(p2.x(), p2.y()); m.rotate(-angle); m.translate(-p2.x(), -p2.y()); d->matrix *= m; m.reset(); m.translate(p2.x(), p2.y()); m.scale(m.m11(), m.m22()*-1); m.translate(-p2.x(), -p2.y()); d->matrix *= m; m.reset(); m.translate(p2.x(), p2.y()); m.rotate(-(360-angle)); m.translate(-p2.x(), -p2.y()); d->matrix *= m; d->mirror = !d->mirror; } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::Mirror() { QTransform m; m.scale(-1, 1); d->matrix *= m; d->mirror = !d->mirror; } //--------------------------------------------------------------------------------------------------------------------- int VLayoutPiece::DetailEdgesCount() const { return DetailPath().count(); } //--------------------------------------------------------------------------------------------------------------------- int VLayoutPiece::LayoutEdgesCount() const { const int count = d->layoutAllowance.count(); return count > 2 ? count : 0; } //--------------------------------------------------------------------------------------------------------------------- QLineF VLayoutPiece::LayoutEdge(int i) const { return Edge(d->layoutAllowance, i); } //--------------------------------------------------------------------------------------------------------------------- int VLayoutPiece::LayoutEdgeByPoint(const QPointF &p1) const { return EdgeByPoint(d->layoutAllowance, p1); } //--------------------------------------------------------------------------------------------------------------------- QRectF VLayoutPiece::MappedDetailBoundingRect() const { return BoundingRect(GetMappedExternalContourPoints()); } //--------------------------------------------------------------------------------------------------------------------- QRectF VLayoutPiece::DetailBoundingRect() const { return BoundingRect(GetExternalContourPoints()); } //--------------------------------------------------------------------------------------------------------------------- QRectF VLayoutPiece::MappedLayoutBoundingRect() const { return BoundingRect(GetMappedLayoutAllowancePoints()); } //--------------------------------------------------------------------------------------------------------------------- qreal VLayoutPiece::Diagonal() const { const QRectF rec = MappedLayoutBoundingRect(); return qSqrt(pow(rec.height(), 2) + pow(rec.width(), 2)); } //--------------------------------------------------------------------------------------------------------------------- bool VLayoutPiece::isNull() const { if (d->contour.isEmpty() == false && d->layoutWidth > 0) { if (IsSeamAllowance() && not IsSeamAllowanceBuiltIn() && d->seamAllowance.isEmpty() == false) { return false; } else { return true; } } else { return true; } } //--------------------------------------------------------------------------------------------------------------------- qint64 VLayoutPiece::Square() const { return d->m_square; } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::SetLayoutAllowancePoints() { d->m_square = 0; if (d->layoutWidth > 0) { if (IsSeamAllowance() && not IsSeamAllowanceBuiltIn()) { d->layoutAllowance = Equidistant(PrepareAllowance(GetMappedSeamAllowancePoints()), d->layoutWidth, GetName()); if (not d->layoutAllowance.isEmpty()) { d->layoutAllowance.removeLast(); d->m_square = qFloor(qAbs(SumTrapezoids(GetSeamAllowancePoints())/2.0)); } } else { d->layoutAllowance = Equidistant(PrepareAllowance(GetMappedContourPoints()), d->layoutWidth, GetName()); if (not d->layoutAllowance.isEmpty()) { d->layoutAllowance.removeLast(); d->m_square = qFloor(qAbs(SumTrapezoids(GetContourPoints())/2.0)); } } } else { d->layoutAllowance.clear(); } } //--------------------------------------------------------------------------------------------------------------------- QVector VLayoutPiece::GetMappedExternalContourPoints() const { return IsSeamAllowance() && not IsSeamAllowanceBuiltIn() ? GetMappedSeamAllowancePoints() : GetMappedContourPoints(); } //--------------------------------------------------------------------------------------------------------------------- QVector VLayoutPiece::GetExternalContourPoints() const { return IsSeamAllowance() && not IsSeamAllowanceBuiltIn() ? GetSeamAllowancePoints() : GetContourPoints(); } //--------------------------------------------------------------------------------------------------------------------- QVector VLayoutPiece::GetMappedPassmarks() const { return Map(d->passmarks); } //--------------------------------------------------------------------------------------------------------------------- QVector VLayoutPiece::GetPassmarks() const { return d->passmarks; } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::SetPassmarks(const QVector &passmarks) { if (IsSeamAllowance()) { d->passmarks = passmarks; } } //--------------------------------------------------------------------------------------------------------------------- QVector VLayoutPiece::GetMappedPlaceLabels() const { return Map(d->m_placeLabels); } //--------------------------------------------------------------------------------------------------------------------- QVector VLayoutPiece::GetPlaceLabels() const { return d->m_placeLabels; } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::SetPlaceLabels(const QVector &labels) { d->m_placeLabels = labels; } //--------------------------------------------------------------------------------------------------------------------- QVector > VLayoutPiece::MappedInternalPathsForCut(bool cut) const { QVector > paths; for (const auto &path : d->m_internalPaths) { if (path.IsCutPath() == cut) { paths.append(Map(path.Points())); } } return paths; } //--------------------------------------------------------------------------------------------------------------------- QVector VLayoutPiece::GetInternalPaths() const { return d->m_internalPaths; } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::SetInternalPaths(const QVector &internalPaths) { d->m_internalPaths = internalPaths; } //--------------------------------------------------------------------------------------------------------------------- QPainterPath VLayoutPiece::MappedContourPath() const { return d->matrix.map(ContourPath()); } //--------------------------------------------------------------------------------------------------------------------- QPainterPath VLayoutPiece::ContourPath() const { QPainterPath path; // contour if (not IsHideMainPath() || not IsSeamAllowance() || IsSeamAllowanceBuiltIn()) { path = PainterPath(GetContourPoints()); } // seam allowance if (IsSeamAllowance()) { if (not IsSeamAllowanceBuiltIn()) { // Draw seam allowance QVectorpoints = GetSeamAllowancePoints(); if (ConstLast(points).toPoint() != ConstFirst(points).toPoint()) { points.append(points.at(0));// Should be always closed } QPainterPath ekv; ekv.moveTo(points.at(0)); for (qint32 i = 1; i < points.count(); ++i) { ekv.lineTo(points.at(i)); } path.addPath(ekv); } // Draw passmarks QPainterPath passmaksPath; const QVector passmarks = GetPassmarks(); for(const auto &passmark : passmarks) { for (const auto &line : passmark.lines) { passmaksPath.moveTo(line.p1()); passmaksPath.lineTo(line.p2()); } } path.addPath(passmaksPath); path.setFillRule(Qt::WindingFill); } return path; } //--------------------------------------------------------------------------------------------------------------------- QPainterPath VLayoutPiece::MappedLayoutAllowancePath() const { return PainterPath(GetMappedLayoutAllowancePoints()); } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::DrawMiniature(QPainter &painter) const { painter.drawPath(ContourPath()); for (const auto &path : d->m_internalPaths) { painter.save(); QPen pen = painter.pen(); pen.setStyle(path.PenStyle()); painter.setPen(pen); painter.drawPath(path.GetPainterPath()); painter.restore(); } for (const auto &label : d->m_placeLabels) { painter.drawPath(VPlaceLabelItem::LabelShapePath(label.shape)); } QVector gPoints = GetGrainline(); if (not gPoints.isEmpty()) { QPainterPath path; path.moveTo(gPoints.at(0)); for (auto p : qAsConst(gPoints)) { path.lineTo(p); } painter.drawPath(path); } } //--------------------------------------------------------------------------------------------------------------------- QGraphicsItem *VLayoutPiece::GetItem(bool textAsPaths) const { QGraphicsPathItem *item = GetMainItem(); for (auto &path : d->m_internalPaths) { auto* pathItem = new QGraphicsPathItem(item); pathItem->setPath(d->matrix.map(path.GetPainterPath())); QPen pen = pathItem->pen(); pen.setStyle(path.PenStyle()); pen.setWidthF(VAbstractApplication::VApp()->Settings()->WidthHairLine()); pathItem->setPen(pen); } for (const auto &label : d->m_placeLabels) { auto* pathItem = new QGraphicsPathItem(item); QPen pen = pathItem->pen(); pen.setWidthF(VAbstractApplication::VApp()->Settings()->WidthHairLine()); pathItem->setPen(pen); pathItem->setPath(d->matrix.map(VPlaceLabelItem::LabelShapePath(label.shape))); } CreateLabelStrings(item, d->detailLabel, d->m_tmDetail, textAsPaths); CreateLabelStrings(item, d->patternInfo, d->m_tmPattern, textAsPaths); CreateGrainlineItem(item); return item; } //--------------------------------------------------------------------------------------------------------------------- bool VLayoutPiece::IsLayoutAllowanceValid() const { QVector base = (IsSeamAllowance() && not IsSeamAllowanceBuiltIn()) ? d->seamAllowance : d->contour; return VAbstractPiece::IsAllowanceValid(base, d->layoutAllowance); } //--------------------------------------------------------------------------------------------------------------------- qreal VLayoutPiece::BiggestEdge() const { qreal edge = 0; if (LayoutEdgesCount() < 1) { return edge; } for (int i = 1; i < LayoutEdgesCount(); ++i) { const qreal length = LayoutEdge(i).length(); if (length > edge) { edge = length; } } return edge; } //--------------------------------------------------------------------------------------------------------------------- QRectF VLayoutPiece::BoundingRect(QVector points) { points.append(ConstFirst(points)); return QPolygonF(points).boundingRect(); } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::CreateLabelStrings(QGraphicsItem *parent, const QVector &labelShape, const VTextManager &tm, bool textAsPaths) const { SCASSERT(parent != nullptr) if (labelShape.count() > 2) { const qreal dW = QLineF(labelShape.at(0), labelShape.at(1)).length(); const qreal dH = QLineF(labelShape.at(1), labelShape.at(2)).length(); const qreal angle = - QLineF(labelShape.at(0), labelShape.at(1)).angle(); qreal dY = 0; for (int i = 0; i < tm.GetSourceLinesCount(); ++i) { const TextLine& tl = tm.GetSourceLine(i); QFont fnt = tm.GetFont(); fnt.setPixelSize(tm.GetFont().pixelSize() + tl.m_iFontSize); fnt.setBold(tl.m_bold); fnt.setItalic(tl.m_italic); QFontMetrics fm(fnt); if (textAsPaths) { dY += fm.height(); } if (dY > dH) { break; } QString qsText = tl.m_qsText; if (TextWidth(fm, qsText) > dW) { qsText = fm.elidedText(qsText, Qt::ElideMiddle, static_cast(dW)); } qreal dX = 0; if (tl.m_eAlign == 0 || (tl.m_eAlign & Qt::AlignLeft) > 0) { dX = 0; } else if ((tl.m_eAlign & Qt::AlignHCenter) > 0) { dX = (dW - TextWidth(fm, qsText))/2; } else if ((tl.m_eAlign & Qt::AlignRight) > 0) { dX = dW - TextWidth(fm, qsText); } // set up the rotation around top-left corner matrix QTransform labelMatrix; labelMatrix.translate(labelShape.at(0).x(), labelShape.at(0).y()); if (d->mirror) { labelMatrix.scale(-1, 1); labelMatrix.rotate(-angle); labelMatrix.translate(-dW, 0); labelMatrix.translate(dX, dY); // Each string has own position } else { labelMatrix.rotate(angle); labelMatrix.translate(dX, dY); // Each string has own position } labelMatrix *= d->matrix; if (textAsPaths) { QPainterPath path; path.addText(0, - static_cast(fm.ascent())/6., fnt, qsText); auto* item = new QGraphicsPathItem(parent); item->setPath(path); item->setBrush(QBrush(Qt::black)); item->setTransform(labelMatrix); dY += tm.GetSpacing(); } else { auto* item = new QGraphicsSimpleTextItem(parent); item->setFont(fnt); item->setText(qsText); item->setTransform(labelMatrix); dY += (fm.height() + tm.GetSpacing()); } } } } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::CreateGrainlineItem(QGraphicsItem *parent) const { SCASSERT(parent != nullptr) if (not d->grainlineEnabled || d->grainlinePoints.count() < 2) { return; } auto* item = new VGraphicsFillItem(parent); item->SetWidth(VAbstractApplication::VApp()->Settings()->WidthHairLine()); QPainterPath path; QVector gPoints = GetMappedGrainline(); path.moveTo(gPoints.at(0)); for (auto p : qAsConst(gPoints)) { path.lineTo(p); } item->setPath(path); } //--------------------------------------------------------------------------------------------------------------------- QVector VLayoutPiece::DetailPath() const { if (IsSeamAllowance() && not IsSeamAllowanceBuiltIn()) { return d->seamAllowance; } else { return d->contour; } } //--------------------------------------------------------------------------------------------------------------------- QGraphicsPathItem *VLayoutPiece::GetMainItem() const { QGraphicsPathItem *item = new QGraphicsPathItem(); QPen pen = item->pen(); pen.setWidthF(VAbstractApplication::VApp()->Settings()->WidthHairLine()); item->setPen(pen); item->setPath(MappedContourPath()); return item; } //--------------------------------------------------------------------------------------------------------------------- QGraphicsPathItem *VLayoutPiece::GetMainPathItem() const { QGraphicsPathItem *item = new QGraphicsPathItem(); QPen pen = item->pen(); pen.setWidthF(VAbstractApplication::VApp()->Settings()->WidthHairLine()); item->setPen(pen); QPainterPath path; // contour QVector points = GetMappedContourPoints(); path.moveTo(points.at(0)); for (qint32 i = 1; i < points.count(); ++i) { path.lineTo(points.at(i)); } path.lineTo(points.at(0)); item->setPath(path); return item; } //--------------------------------------------------------------------------------------------------------------------- bool VLayoutPiece::IsMirror() const { return d->mirror; } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::SetMirror(bool value) { d->mirror = value; } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::SetGradationId(const QString &id) { d->m_gradationId = id; } //--------------------------------------------------------------------------------------------------------------------- auto VLayoutPiece::GetGradationId() const -> QString { return d->m_gradationId; } //--------------------------------------------------------------------------------------------------------------------- auto VLayoutPiece::GetXScale() const -> qreal { return d->m_xScale; } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::SetXScale(qreal xs) { d->m_xScale = xs; } //--------------------------------------------------------------------------------------------------------------------- auto VLayoutPiece::GetYScale() const -> qreal { return d->m_yScale; } //--------------------------------------------------------------------------------------------------------------------- void VLayoutPiece::SetYScale(qreal ys) { d->m_yScale = ys; } //--------------------------------------------------------------------------------------------------------------------- QLineF VLayoutPiece::Edge(const QVector &path, int i) const { if (i < 1) { // Doesn't exist such edge return QLineF(); } int i1, i2; if (i < path.count()) { i1 = i-1; i2 = i; } else { i1 = path.count()-1; i2 = 0; } if (d->mirror) { QVector newPath = Map(path); return QLineF(newPath.at(i1), newPath.at(i2)); } else { return QLineF(d->matrix.map(path.at(i1)), d->matrix.map(path.at(i2))); } } //--------------------------------------------------------------------------------------------------------------------- // NOTE: Once C++17 is made mandatory, this method can further be refactored with std::optional int VLayoutPiece::EdgeByPoint(const QVector &path, const QPointF &p1) const { if (p1.isNull() || path.count() < 3) { return 0; } const auto points = Map(path); const auto posIter = std::find_if(points.cbegin(), points.cend(), [&p1](const QPointF &point){ return VFuzzyComparePoints(point, p1); }); if (posIter != points.cend()) { return static_cast(posIter - points.cbegin() + 1); } return 0; // Did not find edge }