diff --git a/src/libs/vlayout/vabstractpiece.h b/src/libs/vlayout/vabstractpiece.h index 4fa1e78b5..4cea4f1ac 100644 --- a/src/libs/vlayout/vabstractpiece.h +++ b/src/libs/vlayout/vabstractpiece.h @@ -158,6 +158,9 @@ public: static QVector Equidistant(const QVector &points, qreal width); static qreal SumTrapezoids(const QVector &points); static QVector CheckLoops(const QVector &points); + static QVector EkvPoint(const VSAPoint &p1Line1, const VSAPoint &p2Line1, + const VSAPoint &p1Line2, const VSAPoint &p2Line2, qreal width); + static QLineF ParallelLine(const VSAPoint &p1, const VSAPoint &p2, qreal width); template static QVector CorrectEquidistantPoints(const QVector &points, bool removeFirstAndLast = true); @@ -176,8 +179,6 @@ private: static QVector SubPath(const QVector &path, int startIndex, int endIndex); static Q_DECL_CONSTEXPR qreal PointPosition(const QPointF &p, const QLineF &line); static qreal MaxLocalSA(const VSAPoint &p, qreal width); - static QVector EkvPoint(const VSAPoint &p1Line1, const VSAPoint &p2Line1, - const VSAPoint &p1Line2, const VSAPoint &p2Line2, qreal width); static QVector AngleByLength(const QPointF &p2, const QPointF &sp1, const QPointF &sp2, const QPointF &sp3, qreal width); static QVector AngleByIntersection(const QPointF &p1, const QPointF &p2, const QPointF &p3, @@ -195,7 +196,6 @@ private: static QVector AngleBySecondRightAngle(const QPointF &p2, const QPointF &p3, const QPointF &sp1, const QPointF &sp2, const QPointF &sp3, qreal width); - static QLineF ParallelLine(const VSAPoint &p1, const VSAPoint &p2, qreal width); static QLineF ParallelLine(const QPointF &p1, const QPointF &p2, qreal width); static QPointF SingleParallelPoint(const QPointF &p1, const QPointF &p2, qreal angle, qreal width); static QLineF BisectorLine(const QPointF &p1, const QPointF &p2, const QPointF &p3); diff --git a/src/libs/vpatterndb/vpiece.cpp b/src/libs/vpatterndb/vpiece.cpp index 1de1c57ca..904c32fb9 100644 --- a/src/libs/vpatterndb/vpiece.cpp +++ b/src/libs/vpatterndb/vpiece.cpp @@ -66,6 +66,155 @@ QVector PieceMissingNodes(const QVector &d1Nodes, const QVecto return r; } + +//--------------------------------------------------------------------------------------------------------------------- +QVector FilterRecords(QVector records) +{ + if (records.size() < 2) + { + return records; + } + + bool foundFilter = false;// Need in case "filter" will stay empty. + CustomSARecord filter; + int startIndex = records.size()-1; + + for (int i = 0; i < records.size(); ++i) + { + if (records.at(i).startPoint < static_cast(startIndex)) + { + startIndex = i; + filter = records.at(i); + foundFilter = true; + } + } + + if (not foundFilter) + { + return records; // return as is + } + + records.remove(startIndex); + + QVector secondRound; + for (int i = 0; i < records.size(); ++i) + { + if (records.at(i).startPoint > filter.endPoint) + { + secondRound.append(records.at(i)); + } + } + + QVector filtered; + filtered.append(filter); + + filtered += FilterRecords(secondRound); + + return filtered; +} + +//--------------------------------------------------------------------------------------------------------------------- +qreal PassmarkLength(const VSAPoint &passmarkSAPoint, qreal width) +{ + qreal w1 = passmarkSAPoint.GetSAAfter(); + if (w1 < 0) + { + w1 = width; + } + + qreal w2 = passmarkSAPoint.GetSABefore(); + if (w2 < 0) + { + w2 = width; + } + + return qMax(w1, w2); +} + +const qreal passmarkGap = (1.5/*mm*/ / 25.4) * PrintDPI; + +//--------------------------------------------------------------------------------------------------------------------- +QVector CreateTwoPassmarkLines(const QLineF &line) +{ + QPointF l1p1; + { + QLineF line1 = line; + line1.setAngle(line1.angle() + 90); + line1.setLength(passmarkGap/2.); + l1p1 = line1.p2(); + } + + QPointF l2p1; + { + QLineF line2 = line; + line2.setAngle(line2.angle() - 90); + line2.setLength(passmarkGap/2.); + l2p1 = line2.p2(); + } + + QPointF l1p2; + { + QLineF line1 = QLineF(line.p2(), line.p1()); + line1.setAngle(line1.angle() - 90); + line1.setLength(passmarkGap/2.); + l1p2 = line1.p2(); + } + + QPointF l2p2; + { + QLineF line2 = QLineF(line.p2(), line.p1()); + line2.setAngle(line2.angle() + 90); + line2.setLength(passmarkGap/2.); + l2p2 = line2.p2(); + } + + QVector lines; + lines.append(QLineF(l1p1, l1p2)); + lines.append(QLineF(l2p1, l2p2)); + return lines; +} + +//--------------------------------------------------------------------------------------------------------------------- +QVector CreateThreePassmarkLines(const QLineF &line) +{ + QPointF l1p1; + { + QLineF line1 = line; + line1.setAngle(line1.angle() + 90); + line1.setLength(passmarkGap); + l1p1 = line1.p2(); + } + + QPointF l2p1; + { + QLineF line2 = line; + line2.setAngle(line2.angle() - 90); + line2.setLength(passmarkGap); + l2p1 = line2.p2(); + } + + QPointF l1p2; + { + QLineF line1 = QLineF(line.p2(), line.p1()); + line1.setAngle(line1.angle() - 90); + line1.setLength(passmarkGap); + l1p2 = line1.p2(); + } + + QPointF l2p2; + { + QLineF line2 = QLineF(line.p2(), line.p1()); + line2.setAngle(line2.angle() + 90); + line2.setLength(passmarkGap); + l2p2 = line2.p2(); + } + + QVector lines; + lines.append(QLineF(l1p1, l1p2)); + lines.append(line); + lines.append(QLineF(l2p1, l2p2)); + return lines; +} } //--------------------------------------------------------------------------------------------------------------------- @@ -137,7 +286,7 @@ QVector VPiece::SeamAllowancePoints(const VContainer *data) const return QVector(); } - const QVector records = GetValidRecords(); + const QVector records = FilterRecords(GetValidRecords()); int recordIndex = -1; bool insertingCSA = false; const qreal width = ToPixel(GetSAWidth(), *data->GetPatternUnit()); @@ -215,6 +364,52 @@ QVector VPiece::SeamAllowancePoints(const VContainer *data) const return Equidistant(pointsEkv, width); } +//--------------------------------------------------------------------------------------------------------------------- +QVector VPiece::PassmarksLines(const VContainer *data) const +{ + if (not IsSeamAllowance() || not IsPassmarksPossible()) + { + return QVector(); + } + + QVector passmarks; + + for (int i = 0; i< d->m_path.CountNodes(); ++i) + { + const VPieceNode &node = d->m_path.at(i); + if (node.IsExcluded() || not node.IsPassmark()) + { + continue;// skip node + } + + int passmarkIndex = i; + + int previousIndex = 0; + if (passmarkIndex == 0) + { + previousIndex = VPiecePath::FindInLoopNotExcludedUp(d->m_path.CountNodes()-1, d->m_path.GetNodes()); + } + else + { + previousIndex = VPiecePath::FindInLoopNotExcludedUp(passmarkIndex-1, d->m_path.GetNodes()); + } + + int nextIndex = 0; + if (passmarkIndex == d->m_path.CountNodes()-1) + { + nextIndex = VPiecePath::FindInLoopNotExcludedDown(0, d->m_path.GetNodes()); + } + else + { + nextIndex = VPiecePath::FindInLoopNotExcludedDown(passmarkIndex+1, d->m_path.GetNodes()); + } + + passmarks += CreatePassmark(previousIndex, passmarkIndex, nextIndex, data); + } + + return passmarks; +} + //--------------------------------------------------------------------------------------------------------------------- QPainterPath VPiece::MainPathPath(const VContainer *data) const { @@ -259,6 +454,30 @@ QPainterPath VPiece::SeamAllowancePath(const VContainer *data) const return ekv; } +//--------------------------------------------------------------------------------------------------------------------- +QPainterPath VPiece::PassmarksPath(const VContainer *data) const +{ + const QVector passmarks = PassmarksLines(data); + QPainterPath path; + + // seam allowence + if (IsSeamAllowance()) + { + if (not passmarks.isEmpty()) + { + for (qint32 i = 0; i < passmarks.count(); ++i) + { + path.moveTo(passmarks.at(i).p1()); + path.lineTo(passmarks.at(i).p2()); + } + + path.setFillRule(Qt::WindingFill); + } + } + + return path; +} + //--------------------------------------------------------------------------------------------------------------------- qreal VPiece::GetMx() const { @@ -505,7 +724,8 @@ QVector VPiece::GetValidRecords() const && indexStartPoint != -1 && not d->m_path.at(indexStartPoint).IsExcluded() && indexEndPoint != -1 - && not d->m_path.at(indexEndPoint).IsExcluded()) + && not d->m_path.at(indexEndPoint).IsExcluded() + && record.startPoint < record.endPoint) { records.append(record); } @@ -513,6 +733,267 @@ QVector VPiece::GetValidRecords() const return records; } +//--------------------------------------------------------------------------------------------------------------------- +QVector VPiece::GetNodeSAPoints(int index, const VContainer *data) const +{ + SCASSERT(data != nullptr) + + if (index < 0 || index >= d->m_path.CountNodes()) + { + return QVector(); + } + + const VPieceNode &node = d->m_path.at(index); + QVector points; + + if (node.GetTypeTool() == Tool::NodePoint) + { + points.append(VPiecePath::PreparePointEkv(node, data)); + } + else + { + const QSharedPointer curve = data->GeometricObject(node.GetId()); + const qreal width = ToPixel(GetSAWidth(), *data->GetPatternUnit()); + + points += VPiecePath::CurveSeamAllowanceSegment(data, d->m_path.GetNodes(), curve, index, node.GetReverse(), + width); + } + return points; +} + +//--------------------------------------------------------------------------------------------------------------------- +bool VPiece::GetPassmarkSAPoint(int index, const VContainer *data, VSAPoint &point) const +{ + SCASSERT(data != nullptr) + + const QVector points = GetNodeSAPoints(index, data); + + if (points.isEmpty() || points.size() > 1) + { + return false; + } + + point = points.first(); + return true; +} + +//--------------------------------------------------------------------------------------------------------------------- +bool VPiece::GetPassmarkPreviousSAPoint(int index, const VSAPoint &passmarkSAPoint, const VContainer *data, + VSAPoint &point) const +{ + SCASSERT(data != nullptr) + + const QVector points = GetNodeSAPoints(index, data); + + if (points.isEmpty()) + { + return false; // Something wrong + } + + bool found = false; + int nodeIndex = points.size()-1; + do + { + const VSAPoint previous = points.at(nodeIndex); + if (passmarkSAPoint.toPoint() != previous.toPoint()) + { + point = previous; + found = true; + } + --nodeIndex; + } while (nodeIndex >= 0 && not found); + + if (not found) + { + return false; // Something wrong + } + return true; +} + +//--------------------------------------------------------------------------------------------------------------------- +bool VPiece::GetPassmarkNextSAPoint(int index, const VSAPoint &passmarkSAPoint, const VContainer *data, + VSAPoint &point) const +{ + const QVector points = GetNodeSAPoints(index, data); + + if (points.isEmpty()) + { + return false; // Something wrong + } + + bool found = false; + int nodeIndex = 0; + do + { + const VSAPoint next = points.at(nodeIndex); + if (passmarkSAPoint.toPoint() != next.toPoint()) + { + point = next; + found = true; + } + ++nodeIndex; + + } while (nodeIndex < points.size() && not found); + + if (not found) + { + return false; // Something wrong + } + return true; +} + +//--------------------------------------------------------------------------------------------------------------------- +bool VPiece::GetSeamPassmarkSAPoint(const VSAPoint &previousSAPoint, const VSAPoint &passmarkSAPoint, + const VSAPoint &nextSAPoint, const VContainer *data, QPointF &point) const +{ + SCASSERT(data != nullptr) + + const qreal width = ToPixel(GetSAWidth(), *data->GetPatternUnit()); + const QVector ekvPoints = EkvPoint(previousSAPoint, passmarkSAPoint, nextSAPoint, passmarkSAPoint, width); + + if (ekvPoints.isEmpty()) + { // Just in case + return false; // Something wrong + } + + if (ekvPoints.size() == 1 || ekvPoints.size() > 2) + { + point = ekvPoints.first(); + } + else if (ekvPoints.size() == 2) + { + QLineF line = QLineF(ekvPoints.at(0), ekvPoints.at(1)); + line.setLength(line.length()/2.); + point = line.p2(); + } + return true; +} + +//--------------------------------------------------------------------------------------------------------------------- +bool VPiece::IsPassmarksPossible() const +{ + int countPointNodes = 0; + int countOthers = 0; + + for (int i = 0; i< d->m_path.CountNodes(); ++i) + { + const VPieceNode &node = d->m_path.at(i); + if (node.IsExcluded()) + { + continue;// skip node + } + + node.GetTypeTool() == Tool::NodePoint ? ++countPointNodes : ++countOthers; + } + + return countPointNodes >= 3 || (countPointNodes >= 1 && countOthers >= 1); +} + +//--------------------------------------------------------------------------------------------------------------------- +bool VPiece::IsPassmarkVisible(int passmarkIndex) const +{ + if (passmarkIndex < 0 || passmarkIndex >= d->m_path.CountNodes()) + { + return false; + } + + const VPieceNode &node = d->m_path.at(passmarkIndex); + if (node.GetTypeTool() != Tool::NodePoint || not node.IsPassmark() || node.IsExcluded()) + { + return false; + } + + const QVector records = FilterRecords(GetValidRecords()); + if (records.isEmpty()) + { + return true; + } + + for (int i = 0; i < records.size(); ++i) + { + const int indexStartPoint = d->m_path.indexOfNode(records.at(i).startPoint); + const int indexEndPoint = d->m_path.indexOfNode(records.at(i).endPoint); + if (passmarkIndex > indexStartPoint && passmarkIndex < indexEndPoint) + { + return false; + } + } + return true; +} + +//--------------------------------------------------------------------------------------------------------------------- +QVector VPiece::CreatePassmark(int previousIndex, int passmarkIndex, int nextIndex, + const VContainer *data) const +{ + SCASSERT(data != nullptr); + + if (not IsPassmarkVisible(passmarkIndex)) + { + return QVector(); + } + + VSAPoint passmarkSAPoint; + if (not GetPassmarkSAPoint(passmarkIndex, data, passmarkSAPoint)) + { + return QVector(); // Something wrong + } + + VSAPoint previousSAPoint; + if (not GetPassmarkPreviousSAPoint(previousIndex, passmarkSAPoint, data, previousSAPoint)) + { + return QVector(); // Something wrong + } + + VSAPoint nextSAPoint; + if (not GetPassmarkNextSAPoint(nextIndex, passmarkSAPoint, data, nextSAPoint)) + { + return QVector(); // Something wrong + } + + QPointF seamPassmarkSAPoint; + if (not GetSeamPassmarkSAPoint(previousSAPoint, passmarkSAPoint, nextSAPoint, data, seamPassmarkSAPoint)) + { + return QVector(); // Something wrong + } + + const qreal width = ToPixel(GetSAWidth(), *data->GetPatternUnit()); + const QLineF bigLine1 = ParallelLine(previousSAPoint, passmarkSAPoint, width ); + const QLineF bigLine2 = ParallelLine(passmarkSAPoint, nextSAPoint, width ); + + QVector passmarksLines; + + const qreal passmarkLength = PassmarkLength(passmarkSAPoint, width) * 0.25; + const VPieceNode &node = d->m_path.at(passmarkIndex); + if (node.GetPassmarkAngleType() == PassmarkAngleType::Straightforward) + { + QLineF line = QLineF(seamPassmarkSAPoint, passmarkSAPoint); + line.setLength(passmarkLength); + if (node.GetPassmarkLineType() == PassmarkLineType::TwoLines) + { + passmarksLines += CreateTwoPassmarkLines(line); + } + else if (node.GetPassmarkLineType() == PassmarkLineType::ThreeLines) + { + passmarksLines += CreateThreePassmarkLines(line); + } + else + { + passmarksLines.append(line); + } + } + else + { + QLineF edge1 = QLineF(seamPassmarkSAPoint, bigLine1.p1()); + QLineF edge2 = QLineF(seamPassmarkSAPoint, bigLine2.p2()); + + edge1.setAngle(edge1.angle() + edge1.angleTo(edge2)/2.); + edge1.setLength(passmarkLength); + passmarksLines.append(edge1); + } + + return passmarksLines; +} + //--------------------------------------------------------------------------------------------------------------------- int VPiece::IsCSAStart(const QVector &records, quint32 id) { diff --git a/src/libs/vpatterndb/vpiece.h b/src/libs/vpatterndb/vpiece.h index 234a2bbf1..1235f16f4 100644 --- a/src/libs/vpatterndb/vpiece.h +++ b/src/libs/vpatterndb/vpiece.h @@ -61,9 +61,11 @@ public: QVector MainPathPoints(const VContainer *data) const; QVector MainPathNodePoints(const VContainer *data, bool showExcluded = false) const; QVector SeamAllowancePoints(const VContainer *data) const; + QVector PassmarksLines(const VContainer *data) const; QPainterPath MainPathPath(const VContainer *data) const; QPainterPath SeamAllowancePath(const VContainer *data) const; + QPainterPath PassmarksPath(const VContainer *data) const; qreal GetMx() const; void SetMx(qreal value); @@ -113,6 +115,21 @@ private: QVector GetValidRecords() const; + QVector GetNodeSAPoints(int index, const VContainer *data) const; + + bool GetPassmarkSAPoint(int index, const VContainer *data, VSAPoint &point) const; + bool GetPassmarkPreviousSAPoint(int index, const VSAPoint &passmarkSAPoint, const VContainer *data, + VSAPoint &point) const; + bool GetPassmarkNextSAPoint(int index, const VSAPoint &passmarkSAPoint, const VContainer *data, + VSAPoint &point) const; + bool GetSeamPassmarkSAPoint(const VSAPoint &previousSAPoint, const VSAPoint &passmarkSAPoint, + const VSAPoint &nextSAPoint, const VContainer *data, QPointF &point) const; + + bool IsPassmarksPossible() const; + bool IsPassmarkVisible(int passmarkIndex) const; + + QVector CreatePassmark(int previousIndex, int passmarkIndex, int nextIndex, const VContainer *data) const; + static int IsCSAStart(const QVector &records, quint32 id); }; diff --git a/src/libs/vtools/tools/vtoolseamallowance.cpp b/src/libs/vtools/tools/vtoolseamallowance.cpp index 3bcbd3976..adc856fa2 100644 --- a/src/libs/vtools/tools/vtoolseamallowance.cpp +++ b/src/libs/vtools/tools/vtoolseamallowance.cpp @@ -1172,7 +1172,7 @@ VToolSeamAllowance::VToolSeamAllowance(VAbstractPattern *doc, VContainer *data, this->setFlag(QGraphicsItem::ItemIsSelectable, true); RefreshGeometry(); - m_seamAllowance->setBrush(QBrush(Qt::FDiagPattern)); + m_seamAllowance->setBrush(QBrush(Qt::Dense7Pattern)); this->setFlag(QGraphicsItem::ItemSendsGeometryChanges, true); this->setFlag(QGraphicsItem::ItemIsFocusable, true);// For keyboard input focus @@ -1232,15 +1232,21 @@ void VToolSeamAllowance::RefreshGeometry() this->setFlag(QGraphicsItem::ItemSendsGeometryChanges, false); const VPiece detail = VAbstractTool::data.GetPiece(id); - QPainterPath mainPath = detail.MainPathPath(this->getData()); - this->setPath(mainPath); + QPainterPath path = detail.MainPathPath(this->getData()); + + { + QPainterPath mainPath = path; + mainPath.addPath(detail.PassmarksPath(this->getData())); + this->setPath(mainPath); + } + this->setPos(detail.GetMx(), detail.GetMy()); if (detail.IsSeamAllowance()) { - mainPath.addPath(detail.SeamAllowancePath(this->getData())); - mainPath.setFillRule(Qt::OddEvenFill); - m_seamAllowance->setPath(mainPath); + path.addPath(detail.SeamAllowancePath(this->getData())); + path.setFillRule(Qt::OddEvenFill); + m_seamAllowance->setPath(path); } else {