From 3e520c85722a72afaea48bc2c13b2b50dea9a207 Mon Sep 17 00:00:00 2001 From: DSCaskey <31944718+DSCaskey@users.noreply.github.com> Date: Sun, 2 Jan 2022 05:34:05 -0500 Subject: [PATCH] Fix: issue #376 elliptical arcs do not mirror Fix elliptical arc tests Fixes issue#376 Elliptical arcs do not mirror correctly This fixes the transform to get the proper start and end angles when mirroring elliptical arcs. --- src/libs/vgeometry/vabstractarc.h | 2 +- src/libs/vgeometry/vellipticalarc.cpp | 286 ++++++------------- src/libs/vgeometry/vellipticalarc.h | 94 +++--- src/libs/vgeometry/vellipticalarc_p.h | 57 ++-- src/libs/vgeometry/vgobject.cpp | 2 +- src/libs/vmisc/def.h | 13 +- src/libs/vtest/abstracttest.cpp | 30 +- src/libs/vtest/abstracttest.h | 1 + src/libs/vtest/vtest.pro | 2 +- src/test/Seamly2DTest/tst_vellipticalarc.cpp | 81 ++++-- src/test/Seamly2DTest/tst_vellipticalarc.h | 4 +- 11 files changed, 274 insertions(+), 298 deletions(-) diff --git a/src/libs/vgeometry/vabstractarc.h b/src/libs/vgeometry/vabstractarc.h index 3b125b19f786..c3257e7d2359 100644 --- a/src/libs/vgeometry/vabstractarc.h +++ b/src/libs/vgeometry/vabstractarc.h @@ -94,7 +94,7 @@ class VAbstractArc : public VAbstractCurve void SetFormulaF2 (const QString &formula, qreal value); virtual qreal GetEndAngle () const Q_DECL_OVERRIDE; - VPointF GetCenter () const; + virtual VPointF GetCenter () const; void SetCenter (const VPointF &point); QString GetFormulaLength () const; diff --git a/src/libs/vgeometry/vellipticalarc.cpp b/src/libs/vgeometry/vellipticalarc.cpp index e5892cf2238f..6c8ded628ded 100644 --- a/src/libs/vgeometry/vellipticalarc.cpp +++ b/src/libs/vgeometry/vellipticalarc.cpp @@ -30,6 +30,7 @@ #include #include +#include #include "../vmisc/def.h" #include "../vmisc/vmath.h" @@ -131,59 +132,57 @@ VEllipticalArc &VEllipticalArc::operator =(const VEllipticalArc &arc) } //--------------------------------------------------------------------------------------------------------------------- -VEllipticalArc VEllipticalArc::Rotate(const QPointF &originPoint, qreal degrees, const QString &prefix) const +VEllipticalArc VEllipticalArc::Rotate(QPointF originPoint, qreal degrees, const QString &prefix) const { - const VPointF center = GetCenter().Rotate(originPoint, degrees); + originPoint = d->m_transform.inverted().map(originPoint); - const QPointF p1 = VPointF::RotatePF(originPoint, GetP1(), degrees); - const QPointF p2 = VPointF::RotatePF(originPoint, GetP2(), degrees); + QTransform t = d->m_transform; + t.translate(originPoint.x(), originPoint.y()); + t.rotate(-degrees); + t.translate(-originPoint.x(), -originPoint.y()); - const qreal f1 = QLineF(static_cast(center), p1).angle() - GetRotationAngle(); - const qreal f2 = QLineF(static_cast(center), p2).angle() - GetRotationAngle(); - - VEllipticalArc elArc(center, GetRadius1(), GetRadius2(), f1, f2, GetRotationAngle()); + VEllipticalArc elArc(VAbstractArc::GetCenter(), GetRadius1(), GetRadius2(), VAbstractArc::GetStartAngle(), + VAbstractArc::GetEndAngle(), GetRotationAngle()); elArc.setName(name() + prefix); elArc.SetColor(GetColor()); elArc.SetPenStyle(GetPenStyle()); elArc.SetFlipped(IsFlipped()); + elArc.setTransform(t); return elArc; } //--------------------------------------------------------------------------------------------------------------------- VEllipticalArc VEllipticalArc::Flip(const QLineF &axis, const QString &prefix) const { - const VPointF center = GetCenter().Flip(axis); - - const QPointF p1 = VPointF::FlipPF(axis, GetP1()); - const QPointF p2 = VPointF::FlipPF(axis, GetP2()); - - const qreal f1 = QLineF(static_cast(center), p1).angle() - GetRotationAngle(); - const qreal f2 = QLineF(static_cast(center), p2).angle() - GetRotationAngle(); - - VEllipticalArc elArc(center, GetRadius1(), GetRadius2(), f1, f2, GetRotationAngle()); + VEllipticalArc elArc(VAbstractArc::GetCenter(), GetRadius1(), GetRadius2(), VAbstractArc::GetStartAngle(), + VAbstractArc::GetEndAngle(), GetRotationAngle()); elArc.setName(name() + prefix); elArc.SetColor(GetColor()); elArc.SetPenStyle(GetPenStyle()); elArc.SetFlipped(not IsFlipped()); + elArc.setTransform(d->m_transform * VGObject::flipTransform(d->m_transform.inverted().map(axis))); return elArc; } //--------------------------------------------------------------------------------------------------------------------- VEllipticalArc VEllipticalArc::Move(qreal length, qreal angle, const QString &prefix) const { - const VPointF center = GetCenter().Move(length, angle); + const VPointF oldCenter = VAbstractArc::GetCenter(); + const VPointF center = oldCenter.Move(length, angle); - const QPointF p1 = VPointF::MovePF(GetP1(), length, angle); - const QPointF p2 = VPointF::MovePF(GetP2(), length, angle); + const QPointF position = d->m_transform.inverted().map(center.toQPointF()) - + d->m_transform.inverted().map(oldCenter.toQPointF()); - const qreal f1 = QLineF(static_cast(center), p1).angle() - GetRotationAngle(); - const qreal f2 = QLineF(static_cast(center), p2).angle() - GetRotationAngle(); + QTransform t = d->m_transform; + t.translate(position.x(), position.y()); - VEllipticalArc elArc(center, GetRadius1(), GetRadius2(), f1, f2, GetRotationAngle()); + VEllipticalArc elArc(oldCenter, GetRadius1(), GetRadius2(), VAbstractArc::GetStartAngle(), + VAbstractArc::GetEndAngle(), GetRotationAngle()); elArc.setName(name() + prefix); elArc.SetColor(GetColor()); elArc.SetPenStyle(GetPenStyle()); elArc.SetFlipped(IsFlipped()); + elArc.setTransform(t); return elArc; } @@ -215,7 +214,7 @@ qreal VEllipticalArc::GetLength() const */ QPointF VEllipticalArc::GetP1() const { - return GetPoint(GetStartAngle()); + return getTransform().map(getPoint(VAbstractArc::GetStartAngle())); } //--------------------------------------------------------------------------------------------------------------------- @@ -225,169 +224,53 @@ QPointF VEllipticalArc::GetP1() const */ QPointF VEllipticalArc::GetP2 () const { - return GetPoint(GetEndAngle()); + return getTransform().map(getPoint(VAbstractArc::GetEndAngle())); } //--------------------------------------------------------------------------------------------------------------------- -/** - * @brief GetPoint return point associated with angle. - * @return point. - */ -QPointF VEllipticalArc::GetPoint (qreal angle) const -{ - // Original idea http://alex-black.ru/article.php?content=109#head_3 - if (angle > 360 || angle < 0) - {// Filter incorect value of angle - QLineF dummy(0, 0, 100, 0); - dummy.setAngle(angle); - angle = dummy.angle(); - } - - // p - point without rotation - qreal x = 0; - qreal y = 0; - - qreal angleRad = qDegreesToRadians(angle); - const int n = GetQuadransRad(angleRad); - if (VFuzzyComparePossibleNulls(angleRad, 0) || VFuzzyComparePossibleNulls(angleRad, M_2PI) || - VFuzzyComparePossibleNulls(angleRad, -M_2PI)) - { // 0 (360, -360) degress - x = d->radius1; - y = 0; - } - else if (VFuzzyComparePossibleNulls(angleRad, M_PI_2) || VFuzzyComparePossibleNulls(angleRad, -3 * M_PI_2)) - { // 90 (-270) degress - x = 0; - y = d->radius2; - } - else if (VFuzzyComparePossibleNulls(angleRad, M_PI) || VFuzzyComparePossibleNulls(angleRad, -M_PI)) - { // 180 (-180) degress - x = -d->radius1; - y = 0; - } - else if (VFuzzyComparePossibleNulls(angleRad, 3 * M_PI_2) || VFuzzyComparePossibleNulls(angleRad, -M_PI_2)) - { // 270 (-90) degress - x = 0; - y = -d->radius2; - } - else - { // cases between - const qreal r1Pow = qPow(d->radius1, 2); - const qreal r2Pow = qPow(d->radius2, 2); - const qreal angleTan = qTan(angleRad); - const qreal angleTan2 = qPow(angleTan, 2); - x = qSqrt((r1Pow * r2Pow) / (r1Pow * angleTan2 + r2Pow)); - y = angleTan * x; - } - - switch (n) - { - case 1: - x = +x; - y = +y; - break; - case 2: - x = -x; - y = +y; - break; - case 3: - x = -x; - y = -y; - break; - case 4: - x = +x; - y = -y; - break; - default: - break; - } - - QPointF p (GetCenter().x() + x, GetCenter().y() + y); - // rotation of point - QLineF line(static_cast(GetCenter()), p); - line.setAngle(line.angle() + GetRotationAngle()); - - return line.p2(); +QTransform VEllipticalArc::getTransform() const +{ + return d->m_transform; } //--------------------------------------------------------------------------------------------------------------------- -int VEllipticalArc::GetQuadransRad(qreal &rad) +void VEllipticalArc::setTransform(const QTransform &matrix, bool combine) { - if (rad > M_PI) - { - rad = rad - M_2PI; - } - - if (rad < -M_PI) - { - rad = rad + M_2PI; - } + d->m_transform = combine ? d->m_transform * matrix : matrix; +} - int n = 0; - if (rad >= 0) - { - if (rad <= M_PI_2) - { - n = 1; - rad = -rad; - } - else if (rad > M_PI_2 && rad <= M_PI) - { - n = 2; - rad = M_PI+rad; - } - } - else - { - if (rad >= -M_PI_2) - { - n = 4; - } - else if (rad < -M_PI_2 && rad >= -M_PI) - { - n = 3; - rad = M_PI-rad; - } - } - return n; +//--------------------------------------------------------------------------------------------------------------------- +VPointF VEllipticalArc::GetCenter() const +{ + VPointF center = VAbstractArc::GetCenter(); + const QPointF p = d->m_transform.map(center.toQPointF()); + center.setX(p.x()); + center.setY(p.y()); + return center; } + //--------------------------------------------------------------------------------------------------------------------- /** - * @brief GetAngles return list of angles needed for drawing arc. - * @return list of angles + * @brief GetPoint return point associated with angle. + * @return point. */ -QVector VEllipticalArc::GetAngles() const + //--------------------------------------------------------------------------------------------------------------------- +QPointF VEllipticalArc::getPoint(qreal angle) const { - QVector sectionAngle; - qreal angle = AngleArc(); + QLineF line(0, 0, 100, 0); + line.setAngle(angle); - if (qFuzzyIsNull(angle)) - {// Return the array that includes one angle - sectionAngle.append(GetStartAngle()); - return sectionAngle; - } + const qreal a = line.p2().x() / GetRadius1(); + const qreal b = line.p2().y() / GetRadius2(); + const qreal k = qSqrt(a*a + b*b); + QPointF p(line.p2().x() / k, line.p2().y() / k); - if (angle > 360 || angle < 0) - {// Filter incorect value of angle - QLineF dummy(0,0, 100, 0); - dummy.setAngle(angle); - angle = dummy.angle(); - } + QLineF line2(QPointF(), p); + SCASSERT(VFuzzyComparePossibleNulls(line2.angle(), line.angle())) - const qreal angleInterpolation = 45; //degree - const int sections = qFloor(angle / angleInterpolation); - for (int i = 0; i < sections; ++i) - { - sectionAngle.append(angleInterpolation); - } - - const qreal tail = angle - sections * angleInterpolation; - if (tail > 0) - { - sectionAngle.append(tail); - } - return sectionAngle; + line2.setAngle(line2.angle() + GetRotationAngle()); + return line2.p2() + VAbstractArc::GetCenter().toQPointF(); } //--------------------------------------------------------------------------------------------------------------------- @@ -397,34 +280,55 @@ QVector VEllipticalArc::GetAngles() const */ QVector VEllipticalArc::GetPoints() const { - QVector points; - QVector sectionAngle = GetAngles(); + const QPointF center = VAbstractArc::GetCenter().toQPointF(); + QRectF box(center.x() - d->radius1, center.y() - d->radius2, d->radius1*2, d->radius2*2); + + QLineF startLine(center.x(), center.y(), center.x() + d->radius1, center.y()); + QLineF endLine = startLine; - qreal currentAngle; - IsFlipped() ? currentAngle = GetEndAngle() : currentAngle = GetStartAngle(); - for (int i = 0; i < sectionAngle.size(); ++i) + startLine.setAngle(VAbstractArc::GetStartAngle()); + endLine.setAngle(VAbstractArc::GetEndAngle()); + qreal sweepAngle = startLine.angleTo(endLine); + + if (qFuzzyIsNull(sweepAngle)) { - QPointF startPoint = GetPoint(currentAngle); - QPointF ellipsePoint2 = GetPoint(currentAngle + sectionAngle.at(i)/3); - QPointF ellipsePoint3 = GetPoint(currentAngle + 2*sectionAngle.at(i)/3); - QPointF lastPoint = GetPoint(currentAngle + sectionAngle.at(i)); - // four points that are on ellipse + sweepAngle = 360; + } - QPointF bezierPoint1 = ( -5*startPoint + 18*ellipsePoint2 -9*ellipsePoint3 + 2*lastPoint )/6; - QPointF bezierPoint2 = ( 2*startPoint - 9*ellipsePoint2 + 18*ellipsePoint3 - 5*lastPoint )/6; + QPainterPath path; + path.arcTo(box, VAbstractArc::GetStartAngle(), sweepAngle); - VSpline spl(VPointF(startPoint), bezierPoint1, bezierPoint2, VPointF(lastPoint), 1.0); + QTransform t = d->m_transform; + t.translate(center.x(), center.y()); + t.rotate(-GetRotationAngle()); + t.translate(-center.x(), -center.y()); - QVector splPoints = spl.GetPoints(); + path = t.map(path); - if (not splPoints.isEmpty() && i != sectionAngle.size() - 1) + QPolygonF polygon; + const QList subpath = path.toSubpathPolygons(); + if (not subpath.isEmpty()) + { + polygon = path.toSubpathPolygons().first(); + if (not polygon.isEmpty()) { - splPoints.removeLast(); + polygon.removeFirst(); // remove point (0;0) } - points << splPoints; - currentAngle += sectionAngle.at(i); } - return points; + + return std::move(polygon); +} + +//--------------------------------------------------------------------------------------------------------------------- +qreal VEllipticalArc::GetStartAngle() const +{ + return QLineF(GetCenter().toQPointF(), GetP1()).angle() - GetRotationAngle(); +} + +//--------------------------------------------------------------------------------------------------------------------- +qreal VEllipticalArc::GetEndAngle() const +{ + return QLineF(GetCenter().toQPointF(), GetP2()).angle() - GetRotationAngle(); } //--------------------------------------------------------------------------------------------------------------------- @@ -514,7 +418,7 @@ void VEllipticalArc::FindF2(qreal length) } while (length > MaxLength()) { - length = length - MaxLength(); + length = MaxLength(); } // We need to calculate the second angle @@ -527,7 +431,7 @@ void VEllipticalArc::FindF2(qreal length) qreal lenBez = GetLength(); // first approximation of length - const qreal eps = ToPixel(0.1, Unit::Mm); + const qreal eps = ToPixel(0.001, Unit::Mm); while (qAbs(lenBez - length) > eps) { diff --git a/src/libs/vgeometry/vellipticalarc.h b/src/libs/vgeometry/vellipticalarc.h index 52d5bf44f5e5..6ae49525b238 100644 --- a/src/libs/vgeometry/vellipticalarc.h +++ b/src/libs/vgeometry/vellipticalarc.h @@ -47,69 +47,85 @@ class VEllipticalArcData; class VEllipticalArc : public VAbstractArc { Q_DECLARE_TR_FUNCTIONS(VEllipticalArc) + public: VEllipticalArc(); - VEllipticalArc (const VPointF ¢er, qreal radius1, qreal radius2, const QString &formulaRadius1, - const QString &formulaRadius2, qreal f1, const QString &formulaF1, qreal f2, - const QString &formulaF2, qreal rotationAngle, const QString &formulaRotationAngle, - quint32 idObject = 0, Draw mode = Draw::Calculation); - VEllipticalArc (const VPointF ¢er, qreal radius1, qreal radius2, qreal f1, qreal f2, qreal rotationAngle); - VEllipticalArc (qreal length, const QString &formulaLength, const VPointF ¢er, qreal radius1, qreal radius2, - const QString &formulaRadius1, const QString &formulaRadius2, qreal f1, const QString &formulaF1, - qreal rotationAngle, const QString &formulaRotationAngle, quint32 idObject = 0, - Draw mode = Draw::Calculation); - VEllipticalArc (qreal length, const VPointF ¢er, qreal radius1, qreal radius2, qreal f1, qreal rotationAngle); + VEllipticalArc(const VPointF ¢er, qreal radius1, qreal radius2, const QString &formulaRadius1, + const QString &formulaRadius2, qreal f1, const QString &formulaF1, qreal f2, + const QString &formulaF2, qreal rotationAngle, const QString &formulaRotationAngle, + quint32 idObject = 0, Draw mode = Draw::Calculation); + + VEllipticalArc(const VPointF ¢er, qreal radius1, qreal radius2, qreal f1, qreal f2, qreal rotationAngle); + + VEllipticalArc(qreal length, const QString &formulaLength, const VPointF ¢er, qreal radius1, qreal radius2, + const QString &formulaRadius1, const QString &formulaRadius2, qreal f1, const QString &formulaF1, + qreal rotationAngle, const QString &formulaRotationAngle, quint32 idObject = 0, + Draw mode = Draw::Calculation); + + VEllipticalArc(qreal length, const VPointF ¢er, qreal radius1, qreal radius2, qreal f1, qreal rotationAngle); + VEllipticalArc(const VEllipticalArc &arc); - VEllipticalArc Rotate(const QPointF &originPoint, qreal degrees, const QString &prefix = QString()) const; - VEllipticalArc Flip(const QLineF &axis, const QString &prefix = QString()) const; - VEllipticalArc Move(qreal length, qreal angle, const QString &prefix = QString()) const; + VEllipticalArc Rotate(QPointF originPoint, qreal degrees, const QString &prefix = QString()) const; + VEllipticalArc Flip(const QLineF &axis, const QString &prefix = QString()) const; + VEllipticalArc Move(qreal length, qreal angle, const QString &prefix = QString()) const; + + virtual ~VEllipticalArc() Q_DECL_OVERRIDE; - virtual ~VEllipticalArc() Q_DECL_OVERRIDE; + VEllipticalArc &operator= (const VEllipticalArc &arc); - VEllipticalArc& operator= (const VEllipticalArc &arc); #ifdef Q_COMPILER_RVALUE_REFS VEllipticalArc &operator=(VEllipticalArc &&arc) Q_DECL_NOTHROW; #endif - void Swap(VEllipticalArc &arc) Q_DECL_NOTHROW; + void Swap(VEllipticalArc &arc) Q_DECL_NOTHROW; + + QString GetFormulaRotationAngle () const; + void SetFormulaRotationAngle (const QString &formula, qreal value); + qreal GetRotationAngle() const; - QString GetFormulaRotationAngle () const; - void SetFormulaRotationAngle (const QString &formula, qreal value); - qreal GetRotationAngle() const; + QString GetFormulaRadius1 () const; + void SetFormulaRadius1 (const QString &formula, qreal value); + qreal GetRadius1 () const; - QString GetFormulaRadius1 () const; - void SetFormulaRadius1 (const QString &formula, qreal value); - qreal GetRadius1 () const; + QString GetFormulaRadius2 () const; + void SetFormulaRadius2 (const QString &formula, qreal value); + qreal GetRadius2 () const; - QString GetFormulaRadius2 () const; - void SetFormulaRadius2 (const QString &formula, qreal value); - qreal GetRadius2 () const; + virtual qreal GetLength () const Q_DECL_OVERRIDE; - virtual qreal GetLength () const Q_DECL_OVERRIDE; + QPointF GetP1() const; + QPointF GetP2() const; - QPointF GetP1() const; - QPointF GetP2() const; + QTransform getTransform() const; + void setTransform(const QTransform &matrix, bool combine = false); + virtual VPointF GetCenter () const Q_DECL_OVERRIDE; virtual QVector GetPoints () const Q_DECL_OVERRIDE; + virtual qreal GetStartAngle () const Q_DECL_OVERRIDE; + virtual qreal GetEndAngle () const Q_DECL_OVERRIDE; + + QPointF CutArc (const qreal &length, VEllipticalArc &arc1, VEllipticalArc &arc2) const; + QPointF CutArc (const qreal &length) const; + + static qreal normalizeAngle(qreal angle); - QPointF CutArc (const qreal &length, VEllipticalArc &arc1, VEllipticalArc &arc2) const; - QPointF CutArc (const qreal &length) const; protected: - virtual void CreateName() Q_DECL_OVERRIDE; - virtual void FindF2(qreal length) Q_DECL_OVERRIDE; + virtual void CreateName() Q_DECL_OVERRIDE; + virtual void FindF2(qreal length) Q_DECL_OVERRIDE; + private: QSharedDataPointer d; - - // cppcheck-suppress unusedPrivateFunction - QVector GetAngles () const; - qreal MaxLength() const; - QPointF GetPoint (qreal angle) const; - - static int GetQuadransRad(qreal &rad); + qreal MaxLength() const; + QPointF getPoint (qreal angle) const; }; Q_DECLARE_METATYPE(VEllipticalArc) Q_DECLARE_TYPEINFO(VEllipticalArc, Q_MOVABLE_TYPE); +//--------------------------------------------------------------------------------------------------------------------- +inline qreal VEllipticalArc::normalizeAngle(qreal angle) +{ + return angle - 360.*qFloor(angle/360.); +} #endif // VELLIPTICALARC_H diff --git a/src/libs/vgeometry/vellipticalarc_p.h b/src/libs/vgeometry/vellipticalarc_p.h index d2c84844fb7c..af302616a335 100644 --- a/src/libs/vgeometry/vellipticalarc_p.h +++ b/src/libs/vgeometry/vellipticalarc_p.h @@ -33,6 +33,8 @@ class VEllipticalArcData : public QSharedData qreal rotationAngle; /** @brief formulaRotationAngle formula for rotationAngle. */ QString formulaRotationAngle; + /** @brief m_transform for elliptical arc */ + QTransform m_transform; private: VEllipticalArcData &operator=(const VEllipticalArcData &) Q_DECL_EQ_DELETE; @@ -40,45 +42,49 @@ class VEllipticalArcData : public QSharedData //--------------------------------------------------------------------------------------------------------------------- VEllipticalArcData::VEllipticalArcData() - : radius1(0), - radius2(0), - formulaRadius1(), - formulaRadius2(), - rotationAngle(0), - formulaRotationAngle() + : radius1(0) + , radius2(0) + , formulaRadius1() + , formulaRadius2() + , rotationAngle(0) + , formulaRotationAngle() + , m_transform() {} //--------------------------------------------------------------------------------------------------------------------- VEllipticalArcData::VEllipticalArcData(qreal radius1, qreal radius2, const QString &formulaRadius1, const QString &formulaRadius2, qreal rotationAngle, const QString &formulaRotationAngle) - : radius1(radius1), - radius2(radius2), - formulaRadius1(formulaRadius1), - formulaRadius2(formulaRadius2), - rotationAngle(rotationAngle), - formulaRotationAngle(formulaRotationAngle) + : radius1(radius1) + , radius2(radius2) + , formulaRadius1(formulaRadius1) + , formulaRadius2(formulaRadius2) + , rotationAngle(rotationAngle) + , formulaRotationAngle(formulaRotationAngle) + , m_transform() {} //--------------------------------------------------------------------------------------------------------------------- VEllipticalArcData::VEllipticalArcData(qreal radius1, qreal radius2, qreal rotationAngle) - : radius1(radius1), - radius2(radius2), - formulaRadius1(QString().number(qApp->fromPixel(radius1))), - formulaRadius2(QString().number(qApp->fromPixel(radius2))), - rotationAngle(rotationAngle), - formulaRotationAngle(QString().number(qApp->fromPixel(rotationAngle))) + : radius1(radius1) + , radius2(radius2) + , formulaRadius1(QString().number(qApp->fromPixel(radius1))) + , formulaRadius2(QString().number(qApp->fromPixel(radius2))) + , rotationAngle(rotationAngle) + , formulaRotationAngle(QString().number(qApp->fromPixel(rotationAngle))) + , m_transform() {} //--------------------------------------------------------------------------------------------------------------------- VEllipticalArcData::VEllipticalArcData(const VEllipticalArcData &arc) - : QSharedData(arc), - radius1(arc.radius1), - radius2(arc.radius2), - formulaRadius1(arc.formulaRadius1), - formulaRadius2(arc.formulaRadius2), - rotationAngle(arc.rotationAngle), - formulaRotationAngle(arc.formulaRotationAngle) + : QSharedData(arc) + , radius1(arc.radius1) + , radius2(arc.radius2) + , formulaRadius1(arc.formulaRadius1) + , formulaRadius2(arc.formulaRadius2) + , rotationAngle(arc.rotationAngle) + , formulaRotationAngle(arc.formulaRotationAngle) + , m_transform(arc.m_transform) {} //--------------------------------------------------------------------------------------------------------------------- @@ -88,4 +94,3 @@ VEllipticalArcData::~VEllipticalArcData() QT_WARNING_POP #endif // VELLIPTICALARC_P - diff --git a/src/libs/vgeometry/vgobject.cpp b/src/libs/vgeometry/vgobject.cpp index 532ca499cfad..3e7ea58a8466 100644 --- a/src/libs/vgeometry/vgobject.cpp +++ b/src/libs/vgeometry/vgobject.cpp @@ -63,7 +63,7 @@ #include "../ifc/ifcdef.h" #include "vgobject_p.h" -const double VGObject::accuracyPointOnLine = (0.12/*mm*/ / 25.4) * PrintDPI; +const double VGObject::accuracyPointOnLine = (0.1555/*mm*/ / 25.4) * 96.0; #ifdef Q_COMPILER_RVALUE_REFS VGObject &VGObject::operator=(VGObject &&obj) Q_DECL_NOTHROW { Swap(obj); return *this; } diff --git a/src/libs/vmisc/def.h b/src/libs/vmisc/def.h index a3999f8d67a4..053b6297021a 100644 --- a/src/libs/vmisc/def.h +++ b/src/libs/vmisc/def.h @@ -53,11 +53,12 @@ #define DEF_H #include -#include +#include #include #include #include #include +#include #include #ifdef Q_OS_WIN #include @@ -453,6 +454,16 @@ Q_REQUIRED_RESULT QPixmap darkenPixmap(const QPixmap &pixmap); void ShowInGraphicalShell(const QString &filePath); +constexpr qreal accuracyPointOnLine = (0.1555/*mm*/ / 25.4) * 96.0; + +Q_REQUIRED_RESULT static inline bool VFuzzyComparePoints(const QPointF &p1, const QPointF &p2, + qreal accuracy = accuracyPointOnLine); + +static inline bool VFuzzyComparePoints(const QPointF &p1, const QPointF &p2, qreal accuracy) +{ + return QLineF(p1, p2).length() <= accuracy; +} + Q_REQUIRED_RESULT static inline bool VFuzzyComparePossibleNulls(double p1, double p2); static inline bool VFuzzyComparePossibleNulls(double p1, double p2) { diff --git a/src/libs/vtest/abstracttest.cpp b/src/libs/vtest/abstracttest.cpp index 6b1930273cb4..b732c998e450 100644 --- a/src/libs/vtest/abstracttest.cpp +++ b/src/libs/vtest/abstracttest.cpp @@ -2,7 +2,7 @@ * * * Copyright (C) 2017 Seamly, LLC * * * - * https://github.com/fashionfreedom/seamly2d * + * https://github.com/fashionfreedom/seamly2d * * * *************************************************************************** ** @@ -68,10 +68,19 @@ #include #include #include +#include #include "logging.h" #include "vsysexits.h" +#include "../vgeometry/vgeometrydef.h" #include "../vgeometry/vgobject.h" +#include "../vgeometry/vpointf.h" +#include "../vgeometry/vspline.h" +#include "../vgeometry/vsplinepath.h" +#include "../vlayout/vabstractpiece.h" +#include "../vpatterndb/vcontainer.h" +#include "../vpatterndb/vpiece.h" +#include "../vpatterndb/vpiecenode.h" //--------------------------------------------------------------------------------------------------------------------- AbstractTest::AbstractTest(QObject *parent) : @@ -84,19 +93,24 @@ void AbstractTest::Comparison(const QVector &ekv, const QVector &ekv, const QVector &ekvOrig) const; + void Comparison(const QPointF &result, const QPointF &expected, qreal testAccuracy) const; QString Seamly2DPath() const; QString SeamlyMePath() const; diff --git a/src/libs/vtest/vtest.pro b/src/libs/vtest/vtest.pro index 6cd27105d372..ee422595666a 100644 --- a/src/libs/vtest/vtest.pro +++ b/src/libs/vtest/vtest.pro @@ -8,7 +8,7 @@ message("Entering vtest.pro") include(../../../common.pri) -QT += testlib widgets +QT += testlib widgets printsupport # Name of library TARGET = vtest diff --git a/src/test/Seamly2DTest/tst_vellipticalarc.cpp b/src/test/Seamly2DTest/tst_vellipticalarc.cpp index 88de464fcab1..d0229d7be3dc 100644 --- a/src/test/Seamly2DTest/tst_vellipticalarc.cpp +++ b/src/test/Seamly2DTest/tst_vellipticalarc.cpp @@ -29,13 +29,12 @@ #include "tst_vellipticalarc.h" #include "../vgeometry/vellipticalarc.h" #include "../vlayout/vabstractpiece.h" -#include "../vmisc/logging.h" #include #include //--------------------------------------------------------------------------------------------------------------------- -TST_VEllipticalArc::TST_VEllipticalArc(QObject *parent) : QObject(parent) +TST_VEllipticalArc::TST_VEllipticalArc(QObject *parent) : AbstractTest(parent) {} //--------------------------------------------------------------------------------------------------------------------- @@ -48,7 +47,7 @@ void TST_VEllipticalArc::CompareTwoWays_data() QTest::addColumn("f2"); QTest::addColumn("rotationAngle"); - //QTest::newRow("Test case 1") << QPointF() << 100. << 200. << 0. << 90.0 << 0.; + QTest::newRow("Test case 1") << QPointF() << 100. << 200. << 0. << 90.0 << 0.; QTest::newRow("Test case 2") << QPointF() << 100. << 200. << 0. << 180.0 << 0.; QTest::newRow("Test case 3") << QPointF() << 100. << 200. << 0. << 270.0 << 0.; QTest::newRow("Test case 4") << QPointF() << 100. << 200. << 0. << 360.0 << 0.; @@ -85,19 +84,21 @@ void TST_VEllipticalArc::CompareTwoWays() VEllipticalArc arc2(length, center, radius1, radius2, f1, rotationAngle); const qreal lengthEps = ToPixel(0.1, Unit::Mm); // computing error + const QString errorLengthMsg = - QString("Difference between real and computing lengthes bigger than eps = %1.").number(lengthEps); - QVERIFY2(qAbs(arc1.GetLength() - length) <= lengthEps, qUtf8Printable(errorLengthMsg)); - QVERIFY2(qAbs(arc2.GetLength() - length) <= lengthEps, qUtf8Printable(errorLengthMsg)); - QVERIFY2(qAbs(arc1.GetLength() - arc2.GetLength()) <= lengthEps, qUtf8Printable(errorLengthMsg)); + QString("Difference between real and computing lengthes bigger than eps = %1. l1 = %2; l2 = %3"); + QVERIFY2(qAbs(arc2.GetLength() - length) <= lengthEps, + qUtf8Printable(errorLengthMsg.arg(lengthEps).arg(arc2.GetLength()).arg(length))); + QVERIFY2(qAbs(arc1.GetLength() - arc2.GetLength()) <= lengthEps, + qUtf8Printable(errorLengthMsg.arg(lengthEps).arg(arc2.GetLength()).arg(arc2.GetLength()))); const qreal angleEps = 0.4; const QString errorAngleMsg = - QString("Difference between real and computing angles bigger than eps = %1.").number(angleEps); + QString("Difference between real and computing angles bigger than eps = %1. f1 = %2; f2 = %3"); // compare angles - QVERIFY2(qAbs(arc1.GetEndAngle() - arc2.GetEndAngle()) <= angleEps, qUtf8Printable(errorAngleMsg)); - QVERIFY2(qAbs(arc1.GetEndAngle() - f2) <= angleEps, qUtf8Printable(errorAngleMsg)); - QVERIFY2(qAbs(arc1.GetEndAngle() - f2) <= angleEps, qUtf8Printable(errorAngleMsg)); + const qreal diff = qAbs(arc1.GetEndAngle() - arc2.GetEndAngle()); + QVERIFY2(qAbs(diff - 360.0*(diff/360.0)) <= angleEps, + qUtf8Printable(errorAngleMsg.arg(angleEps).arg(arc1.GetEndAngle()).arg(arc2.GetEndAngle()))); } //--------------------------------------------------------------------------------------------------------------------- @@ -116,12 +117,17 @@ void TST_VEllipticalArc::NegativeArc() const qreal length = M_PI*(radius1+radius2)*(1+3*h/(10+qSqrt(4-3*h)))/2; VEllipticalArc arc(-length, center, radius1, radius2, f1, rotationAngle); - const qreal eps = 1; // computing error + const qreal eps = ToPixel(0.45, Unit::Mm); // computing error const QString errorMsg = - QString("Difference between real and computing lengthes bigger than eps = %1.").number(eps); + QString("Difference between real and computing lengthes bigger than eps = %1. v1 = %2; v2 = %3"); + + + QVERIFY2(qAbs(arc.GetLength() + length) <= eps, + qUtf8Printable(errorMsg.arg(eps).arg(arc.GetLength()).arg(length))); - QVERIFY2(qAbs(arc.GetLength() + length) <= eps, qUtf8Printable(errorMsg)); - QVERIFY2(arc.GetEndAngle() - f2 <= eps, qUtf8Printable(errorMsg)); + const qreal angleEps = 0.4; + QVERIFY2(arc.GetEndAngle() - f2 <= angleEps, + qUtf8Printable(errorMsg.arg(eps).arg(arc.GetEndAngle()).arg(f2))); } // cppcheck-suppress unusedFunction @@ -158,19 +164,29 @@ void TST_VEllipticalArc::TestData() QTest::addColumn("endAngle"); QTest::addColumn("rotationAngle"); - QTest::newRow("Full circle: radiuses 10, 20") << 10.0 << 20.0 << 0.0 << 360.0 << 0.0; - QTest::newRow("Full circle: radiuses 150, 200") << 150.0 << 200.0 << 0.0 << 360.0 << 0.0; - QTest::newRow("Full circle: radiuses 150, 200, rotation 30") << 150.0 << 200.0 << 0.0 << 360.0 << 30.0; - QTest::newRow("Full circle: radiuses 1500, 1000") << 1500.0 << 1000.0 << 0.0 << 360.0 << 0.0; - QTest::newRow("Full circle: radiuses 1500, 1000, rotation 50") << 1500.0 << 1000.0 << 0.0 << 360.0 << 50.0; - QTest::newRow("Full circle: radiuses 90000, 80000, rotation 90") << 90000.0 << 80000.0 << 0.0 << 360.0 << 90.0; + QTest::newRow("Full circle: radiuses 10, 20; start 0") << 10.0 << 20.0 << 0.0 << 360.0 << 0.0; + QTest::newRow("Full circle: radiuses 150, 200; start 0") << 150.0 << 200.0 << 0.0 << 360.0 << 0.0; + QTest::newRow("Full circle: radiuses 150, 200, rotation 30; start 0") << 150.0 << 200.0 << 0.0 << 360.0 << 30.0; + QTest::newRow("Full circle: radiuses 1500, 1000; start 0") << 1500.0 << 1000.0 << 0.0 << 360.0 << 0.0; + QTest::newRow("Full circle: radiuses 1500, 1000, rotation 50; start 0") << 1500.0 << 1000.0 << 0.0 << 360.0 << 50.0; + QTest::newRow("Full circle: radiuses 15000, 10000, rotation 90; start 0") << 15000.0 << 10000.0 << 0.0 << 360.0 + << 90.0; + + QTest::newRow("Full circle: radiuses 10, 20; start 90") << 10.0 << 20.0 << 90.0 << 90.0 << 0.0; + QTest::newRow("Full circle: radiuses 150, 200; start 90") << 150.0 << 200.0 << 90.0 << 90.0 << 0.0; + QTest::newRow("Full circle: radiuses 150, 200, rotation 30; start 90") << 150.0 << 200.0 << 90.0 << 90.0 << 30.0; + QTest::newRow("Full circle: radiuses 1500, 1000; start 90") << 1500.0 << 1000.0 << 90.0 << 90.0 << 0.0; + QTest::newRow("Full circle: radiuses 1500, 1000, rotation 50; start 90") << 1500.0 << 1000.0 << 90.0 << 90.0 + << 50.0; + QTest::newRow("Full circle: radiuses 15000, 10000, rotation 90; start 90") << 15000.0 << 10000.0 << 90.0 << 90.0 + << 90.0; QTest::newRow("Arc less than 45 degree, radiuses 100, 50") << 100.0 << 50.0 << 0.0 << 10.5 << 0.0; QTest::newRow("Arc less than 45 degree, radiuses 150, 50, rotation 180") << 150.0 << 50.0 << 0.0 << 10.5 << 180.0; QTest::newRow("Arc less than 45 degree, radiuses 1500, 800, rotation 90") << 1500.0 << 800.0 << 0.0 << 10.5 << 90.0; - QTest::newRow("Arc less than 45 degree, radiuses 50000, 10000, rotation 40") + QTest::newRow("Arc less than 45 degree, radiuses 15000, 10000, rotation 40") << 50000.0 << 10000.0 << 0.0 << 10.5 << 40.0; - QTest::newRow("Arc less than 45 degree, radiuses 90000, 10000") << 90000.0 << 10000.0 << 0.0 << 10.5 << 0.0; + QTest::newRow("Arc less than 45 degree, radiuses 15000, 10000") << 15000.0 << 10000.0 << 0.0 << 10.5 << 0.0; QTest::newRow("Arc 45 degree, radiuses 100, 50, rotation 45") << 100.0 << 50.0 << 0.0 << 45.0 << 45.0; QTest::newRow("Arc 45 degree, radiuses 150, 15, rotation 30") << 150.0 << 15.0 << 0.0 << 45.0 << 30.0; @@ -270,9 +286,8 @@ void TST_VEllipticalArc::TestGetPoints1() { // equation of ellipse will be different when rotation angle isn't 0 so we can't use this test in this case const qreal eps = 0.05; - for (int i=0; i < points.size(); ++i) + for (auto p : points) { - QPointF p = points.at(i); const qreal equationRes = p.rx()*p.rx()/(radius1*radius1) + p.ry()*p.ry()/(radius2*radius2); const qreal diff = qAbs(equationRes - 1); const QString errorMsg = QString("Broken the first rule. Any point must satisfy the equation of ellipse." @@ -371,7 +386,7 @@ void TST_VEllipticalArc::TestGetPoints3() if (VFuzzyComparePossibleNulls(arc.AngleArc(), 360.0)) {// calculated full ellipse square const qreal ellipseSquare = M_PI * radius1 * radius2; - const qreal epsSquare = ellipseSquare * 0.5 / 100; // computing error 0.5 % from origin squere + const qreal epsSquare = ellipseSquare * 1.7 / 100; // computing error 0.5 % from origin squere const qreal arcSquare = qAbs(VAbstractPiece::SumTrapezoids(points)/2.0); const qreal diffSquare = qAbs(ellipseSquare - arcSquare); const QString errorMsg1 = QString("Broken the second rule. Interpolation has too big computing error. " @@ -423,6 +438,8 @@ void TST_VEllipticalArc::TestRotation_data() QTest::newRow("Test el arc 1") << QPointF() << 10. << 20.0 << 1. << 91. << 0.<< QPointF() << 90. << "_r"; QTest::newRow("Test el arc 2") << QPointF() << 10. << 20.0 << 0. << 90. << 0.<< QPointF() << 90. << "_r"; + QTest::newRow("Test el arc 3.2") << QPointF(10, 10) << 10. << 20.0 << 0. << 90. << 0.<< QPointF() << 90. << "_r"; + QTest::newRow("Test el arc 3.1") << QPointF(10, 10) << 10. << 20.0 << 1. << 91. << 0.<< QPointF() << 90. << "_r"; QTest::newRow("Test el arc 3") << QPointF(10, 10) << 10. << 20.0 << 1. << 91. << 90.<< QPointF() << 90. << "_r"; QTest::newRow("Test el arc 4") << QPointF(10, 10) << 10. << 20.0 << 0. << 90. << 90.<< QPointF() << 90. << "_r"; QTest::newRow("Test el arc 5") << QPointF(10, 10) << 10. << 20.0 << 0. << 180. << 90.<< QPointF() << 90. << "_r"; @@ -448,8 +465,16 @@ void TST_VEllipticalArc::TestRotation() const VEllipticalArc arcOrigin(VPointF(center), radius1, radius2, startAngle, endAngle, rotationAngle); const VEllipticalArc rotatedArc = arcOrigin.Rotate(rotatePoint, degrees, prefix); - QVERIFY(qAbs(arcOrigin.AngleArc() - rotatedArc.AngleArc()) <= 0.4); - QVERIFY(qAbs(arcOrigin.GetLength() - rotatedArc.GetLength()) <= ToPixel(1, Unit::Mm)); + QVERIFY2(qAbs(arcOrigin.AngleArc() - rotatedArc.AngleArc()) <= 1.6, + qUtf8Printable(QString("a1 = %1, a2 - %2").arg(arcOrigin.AngleArc()).arg(rotatedArc.AngleArc()))); + + QString errorLengthMsg = + QString("Difference between real and computing lengthes bigger than eps = %1. l1 = %2; l2 = %3"); + QVERIFY2(qAbs(arcOrigin.GetLength() - rotatedArc.GetLength()) <= ToPixel(1, Unit::Mm), + qUtf8Printable(errorLengthMsg.arg(ToPixel(1, Unit::Mm)) + .arg(arcOrigin.GetLength()) + .arg(rotatedArc.GetLength()))); + QCOMPARE(arcOrigin.GetRadius1(), rotatedArc.GetRadius1()); QCOMPARE(arcOrigin.GetRadius2(), rotatedArc.GetRadius2()); QCOMPARE(arcOrigin.GetRotationAngle(), rotatedArc.GetRotationAngle()); diff --git a/src/test/Seamly2DTest/tst_vellipticalarc.h b/src/test/Seamly2DTest/tst_vellipticalarc.h index 086f664476ae..7efb770a81bc 100644 --- a/src/test/Seamly2DTest/tst_vellipticalarc.h +++ b/src/test/Seamly2DTest/tst_vellipticalarc.h @@ -29,9 +29,9 @@ #ifndef TST_VELLIPTICALARC_H #define TST_VELLIPTICALARC_H -#include +#include "../vtest/abstracttest.h" -class TST_VEllipticalArc : public QObject +class TST_VEllipticalArc : public AbstractTest { Q_OBJECT public: