Breaking change. All intersections are now treaded as a loop.
--HG-- branch : develop
This commit is contained in:
parent
c2d194c23f
commit
bcea1b69fa
|
@ -27,6 +27,7 @@
|
||||||
- New feature "Hide piece's main path".
|
- New feature "Hide piece's main path".
|
||||||
- Allow controlling priority selecting pieces in layout.
|
- Allow controlling priority selecting pieces in layout.
|
||||||
- No scissors on tiled PDF bottom row.
|
- No scissors on tiled PDF bottom row.
|
||||||
|
- All intersections are now treaded as a loop.
|
||||||
|
|
||||||
# Version 0.6.2 (unreleased)
|
# Version 0.6.2 (unreleased)
|
||||||
- [#903] Bug in tool Cut Spline path.
|
- [#903] Bug in tool Cut Spline path.
|
||||||
|
|
57
src/app/share/collection/bugs/loop_by_intersection.val
Normal file
57
src/app/share/collection/bugs/loop_by_intersection.val
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<pattern labelPrefix="en">
|
||||||
|
<!--Pattern created with Valentina v0.7.0.0a (https://valentinaproject.bitbucket.io/).-->
|
||||||
|
<version>0.8.5</version>
|
||||||
|
<unit>cm</unit>
|
||||||
|
<description/>
|
||||||
|
<notes/>
|
||||||
|
<measurements/>
|
||||||
|
<increments/>
|
||||||
|
<previewCalculations/>
|
||||||
|
<draw name="Pattern piece 1">
|
||||||
|
<calculation>
|
||||||
|
<point id="1" mx="0.132292" my="0.264583" name="A" showLabel="true" type="single" x="0.79375" y="1.05833"/>
|
||||||
|
<point angle="0" basePoint="1" id="2" length="6" lineColor="black" mx="0.132292" my="0.264583" name="A1" showLabel="true" type="endLine" typeLine="hair"/>
|
||||||
|
<point angle="180" basePoint="1" id="3" length="6" lineColor="black" mx="0.132292" my="0.264583" name="A2" showLabel="true" type="endLine" typeLine="hair"/>
|
||||||
|
<point angle="90" basePoint="1" id="4" length="3.7" lineColor="black" mx="0.132292" my="0.264583" name="A3" showLabel="true" type="endLine" typeLine="hair"/>
|
||||||
|
<spline aScale="0" angle1="75.4653" angle2="180.572" color="black" id="5" length1="3.44539" length2="2.79047" penStyle="hair" point1="3" point4="4" type="simpleInteractive"/>
|
||||||
|
<spline aScale="0" angle1="0.535754" angle2="113.25" color="black" id="6" length1="3.24281" length2="2.80818" penStyle="hair" point1="4" point4="2" type="simpleInteractive"/>
|
||||||
|
<operation id="7" p1Line="3" p2Line="2" suffix="a1" type="flippingByLine">
|
||||||
|
<source>
|
||||||
|
<item idObject="5"/>
|
||||||
|
<item idObject="6"/>
|
||||||
|
</source>
|
||||||
|
<destination>
|
||||||
|
<item idObject="8"/>
|
||||||
|
<item idObject="9"/>
|
||||||
|
</destination>
|
||||||
|
</operation>
|
||||||
|
</calculation>
|
||||||
|
<modeling>
|
||||||
|
<point id="10" idObject="3" inUse="false" mx="0.132292" my="0.264583" showLabel="true" type="modeling"/>
|
||||||
|
<spline id="11" idObject="5" inUse="false" type="modelingSpline"/>
|
||||||
|
<point id="12" idObject="4" inUse="false" mx="0.132292" my="0.264583" showLabel="true" type="modeling"/>
|
||||||
|
<spline id="13" idObject="6" inUse="false" type="modelingSpline"/>
|
||||||
|
<point id="14" idObject="2" inUse="false" mx="0.132292" my="0.264583" showLabel="true" type="modeling"/>
|
||||||
|
<spline id="15" idObject="9" inUse="false" type="modelingSpline"/>
|
||||||
|
<spline id="16" idObject="8" inUse="false" type="modelingSpline"/>
|
||||||
|
</modeling>
|
||||||
|
<details>
|
||||||
|
<detail forbidFlipping="false" forceFlipping="false" hideMainPath="false" id="17" mx="0" my="0" name="Detail" seamAllowance="true" version="2" width="1.05">
|
||||||
|
<data annotation="" foldPosition="" fontSize="0" height="1" letter="" mx="0" my="0" onFold="false" orientation="" quantity="1" rotation="0" rotationWay="" tilt="" visible="false" width="1"/>
|
||||||
|
<patternInfo fontSize="0" height="1" mx="0" my="0" rotation="0" visible="false" width="1"/>
|
||||||
|
<grainline arrows="0" length="1" mx="0" my="0" rotation="90" visible="false"/>
|
||||||
|
<nodes>
|
||||||
|
<node angle="1" idObject="10" type="NodePoint"/>
|
||||||
|
<node idObject="11" reverse="0" type="NodeSpline"/>
|
||||||
|
<node idObject="12" type="NodePoint"/>
|
||||||
|
<node idObject="13" reverse="0" type="NodeSpline"/>
|
||||||
|
<node angle="1" idObject="14" type="NodePoint"/>
|
||||||
|
<node idObject="15" reverse="1" type="NodeSpline"/>
|
||||||
|
<node idObject="16" reverse="1" type="NodeSpline"/>
|
||||||
|
</nodes>
|
||||||
|
</detail>
|
||||||
|
</details>
|
||||||
|
<groups/>
|
||||||
|
</draw>
|
||||||
|
</pattern>
|
|
@ -52,136 +52,13 @@ const qreal VSAPoint::maxPassmarkLength = (10/*mm*/ / 25.4) * PrintDPI;
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
//---------------------------------------------------------------------------------------------------------------------
|
||||||
// Do we create a point outside of a path?
|
// Do we create a point outside of a path?
|
||||||
inline bool IsOutsidePoint(QPointF p1, QPointF p2, QPointF px)
|
inline bool IsOutsidePoint(QPointF p1, QPointF p2, QPointF px)
|
||||||
{
|
{
|
||||||
return qAbs(QLineF(p1, p2).angle() - QLineF(p1, px).angle()) < 0.001;
|
return qAbs(QLineF(p1, p2).angle() - QLineF(p1, px).angle()) < 0.001;
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------------------------
|
|
||||||
QVector<QPointF> SubPath(const QVector<QPointF> &path, int startIndex, int endIndex)
|
|
||||||
{
|
|
||||||
if (path.isEmpty()
|
|
||||||
|| startIndex < 0 || startIndex >= path.size()
|
|
||||||
|| endIndex < 0 || endIndex >= path.size()
|
|
||||||
|| startIndex == endIndex)
|
|
||||||
{
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVector<QPointF> subPath;
|
|
||||||
int i = startIndex - 1;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
++i;
|
|
||||||
if (i >= path.size())
|
|
||||||
{
|
|
||||||
i = 0;
|
|
||||||
}
|
|
||||||
subPath.append(path.at(i));
|
|
||||||
} while (i != endIndex);
|
|
||||||
|
|
||||||
return subPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------------------------
|
|
||||||
bool Crossing(const QVector<QPointF> &sub1, const QVector<QPointF> &sub2)
|
|
||||||
{
|
|
||||||
if (sub1.isEmpty() || sub2.isEmpty())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QRectF sub1Rect = QPolygonF(sub1).boundingRect();
|
|
||||||
const QRectF sub2Rect = QPolygonF(sub2).boundingRect();
|
|
||||||
if (not sub1Rect.intersects(sub2Rect))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QPainterPath sub1Path;
|
|
||||||
sub1Path.setFillRule(Qt::WindingFill);
|
|
||||||
sub1Path.moveTo(sub1.at(0));
|
|
||||||
for (qint32 i = 1; i < sub1.count(); ++i)
|
|
||||||
{
|
|
||||||
sub1Path.lineTo(sub1.at(i));
|
|
||||||
}
|
|
||||||
sub1Path.lineTo(sub1.at(0));
|
|
||||||
|
|
||||||
QPainterPath sub2Path;
|
|
||||||
sub2Path.setFillRule(Qt::WindingFill);
|
|
||||||
sub2Path.moveTo(sub2.at(0));
|
|
||||||
for (qint32 i = 1; i < sub2.count(); ++i)
|
|
||||||
{
|
|
||||||
sub2Path.lineTo(sub2.at(i));
|
|
||||||
}
|
|
||||||
sub2Path.lineTo(sub2.at(0));
|
|
||||||
|
|
||||||
if (not sub1Path.intersects(sub2Path))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------------------------
|
|
||||||
bool CheckIntersection(const QVector<QPointF> &points, int i, int iNext, int j, int jNext, const QPointF &crossPoint)
|
|
||||||
{
|
|
||||||
QVector<QPointF> sub1 = SubPath(points, iNext, j);
|
|
||||||
sub1.append(crossPoint);
|
|
||||||
sub1 = VAbstractPiece::CorrectEquidistantPoints(sub1, false);
|
|
||||||
const qreal sub1Sum = VAbstractPiece::SumTrapezoids(sub1);
|
|
||||||
|
|
||||||
QVector<QPointF> sub2 = SubPath(points, jNext, i);
|
|
||||||
sub2.append(crossPoint);
|
|
||||||
sub2 = VAbstractPiece::CorrectEquidistantPoints(sub2, false);
|
|
||||||
const qreal sub2Sum = VAbstractPiece::SumTrapezoids(sub2);
|
|
||||||
|
|
||||||
if (sub1Sum < 0 && sub2Sum < 0)
|
|
||||||
{
|
|
||||||
if (Crossing(sub1, sub2))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (not Crossing(sub1, sub2))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------------------------
|
|
||||||
bool ParallelCrossPoint(const QLineF &line1, const QLineF &line2, QPointF &point)
|
|
||||||
{
|
|
||||||
const bool l1p1el2p1 = (line1.p1() == line2.p1());
|
|
||||||
const bool l1p2el2p2 = (line1.p2() == line2.p2());
|
|
||||||
const bool l1p1el2p2 = (line1.p1() == line2.p2());
|
|
||||||
const bool l1p2el2p1 = (line1.p2() == line2.p1());
|
|
||||||
|
|
||||||
if (l1p2el2p2 || l1p2el2p1)
|
|
||||||
{
|
|
||||||
point = line1.p2();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (l1p1el2p1 || l1p1el2p2)
|
|
||||||
{
|
|
||||||
point = line1.p1();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
point = QPointF();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------------------------------------------
|
||||||
Q_DECL_CONSTEXPR qreal PointPosition(const QPointF &p, const QLineF &line)
|
Q_DECL_CONSTEXPR qreal PointPosition(const QPointF &p, const QLineF &line)
|
||||||
{
|
{
|
||||||
|
@ -335,7 +212,11 @@ QVector<QPointF> AngleByIntersection(const QVector<QPointF> &points, QPointF p1,
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
QLineF allowance(p2, px);
|
QLineF allowance(p2, px);
|
||||||
|
//allowance.setLength(allowance.length()-accuracyPointOnLine*2.);
|
||||||
|
//allowance.setAngle(allowance.angle()-0.1); // avoid optimization
|
||||||
pointsIntr.append(allowance.p2());
|
pointsIntr.append(allowance.p2());
|
||||||
|
|
||||||
|
//allowance = QLineF(p2, px);
|
||||||
allowance.setLength(allowance.length() + localWidth * 3.);
|
allowance.setLength(allowance.length() + localWidth * 3.);
|
||||||
pointsIntr.append(allowance.p2());
|
pointsIntr.append(allowance.p2());
|
||||||
pointsIntr.append(bigLine2.p2());
|
pointsIntr.append(bigLine2.p2());
|
||||||
|
@ -652,7 +533,7 @@ qreal AngleBetweenBisectors(const QLineF &b1, const QLineF &b2)
|
||||||
#if !defined(V_NO_ASSERT)
|
#if !defined(V_NO_ASSERT)
|
||||||
// Use for writing tests
|
// Use for writing tests
|
||||||
void DumpVector(const QVector<QPointF> &points)
|
void DumpVector(const QVector<QPointF> &points)
|
||||||
{
|
{
|
||||||
QTemporaryFile temp; // Go to tmp folder to find dump
|
QTemporaryFile temp; // Go to tmp folder to find dump
|
||||||
temp.setAutoRemove(false); // Remove dump manually
|
temp.setAutoRemove(false); // Remove dump manually
|
||||||
if (temp.open())
|
if (temp.open())
|
||||||
|
@ -1031,6 +912,11 @@ QVector<QPointF> VAbstractPiece::Equidistant(QVector<VSAPoint> points, qreal wid
|
||||||
break;
|
break;
|
||||||
case PieceNodeAngle::ByPointsIntersection:
|
case PieceNodeAngle::ByPointsIntersection:
|
||||||
Rollback(QLineF(points.last(), points.at(1)));
|
Rollback(QLineF(points.last(), points.at(1)));
|
||||||
|
if (ekvPoints.size() > 2)
|
||||||
|
{ // Fix for the rule of main path
|
||||||
|
ekvPoints.removeAt(ekvPoints.size()-1);
|
||||||
|
ekvPoints.prepend(ekvPoints.at(ekvPoints.size()-1));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case PieceNodeAngle::BySecondEdgeRightAngle:
|
case PieceNodeAngle::BySecondEdgeRightAngle:
|
||||||
if (not ekvPoints.isEmpty())
|
if (not ekvPoints.isEmpty())
|
||||||
|
@ -1097,7 +983,6 @@ QVector<QPointF> VAbstractPiece::Equidistant(QVector<VSAPoint> points, qreal wid
|
||||||
|
|
||||||
const bool removeFirstAndLast = false;
|
const bool removeFirstAndLast = false;
|
||||||
ekvPoints = CheckLoops(CorrectEquidistantPoints(ekvPoints, removeFirstAndLast));//Result path can contain loops
|
ekvPoints = CheckLoops(CorrectEquidistantPoints(ekvPoints, removeFirstAndLast));//Result path can contain loops
|
||||||
ekvPoints = CheckLoops(CorrectEquidistantPoints(ekvPoints, removeFirstAndLast));//Result path can contain loops
|
|
||||||
// DumpVector(ekvPoints); // Uncomment for dumping test data
|
// DumpVector(ekvPoints); // Uncomment for dumping test data
|
||||||
return ekvPoints;
|
return ekvPoints;
|
||||||
}
|
}
|
||||||
|
@ -1146,6 +1031,8 @@ qreal VAbstractPiece::SumTrapezoids(const QVector<QPointF> &points)
|
||||||
*/
|
*/
|
||||||
QVector<QPointF> VAbstractPiece::CheckLoops(const QVector<QPointF> &points)
|
QVector<QPointF> VAbstractPiece::CheckLoops(const QVector<QPointF> &points)
|
||||||
{
|
{
|
||||||
|
// DumpVector(points); // Uncomment for dumping test data
|
||||||
|
|
||||||
int count = points.size();
|
int count = points.size();
|
||||||
/*If we got less than 4 points no need seek loops.*/
|
/*If we got less than 4 points no need seek loops.*/
|
||||||
if (count < 4)
|
if (count < 4)
|
||||||
|
@ -1164,8 +1051,8 @@ QVector<QPointF> VAbstractPiece::CheckLoops(const QVector<QPointF> &points)
|
||||||
qint32 i, j, jNext = 0;
|
qint32 i, j, jNext = 0;
|
||||||
for (i = 0; i < count; ++i)
|
for (i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
/*Last three points no need check.*/
|
/*Last three points no need to check.*/
|
||||||
/*Triangle has not contain loops*/
|
/*Triangle can not contain a loop*/
|
||||||
if (i > count-3)
|
if (i > count-3)
|
||||||
{
|
{
|
||||||
ekvPoints.append(points.at(i));
|
ekvPoints.append(points.at(i));
|
||||||
|
@ -1206,54 +1093,24 @@ QVector<QPointF> VAbstractPiece::CheckLoops(const QVector<QPointF> &points)
|
||||||
// For closed path last point is equal to first. Using index of the first.
|
// For closed path last point is equal to first. Using index of the first.
|
||||||
pathClosed && jNext == count-1 ? AddUniqueIndex(0) : AddUniqueIndex(jNext);
|
pathClosed && jNext == count-1 ? AddUniqueIndex(0) : AddUniqueIndex(jNext);
|
||||||
|
|
||||||
const QLineF::IntersectType intersect = line1.intersect(line2, &crosPoint);
|
if (uniqueVertices.size() == 4)
|
||||||
if (intersect == QLineF::NoIntersection)
|
{// Lines are not neighbors
|
||||||
{ // According to the documentation QLineF::NoIntersection indicates that the lines do not intersect;
|
const QLineF::IntersectType intersect = line1.intersect(line2, &crosPoint);
|
||||||
// i.e. they are parallel. But parallel also mean they can be on the same line.
|
if (intersect == QLineF::NoIntersection)
|
||||||
// Method IsPointOnLineviaPDP will check it.
|
{ // According to the documentation QLineF::NoIntersection indicates that the lines do not intersect;
|
||||||
if (VGObject::IsPointOnLineviaPDP(points.at(j), points.at(i), points.at(i+1))
|
// i.e. they are parallel. But parallel also mean they can be on the same line.
|
||||||
// Lines are not neighbors
|
// Method IsLineSegmentOnLineSegment will check it.
|
||||||
&& uniqueVertices.size() == 4)
|
if (VGObject::IsLineSegmentOnLineSegment(line1, line2))
|
||||||
{
|
{// Now we really sure that segments are on the same line and have real intersections.
|
||||||
// Left to catch case where segments are on the same line, but do not have real intersections.
|
status = ParallelIntersection;
|
||||||
QLineF tmpLine1 = line1;
|
|
||||||
QLineF tmpLine2 = line2;
|
|
||||||
|
|
||||||
tmpLine1.setAngle(tmpLine1.angle()+90);
|
|
||||||
|
|
||||||
QPointF tmpCrosPoint;
|
|
||||||
const QLineF::IntersectType tmpIntrs1 = tmpLine1.intersect(tmpLine2, &tmpCrosPoint);
|
|
||||||
|
|
||||||
tmpLine1 = line1;
|
|
||||||
tmpLine2.setAngle(tmpLine2.angle()+90);
|
|
||||||
|
|
||||||
const QLineF::IntersectType tmpIntrs2 = tmpLine1.intersect(tmpLine2, &tmpCrosPoint);
|
|
||||||
|
|
||||||
if (tmpIntrs1 == QLineF::BoundedIntersection || tmpIntrs2 == QLineF::BoundedIntersection)
|
|
||||||
{ // Now we really sure that lines are on the same lines and have real intersections.
|
|
||||||
QPointF cPoint;
|
|
||||||
const bool caseFlag = ParallelCrossPoint(line1, line2, cPoint);
|
|
||||||
if (not caseFlag || CheckIntersection(points, i, i+1, j, jNext, cPoint))
|
|
||||||
{
|
|
||||||
status = ParallelIntersection;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (intersect == QLineF::BoundedIntersection)
|
|
||||||
{
|
|
||||||
if (uniqueVertices.size() == 4)
|
|
||||||
{ // Break, but not if lines are neighbors
|
|
||||||
if ((line1.p1() != crosPoint
|
|
||||||
&& line1.p2() != crosPoint
|
|
||||||
&& line2.p1() != crosPoint
|
|
||||||
&& line2.p2() != crosPoint) || CheckIntersection(points, i, i+1, j, jNext, crosPoint))
|
|
||||||
{
|
|
||||||
status = BoundedIntersection;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (intersect == QLineF::BoundedIntersection)
|
||||||
|
{
|
||||||
|
status = BoundedIntersection;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
status = NoIntersection;
|
status = NoIntersection;
|
||||||
}
|
}
|
||||||
|
@ -1278,7 +1135,8 @@ QVector<QPointF> VAbstractPiece::CheckLoops(const QVector<QPointF> &points)
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// DumpVector(ekvPoints); // Uncomment for dumping test data
|
||||||
return ekvPoints;
|
return ekvPoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -168,6 +168,9 @@ private:
|
||||||
QVector<VSAPoint> InputPointsIssue923Test6_6();
|
QVector<VSAPoint> InputPointsIssue923Test6_6();
|
||||||
QVector<QPointF> OutputPointsIssue923Test6_6();
|
QVector<QPointF> OutputPointsIssue923Test6_6();
|
||||||
|
|
||||||
|
QVector<VSAPoint> InputLoopByIntersectionTest();
|
||||||
|
QVector<QPointF> OutputLoopByIntersectionTest();
|
||||||
|
|
||||||
QVector<VSAPoint> InputPointsIssue937Case1() const;
|
QVector<VSAPoint> InputPointsIssue937Case1() const;
|
||||||
QVector<QPointF> OutputPointsIssue937Case1() const;
|
QVector<QPointF> OutputPointsIssue937Case1() const;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user