Fix issues with elliptical arc.
This commit is contained in:
parent
9055f10658
commit
b01fd72af9
|
@ -47,6 +47,10 @@
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
constexpr qreal tolerance = accuracyPointOnLine/8;
|
constexpr qreal tolerance = accuracyPointOnLine/8;
|
||||||
|
|
||||||
|
// Because of overflow we cannot generate arcs more than maxRadius
|
||||||
|
constexpr int maxRadius = 10000;
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------------------------------------------
|
||||||
auto VLen(fpm::fixed_16_16 x, fpm::fixed_16_16 y) -> fpm::fixed_16_16
|
auto VLen(fpm::fixed_16_16 x, fpm::fixed_16_16 y) -> fpm::fixed_16_16
|
||||||
{
|
{
|
||||||
|
@ -201,6 +205,27 @@ auto EllipticArcPoints(QPointF c, qreal radius1, qreal radius2, qreal astart, qr
|
||||||
|
|
||||||
return arc;
|
return arc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------------------------
|
||||||
|
auto JoinVectors(const QVector<QPointF> &v1, const QVector<QPointF> &v2) -> QVector<QPointF>
|
||||||
|
{
|
||||||
|
QVector<QPointF> v;
|
||||||
|
v.reserve(v1.size() + v2.size());
|
||||||
|
|
||||||
|
v = v1;
|
||||||
|
|
||||||
|
constexpr qreal accuracy = (0.0001/*mm*/ / 25.4) * PrintDPI;
|
||||||
|
|
||||||
|
for (auto p : v2)
|
||||||
|
{
|
||||||
|
if (not VFuzzyComparePoints(ConstLast(v), p, accuracy))
|
||||||
|
{
|
||||||
|
v.append(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------------------------------------------
|
||||||
|
@ -326,6 +351,7 @@ auto VEllipticalArc::Rotate(QPointF originPoint, qreal degrees, const QString &p
|
||||||
elArc.SetPenStyle(GetPenStyle());
|
elArc.SetPenStyle(GetPenStyle());
|
||||||
elArc.SetFlipped(IsFlipped());
|
elArc.SetFlipped(IsFlipped());
|
||||||
elArc.SetTransform(t);
|
elArc.SetTransform(t);
|
||||||
|
elArc.SetApproximationScale(GetApproximationScale());
|
||||||
return elArc;
|
return elArc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,6 +371,7 @@ auto VEllipticalArc::Flip(const QLineF &axis, const QString &prefix) const -> VE
|
||||||
elArc.SetPenStyle(GetPenStyle());
|
elArc.SetPenStyle(GetPenStyle());
|
||||||
elArc.SetFlipped(not IsFlipped());
|
elArc.SetFlipped(not IsFlipped());
|
||||||
elArc.SetTransform(d->m_transform * VGObject::FlippingMatrix(d->m_transform.inverted().map(axis)));
|
elArc.SetTransform(d->m_transform * VGObject::FlippingMatrix(d->m_transform.inverted().map(axis)));
|
||||||
|
elArc.SetApproximationScale(GetApproximationScale());
|
||||||
return elArc;
|
return elArc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -373,6 +400,7 @@ auto VEllipticalArc::Move(qreal length, qreal angle, const QString &prefix) cons
|
||||||
elArc.SetPenStyle(GetPenStyle());
|
elArc.SetPenStyle(GetPenStyle());
|
||||||
elArc.SetFlipped(IsFlipped());
|
elArc.SetFlipped(IsFlipped());
|
||||||
elArc.SetTransform(t);
|
elArc.SetTransform(t);
|
||||||
|
elArc.SetApproximationScale(GetApproximationScale());
|
||||||
return elArc;
|
return elArc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -448,12 +476,32 @@ auto VEllipticalArc::GetPoints() const -> QVector<QPointF>
|
||||||
{
|
{
|
||||||
const QPointF center = VAbstractArc::GetCenter().toQPointF();
|
const QPointF center = VAbstractArc::GetCenter().toQPointF();
|
||||||
|
|
||||||
|
// Don't work with 0 radius. Always make it bigger than 0.
|
||||||
|
constexpr qreal threshold = ToPixel(0.001, Unit::Mm);
|
||||||
|
qreal radius1 = qMax(d->radius1, threshold);
|
||||||
|
qreal radius2 = qMax(d->radius2, threshold);
|
||||||
|
qreal max = qMax(d->radius1, d->radius2);
|
||||||
|
qreal scale = 1;
|
||||||
|
|
||||||
|
if (max > maxRadius)
|
||||||
|
{
|
||||||
|
scale = max / maxRadius;
|
||||||
|
radius1 /= scale;
|
||||||
|
radius2 /= scale;
|
||||||
|
}
|
||||||
|
|
||||||
// Generate complete ellipse because angles are not correct and have to be fixed manually
|
// Generate complete ellipse because angles are not correct and have to be fixed manually
|
||||||
QVector<QPointF> points = EllipticArcPoints(center, d->radius1, d->radius2, 0.0, M_2PI, GetApproximationScale());
|
QVector<QPointF> points = EllipticArcPoints(center, radius1, radius2, 0.0, M_2PI, GetApproximationScale());
|
||||||
points = ArcPoints(points);
|
points = ArcPoints(points);
|
||||||
|
|
||||||
QTransform t = d->m_transform;
|
QTransform t = d->m_transform;
|
||||||
t.translate(center.x(), center.y());
|
t.translate(center.x(), center.y());
|
||||||
|
if (not VFuzzyComparePossibleNulls(scale, 1))
|
||||||
|
{
|
||||||
|
// Because fixed 16.16 type has limitations it is very easy to get overflow error.
|
||||||
|
// To avoid this we calculate an arc for scaled radiuses and then scale up to original size.
|
||||||
|
t.scale(scale, scale);
|
||||||
|
}
|
||||||
t.rotate(-GetRotationAngle());
|
t.rotate(-GetRotationAngle());
|
||||||
t.translate(-center.x(), -center.y());
|
t.translate(-center.x(), -center.y());
|
||||||
|
|
||||||
|
@ -693,12 +741,12 @@ auto VEllipticalArc::GetP(qreal angle) const -> QPointF
|
||||||
//---------------------------------------------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------------------------------------------
|
||||||
auto VEllipticalArc::ArcPoints(QVector<QPointF> points) const -> QVector<QPointF>
|
auto VEllipticalArc::ArcPoints(QVector<QPointF> points) const -> QVector<QPointF>
|
||||||
{
|
{
|
||||||
if (points.size() < 2 || VFuzzyComparePossibleNulls(VAbstractArc::GetStartAngle(), VAbstractArc::GetEndAngle()))
|
if (points.size() < 2 || (qFuzzyIsNull(d->radius1) && qFuzzyIsNull(d->radius2)))
|
||||||
{
|
{
|
||||||
return points;
|
return points;
|
||||||
}
|
}
|
||||||
|
|
||||||
QPointF center = static_cast<QPointF>(GetCenter());
|
QPointF center = VAbstractArc::GetCenter().toQPointF();
|
||||||
qreal radius = qMax(d->radius1, d->radius2) * 2;
|
qreal radius = qMax(d->radius1, d->radius2) * 2;
|
||||||
|
|
||||||
QLineF start(center.x(), center.y(), center.x() + radius, center.y());
|
QLineF start(center.x(), center.y(), center.x() + radius, center.y());
|
||||||
|
@ -707,22 +755,18 @@ auto VEllipticalArc::ArcPoints(QVector<QPointF> points) const -> QVector<QPointF
|
||||||
QLineF end(center.x(), center.y(), center.x() + radius, center.y());
|
QLineF end(center.x(), center.y(), center.x() + radius, center.y());
|
||||||
end.setAngle(VAbstractArc::GetEndAngle());
|
end.setAngle(VAbstractArc::GetEndAngle());
|
||||||
|
|
||||||
if (VFuzzyComparePossibleNulls(start.angle(), end.angle()))
|
|
||||||
{
|
|
||||||
return points;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto IsBoundedIntersection = [](QLineF::IntersectType type, QPointF p, const QLineF &segment1,
|
auto IsBoundedIntersection = [](QLineF::IntersectType type, QPointF p, const QLineF &segment1,
|
||||||
const QLineF &segment2)
|
const QLineF &segment2)
|
||||||
{
|
{
|
||||||
return type == QLineF::BoundedIntersection ||
|
return type == QLineF::BoundedIntersection ||
|
||||||
(VGObject::IsPointOnLineSegment (p, segment1.p1(), segment2.p1()) &&
|
(type == QLineF::UnboundedIntersection &&
|
||||||
|
VGObject::IsPointOnLineSegment (p, segment1.p1(), segment2.p1()) &&
|
||||||
VGObject::IsPointOnLineSegment (p, segment2.p1(), segment2.p2()));
|
VGObject::IsPointOnLineSegment (p, segment2.p1(), segment2.p2()));
|
||||||
};
|
};
|
||||||
|
|
||||||
bool begin = true;
|
bool begin = true;
|
||||||
|
|
||||||
if (start.angle() > end.angle())
|
if (start.angle() >= end.angle())
|
||||||
{
|
{
|
||||||
for (int i=0; i < points.size()-1; ++i)
|
for (int i=0; i < points.size()-1; ++i)
|
||||||
{
|
{
|
||||||
|
@ -736,8 +780,16 @@ auto VEllipticalArc::ArcPoints(QVector<QPointF> points) const -> QVector<QPointF
|
||||||
{
|
{
|
||||||
QVector<QPointF> head = points.mid(0, i+1);
|
QVector<QPointF> head = points.mid(0, i+1);
|
||||||
QVector<QPointF> tail = points.mid(i+1, -1);
|
QVector<QPointF> tail = points.mid(i+1, -1);
|
||||||
tail.prepend(p);
|
|
||||||
points = tail + head;
|
tail = JoinVectors({p}, tail);
|
||||||
|
points = JoinVectors(tail, head);
|
||||||
|
points = JoinVectors(points, {p});
|
||||||
|
|
||||||
|
if (VFuzzyComparePossibleNulls(start.angle(), end.angle()))
|
||||||
|
{
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
begin = false;
|
begin = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -282,6 +282,7 @@ void TST_VEllipticalArc::TestGetPoints1()
|
||||||
|
|
||||||
const VPointF center;
|
const VPointF center;
|
||||||
VEllipticalArc arc(center, radius1, radius2, startAngle, endAngle, rotationAngle);
|
VEllipticalArc arc(center, radius1, radius2, startAngle, endAngle, rotationAngle);
|
||||||
|
arc.SetApproximationScale(maxCurveApproximationScale);
|
||||||
|
|
||||||
QVector<QPointF> points = arc.GetPoints();
|
QVector<QPointF> points = arc.GetPoints();
|
||||||
if (qFuzzyIsNull(rotationAngle))
|
if (qFuzzyIsNull(rotationAngle))
|
||||||
|
@ -313,6 +314,7 @@ void TST_VEllipticalArc::TestGetPoints2()
|
||||||
|
|
||||||
const VPointF center;
|
const VPointF center;
|
||||||
VEllipticalArc arc(center, radius1, radius2, startAngle, endAngle, rotationAngle);
|
VEllipticalArc arc(center, radius1, radius2, startAngle, endAngle, rotationAngle);
|
||||||
|
arc.SetApproximationScale(maxCurveApproximationScale);
|
||||||
QVector<QPointF> points = arc.GetPoints();
|
QVector<QPointF> points = arc.GetPoints();
|
||||||
|
|
||||||
const qreal c = qSqrt(qAbs(radius2*radius2 - radius1*radius1));
|
const qreal c = qSqrt(qAbs(radius2*radius2 - radius1*radius1));
|
||||||
|
@ -385,6 +387,7 @@ void TST_VEllipticalArc::TestGetPoints3()
|
||||||
|
|
||||||
const VPointF center;
|
const VPointF center;
|
||||||
VEllipticalArc arc(center, radius1, radius2, startAngle, endAngle, rotationAngle);
|
VEllipticalArc arc(center, radius1, radius2, startAngle, endAngle, rotationAngle);
|
||||||
|
arc.SetApproximationScale(maxCurveApproximationScale);
|
||||||
QVector<QPointF> points = arc.GetPoints();
|
QVector<QPointF> points = arc.GetPoints();
|
||||||
|
|
||||||
if (VFuzzyComparePossibleNulls(arc.AngleArc(), 360.0))
|
if (VFuzzyComparePossibleNulls(arc.AngleArc(), 360.0))
|
||||||
|
@ -413,12 +416,13 @@ void TST_VEllipticalArc::TestGetPoints4()
|
||||||
|
|
||||||
const VPointF center;
|
const VPointF center;
|
||||||
VEllipticalArc arc(center, radius1, radius2, startAngle, endAngle, rotationAngle);
|
VEllipticalArc arc(center, radius1, radius2, startAngle, endAngle, rotationAngle);
|
||||||
|
arc.SetApproximationScale(maxCurveApproximationScale);
|
||||||
|
|
||||||
if (VFuzzyComparePossibleNulls(arc.AngleArc(), 360.0))
|
if (VFuzzyComparePossibleNulls(arc.AngleArc(), 360.0))
|
||||||
{// calculated full ellipse length
|
{// calculated full ellipse length
|
||||||
const qreal h = ((radius1-radius2)*(radius1-radius2))/((radius1+radius2)*(radius1+radius2));
|
const qreal h = ((radius1-radius2)*(radius1-radius2))/((radius1+radius2)*(radius1+radius2));
|
||||||
const qreal ellipseLength = M_PI*(radius1+radius2)*(1+3*h/(10+qSqrt(4-3*h)));
|
const qreal ellipseLength = M_PI*(radius1+radius2)*(1+3*h/(10+qSqrt(4-3*h)));
|
||||||
const qreal epsLength = ellipseLength*0.5/100; // computing error
|
const qreal epsLength = ToPixel(1, Unit::Mm); // computing error
|
||||||
const qreal arcLength = VEllipticalArc(center, radius1, radius2, 0, 360, 0).GetLength();
|
const qreal arcLength = VEllipticalArc(center, radius1, radius2, 0, 360, 0).GetLength();
|
||||||
const qreal diffLength = qAbs(arcLength - ellipseLength);
|
const qreal diffLength = qAbs(arcLength - ellipseLength);
|
||||||
// cppcheck-suppress unreadVariable
|
// cppcheck-suppress unreadVariable
|
||||||
|
@ -446,6 +450,7 @@ void TST_VEllipticalArc::TestGetPoints5()
|
||||||
|
|
||||||
const VPointF center;
|
const VPointF center;
|
||||||
VEllipticalArc arc(center, radius1, radius2, startAngle, endAngle, rotationAngle);
|
VEllipticalArc arc(center, radius1, radius2, startAngle, endAngle, rotationAngle);
|
||||||
|
arc.SetApproximationScale(maxCurveApproximationScale);
|
||||||
|
|
||||||
const qreal stAngle = VEllipticalArc::OptimizeAngle(arc.GetStartAngle()+arc.GetRotationAngle());
|
const qreal stAngle = VEllipticalArc::OptimizeAngle(arc.GetStartAngle()+arc.GetRotationAngle());
|
||||||
const qreal enAngle = VEllipticalArc::OptimizeAngle(arc.GetEndAngle()+arc.GetRotationAngle());
|
const qreal enAngle = VEllipticalArc::OptimizeAngle(arc.GetEndAngle()+arc.GetRotationAngle());
|
||||||
|
@ -471,7 +476,7 @@ void TST_VEllipticalArc::TestGetPoints5()
|
||||||
|
|
||||||
if (points.size() > 2 && qFuzzyIsNull(rotationAngle))
|
if (points.size() > 2 && qFuzzyIsNull(rotationAngle))
|
||||||
{
|
{
|
||||||
const qreal testAccuracy = (1.5/*mm*/ / 25.4) * PrintDPI;
|
const qreal testAccuracy = ToPixel(1.5, Unit::Mm);
|
||||||
Comparison(arc.GetP1(), ConstFirst(points), testAccuracy);
|
Comparison(arc.GetP1(), ConstFirst(points), testAccuracy);
|
||||||
Comparison(arc.GetP2(), ConstLast(points), testAccuracy);
|
Comparison(arc.GetP2(), ConstLast(points), testAccuracy);
|
||||||
|
|
||||||
|
@ -607,8 +612,9 @@ void TST_VEllipticalArc::EmptyArc()
|
||||||
QFETCH(qreal, length);
|
QFETCH(qreal, length);
|
||||||
|
|
||||||
VEllipticalArc empty;
|
VEllipticalArc empty;
|
||||||
|
empty.SetApproximationScale(maxCurveApproximationScale);
|
||||||
empty.SetRadius1(radius1);
|
empty.SetRadius1(radius1);
|
||||||
empty.SetRadius2(radius2);
|
empty.SetRadius2(radius2);
|
||||||
|
|
||||||
QCOMPARE(empty.GetLength(), length);
|
QVERIFY(qAbs(empty.GetLength() - length) <= ToPixel(1, Unit::Mm));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user