/************************************************************************ ** ** @file vppiece.cpp ** @author Ronan Le Tiec ** @date 13 4, 2020 ** ** @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) 2020 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 "vppiece.h" #include #include "../vmisc/def.h" #include "vpsheet.h" #include "vplayout.h" #include "../vlayout/vtextmanager.h" #include "../vlayout/vlayoutpiecepath.h" #include #include #include #include Q_LOGGING_CATEGORY(pPiece, "p.piece") namespace { constexpr qreal minStickyDistance = 3 / 25.4 * PrintDPI; // 3 mm constexpr qreal maxStickyDistance = 10 / 25.4 * PrintDPI; // 10 mm constexpr qreal stickyShift = 1 / 25.4 * PrintDPI; // 1 mm //--------------------------------------------------------------------------------------------------------------------- auto CutEdge(const QLineF &edge) -> QVector { QVector points; if (qFuzzyIsNull(stickyShift)) { points.append(edge.p1()); points.append(edge.p2()); } else { const int n = qFloor(edge.length()/stickyShift); if (n <= 0) { points.append(edge.p1()); points.append(edge.p2()); } else { points.reserve(n); const qreal nShift = edge.length()/n; for (int i = 1; i <= n+1; ++i) { QLineF l1 = edge; l1.setLength(nShift*(i-1)); points.append(l1.p2()); } } } return points; } //--------------------------------------------------------------------------------------------------------------------- auto PrepareStickyPath(const QVector &path) -> QVector { if (path.size() < 2) { return path; } QVector stickyPath; for (int i=0; i &path1, const QVector &path2) -> QLineF { qreal distance = INT_MAX; QLineF closestDistance; for (auto p1 : path1) { for (auto p2 : path2) { QLineF d(p1, p2); if (d.length() <= distance) { distance = d.length(); closestDistance = d; } } } return closestDistance; } } // namespace //--------------------------------------------------------------------------------------------------------------------- VPPiece::VPPiece(const VLayoutPiece &layoutPiece) : VLayoutPiece(layoutPiece) { ClearTransformations(); } //--------------------------------------------------------------------------------------------------------------------- void VPPiece::Update(const VPPiecePtr &piece) { if (piece.isNull()) { return; } SetName(piece->GetName()); SetCountourPoints(piece->GetContourPoints(), piece->IsHideMainPath()); SetSeamAllowancePoints(piece->GetSeamAllowancePoints(), piece->IsSeamAllowance(), piece->IsSeamAllowanceBuiltIn()); SetInternalPaths(piece->GetInternalPaths()); SetPassmarks(piece->GetPassmarks()); SetPlaceLabels(piece->GetPlaceLabels()); SetGrainlineEnabled(piece->IsGrainlineEnabled()); SetGrainlineAngle(piece->GrainlineAngle()); SetGrainlineArrowType(piece->GrainlineArrowType()); SetGrainlinePoints(piece->GetGrainline()); SetPieceLabelRect(piece->GetPieceLabelRect()); SetPieceLabelData(piece->GetPieceLabelData()); SetPatternLabelRect(piece->GetPatternLabelRect()); SetPatternLabelData(piece->GetPatternLabelData()); } //--------------------------------------------------------------------------------------------------------------------- auto VPPiece::GetUniqueID() const -> QString { QString id = VLayoutPiece::GetUniqueID(); if (m_copyNumber > 1) { id = id + '_' + QString::number(m_copyNumber); } return id; } //--------------------------------------------------------------------------------------------------------------------- void VPPiece::ClearTransformations() { // Reset the piece position to the default state SetMatrix(QTransform()); // restore original size QTransform m; m.scale(GetXScale(), GetYScale()); QTransform matrix = GetMatrix(); matrix *= m; SetMatrix(matrix); // translate the piece so that the top left corner of the bouding rect of the piece is at the position // (0,0) in the sheet coordinate system const QPointF offset = MappedDetailBoundingRect().topLeft(); Translate(-offset.x(), -offset.y()); SetMirror(false); } //--------------------------------------------------------------------------------------------------------------------- void VPPiece::SetPosition(QPointF point) { QTransform matrix = GetMatrix(); const QPointF offset = MappedDetailBoundingRect().topLeft(); matrix.translate(point.x() - offset.x(), point.y() - offset.y()); SetMatrix(matrix); } //--------------------------------------------------------------------------------------------------------------------- auto VPPiece::GetPosition() -> QPointF { QTransform matrix = GetMatrix(); return QPointF(matrix.dx(), matrix.dy()); } //--------------------------------------------------------------------------------------------------------------------- void VPPiece::RotateToGrainline(const VPTransformationOrigon &origin) { VPSheetPtr sheet = Sheet(); if (not IsGrainlineEnabled() || sheet.isNull()) { return; } const QVector grainlinePoints = GetMappedGrainline(); if (grainlinePoints.count() < 2) { return; } QLineF grainline(ConstFirst(grainlinePoints), ConstLast(grainlinePoints)); QLineF canonical(ConstFirst(grainlinePoints).x(), ConstFirst(grainlinePoints).y(), ConstFirst(grainlinePoints).x()+100, ConstFirst(grainlinePoints).y()); GrainlineType grainlineType = sheet->GrainlineOrientation(); auto DegreesAtFront = [grainline, canonical, grainlineType]() { QLineF atFront = canonical; if (grainlineType == GrainlineType::Vertical) { atFront.setAngle(90); } qreal angleTo = grainline.angleTo(atFront); return angleTo; }; auto DegreesAtRear = [grainline, canonical, grainlineType]() { QLineF atRear = canonical; atRear.setAngle(grainlineType == GrainlineType::Vertical ? 270 : 180); qreal angleTo = grainline.angleTo(atRear); return angleTo; }; GrainlineArrowDirection type = GrainlineArrowType(); qreal degrees = 0; if (type == GrainlineArrowDirection::atFront) { degrees = DegreesAtFront(); } else if (type == GrainlineArrowDirection::atRear) { degrees = DegreesAtRear(); } else { const qreal atFront = DegreesAtFront(); if (atFront <= 90 || atFront >= 270) { degrees = atFront; } else { degrees = DegreesAtRear(); } } if (origin.custom) { Rotate(MappedDetailBoundingRect().center(), degrees); } else { Rotate(origin.origin, degrees); } } //--------------------------------------------------------------------------------------------------------------------- void VPPiece::SetGrainlineEnabled(bool enabled) { VLayoutPiece::SetGrainlineEnabled(enabled); } //--------------------------------------------------------------------------------------------------------------------- void VPPiece::SetGrainlineAngle(qreal angle) { VLayoutPiece::SetGrainlineAngle(angle); } //--------------------------------------------------------------------------------------------------------------------- void VPPiece::SetGrainlineArrowType(GrainlineArrowDirection type) { VLayoutPiece::SetGrainlineArrowType(type); } //--------------------------------------------------------------------------------------------------------------------- void VPPiece::SetGrainlinePoints(const QVector &points) { VLayoutPiece::SetGrainlinePoints(points); } //--------------------------------------------------------------------------------------------------------------------- auto VPPiece::GetPieceLabelRect() const -> QVector { return VLayoutPiece::GetPieceLabelRect(); } //--------------------------------------------------------------------------------------------------------------------- void VPPiece::SetPieceLabelRect(const QVector &rect) { VLayoutPiece::SetPieceLabelRect(rect); } //--------------------------------------------------------------------------------------------------------------------- auto VPPiece::GetPieceLabelData() const -> VTextManager { return VLayoutPiece::GetPieceLabelData(); } //--------------------------------------------------------------------------------------------------------------------- void VPPiece::SetPieceLabelData(const VTextManager &data) { VLayoutPiece::SetPieceLabelData(data); } //--------------------------------------------------------------------------------------------------------------------- auto VPPiece::GetPatternLabelRect() const -> QVector { return VLayoutPiece::GetPatternLabelRect(); } //--------------------------------------------------------------------------------------------------------------------- void VPPiece::SetPatternLabelRect(const QVector &rect) { VLayoutPiece::SetPatternLabelRect(rect); } //--------------------------------------------------------------------------------------------------------------------- auto VPPiece::GetPatternLabelData() const -> VTextManager { return VLayoutPiece::GetPatternLabelData(); } //--------------------------------------------------------------------------------------------------------------------- void VPPiece::SetPatternLabelData(const VTextManager &data) { VLayoutPiece::SetPatternLabelData(data); } //--------------------------------------------------------------------------------------------------------------------- void VPPiece::Flip() { QTransform pieceMatrix = GetMatrix(); QPointF center = pieceMatrix.map(DetailBoundingRect().center()); QTransform m; m.translate(center.x(), 0); m.scale(-1, 1); m.translate(-center.x(), 0); pieceMatrix *= m; SetMatrix(pieceMatrix); SetMirror(!IsMirror()); } //--------------------------------------------------------------------------------------------------------------------- auto VPPiece::StickyPosition(qreal &dx, qreal &dy) const -> bool { VPLayoutPtr layout = Layout(); if (layout.isNull() || not layout->LayoutSettings().GetStickyEdges()) { return false; } const qreal pieceGap = layout->LayoutSettings().GetPiecesGap(); if (pieceGap <= 0) { return false; } VPSheetPtr sheet = Sheet(); if (sheet.isNull()) { return false; } QList allPieces = sheet->GetPieces(); if (allPieces.count() < 2) { return false; } QVector path = GetMappedExternalContourPoints(); QRectF boundingRect = VLayoutPiece::BoundingRect(path); const qreal stickyDistance = pieceGap+minStickyDistance; QRectF stickyZone = QRectF(boundingRect.topLeft().x()-stickyDistance, boundingRect.topLeft().y()-stickyDistance, boundingRect.width()+stickyDistance*2, boundingRect.height()+stickyDistance*2); QVector stickyPath = PrepareStickyPath(path); QLineF closestDistance; for (const auto& piece : allPieces) { if (piece.isNull() || piece->GetUniqueID() == GetUniqueID()) { continue; } QVector piecePath = piece->GetMappedExternalContourPoints(); QRectF pieceBoundingRect = VLayoutPiece::BoundingRect(piecePath); if (stickyZone.intersects(pieceBoundingRect) || pieceBoundingRect.contains(stickyZone) || stickyZone.contains(pieceBoundingRect)) { if (not VPPiece::PathsSuperposition(path, piecePath)) { QVector pieceStickyPath = PrepareStickyPath(piecePath); closestDistance = ClosestDistance(stickyPath, pieceStickyPath); } } } if (closestDistance.isNull()) { return false; } const qreal extraZone = qBound(minStickyDistance, pieceGap * 50 / 100, maxStickyDistance); const qreal length = closestDistance.length(); if (length > pieceGap && length <= pieceGap + extraZone) { closestDistance.setLength(length - pieceGap); QPointF diff = closestDistance.p2() - closestDistance.p1(); dx = diff.x(); dy = diff.y(); return true; } if (length < pieceGap && length >= pieceGap - extraZone) { closestDistance.setAngle(closestDistance.angle() + 180); closestDistance.setLength(pieceGap - length); QPointF diff = closestDistance.p2() - closestDistance.p1(); dx = diff.x(); dy = diff.y(); return true; } return false; } //--------------------------------------------------------------------------------------------------------------------- auto VPPiece::PathsSuperposition(const QVector &path1, const QVector &path2) -> bool { const QRectF path1Rect = VLayoutPiece::BoundingRect(path1); const QPainterPath path1Path = VAbstractPiece::PainterPath(path1); const QRectF path2Rect = VLayoutPiece::BoundingRect(path2); const QPainterPath path2Path = VAbstractPiece::PainterPath(path2); if (path1Rect.intersects(path2Rect) || path2Rect.contains(path1Rect) || path1Rect.contains(path2Rect)) { if (path1Path.contains(path2Path) || path2Path.contains(path1Path) || path1Path.intersects(path2Path)) { return true; } } return false; } //--------------------------------------------------------------------------------------------------------------------- auto VPPiece::IsValid() const -> bool { if (not IsHideMainPath() && GetContourPoints().isEmpty()) { return false; } if (IsSeamAllowance() && IsSeamAllowanceBuiltIn() && GetContourPoints().isEmpty()) { return false; } if (IsSeamAllowance() && not IsSeamAllowanceBuiltIn() && GetSeamAllowancePoints().isEmpty()) { return false; } if (IsGrainlineEnabled() && GetGrainline().isEmpty()) { return false; } return true; } //--------------------------------------------------------------------------------------------------------------------- auto VPPiece::CopyNumber() const -> quint16 { return m_copyNumber; } //--------------------------------------------------------------------------------------------------------------------- void VPPiece::SetCopyNumber(quint16 newCopyNumber) { m_copyNumber = qMax(static_cast(1), newCopyNumber); }