diff --git a/src/libs/vgeometry/vspline.cpp b/src/libs/vgeometry/vspline.cpp index 6a3771b3a..135926da9 100644 --- a/src/libs/vgeometry/vspline.cpp +++ b/src/libs/vgeometry/vspline.cpp @@ -82,6 +82,34 @@ VSpline::VSpline (VPointF p1, QPointF p2, QPointF p3, VPointF p4, qreal kCurve, } +//--------------------------------------------------------------------------------------------------------------------- +/** + * @brief VSpline constructor + * @param p1 first point spline. + * @param p4 first control point. + * @param angle1 angle from first point to first control point. + * @param angle1Formula formula angle from first point to first control point. + * @param angle2 angle from second point to second control point. + * @param angle2Formula formula angle from second point to second control point. + * @param c1Length length from first point to first control point. + * @param c1LengthFormula formula length from first point to first control point. + * @param c2Length length from second point to first control point. + * @param c2LengthFormula formula length from second point to first control point. + * @param kCurve coefficient of curvature spline. + * @param idObject + * @param mode + */ +VSpline::VSpline(VPointF p1, VPointF p4, qreal angle1, const QString &angle1Formula, qreal angle2, + const QString &angle2Formula, qreal c1Length, const QString &c1LengthFormula, qreal c2Length, + const QString &c2LengthFormula, qreal kCurve, quint32 idObject, Draw mode) + : VAbstractCurve(GOType::Spline, idObject, mode), + d(new VSplineData(p1, p4, angle1, angle1Formula, angle2,angle2Formula, c1Length, c1LengthFormula, c2Length, + c2LengthFormula, kCurve)) +{ + CreateName(); +} + + //--------------------------------------------------------------------------------------------------------------------- VSpline::~VSpline() {} @@ -93,7 +121,7 @@ VSpline::~VSpline() */ qreal VSpline::GetLength () const { - return LengthBezier ( GetP1().toQPointF(), d->p2, d->p3, GetP4().toQPointF()); + return LengthBezier ( GetP1().toQPointF(), GetP2(), GetP3(), GetP4().toQPointF()); } //--------------------------------------------------------------------------------------------------------------------- @@ -222,7 +250,7 @@ QPointF VSpline::CutSpline(qreal length, VSpline &spl1, VSpline &spl2) const */ QVector VSpline::GetPoints () const { - return GetPoints(GetP1().toQPointF(), d->p2, d->p3, GetP4().toQPointF()); + return GetPoints(GetP1().toQPointF(), GetP2(), GetP3(), GetP4().toQPointF()); } //--------------------------------------------------------------------------------------------------------------------- @@ -589,8 +617,8 @@ void VSpline::PointBezier_r ( qreal x1, qreal y1, qreal x2, qreal y2, */ qreal VSpline::CalcSqDistance (qreal x1, qreal y1, qreal x2, qreal y2) { - qreal dx = x2 - x1; - qreal dy = y2 - y1; + const qreal dx = x2 - x1; + const qreal dy = y2 - y1; return dx * dx + dy * dy; } @@ -668,7 +696,9 @@ VPointF VSpline::GetP1() const */ QPointF VSpline::GetP2() const { - return d->p2; + QLineF p1p2(d->p1.x(), d->p1.y(), d->p1.x() + d->c1Length, d->p1.y()); + p1p2.setAngle(d->angle1); + return p1p2.p2(); } //--------------------------------------------------------------------------------------------------------------------- @@ -678,7 +708,9 @@ QPointF VSpline::GetP2() const */ QPointF VSpline::GetP3() const { - return d->p3; + QLineF p4p3(d->p4.x(), d->p4.y(), d->p4.x() + d->c2Length, d->p4.y()); + p4p3.setAngle(d->angle2); + return p4p3.p2(); } //--------------------------------------------------------------------------------------------------------------------- @@ -711,6 +743,42 @@ qreal VSpline::GetEndAngle() const return d->angle2; } +//--------------------------------------------------------------------------------------------------------------------- +QString VSpline::GetStartAngleFormula() const +{ + return d->angle1F; +} + +//--------------------------------------------------------------------------------------------------------------------- +QString VSpline::GetEndAngleFormula() const +{ + return d->angle2F; +} + +//--------------------------------------------------------------------------------------------------------------------- +qreal VSpline::GetC1Length() const +{ + return d->c1Length; +} + +//--------------------------------------------------------------------------------------------------------------------- +qreal VSpline::GetC2Length() const +{ + return d->c2Length; +} + +//--------------------------------------------------------------------------------------------------------------------- +QString VSpline::C1LengthFormula() const +{ + return d->c1LengthF; +} + +//--------------------------------------------------------------------------------------------------------------------- +QString VSpline::C2LengthFormula() const +{ + return d->c2LengthF; +} + //--------------------------------------------------------------------------------------------------------------------- /** * @brief GetKasm1 return coefficient of length first control line. @@ -718,7 +786,8 @@ qreal VSpline::GetEndAngle() const */ qreal VSpline::GetKasm1() const { - return d->kAsm1; + return QLineF(d->p1.toQPointF(), GetP2()).length() / VSplineData::GetL(d->p1.toQPointF(), d->p4.toQPointF(), + d->kCurve); } //--------------------------------------------------------------------------------------------------------------------- @@ -728,7 +797,8 @@ qreal VSpline::GetKasm1() const */ qreal VSpline::GetKasm2() const { - return d->kAsm2; + return QLineF(d->p4.toQPointF(), GetP3()).length() / VSplineData::GetL(d->p1.toQPointF(), d->p4.toQPointF(), + d->kCurve); } //--------------------------------------------------------------------------------------------------------------------- @@ -846,8 +916,8 @@ qreal VSpline::ParamT (const QPointF &pBt) const { QVector ts; // Calculate t coefficient for each axis - ts += CalcT (GetP1().toQPointF().x(), d->p2.x(), d->p3.x(), GetP4().toQPointF().x(), pBt.x()); - ts += CalcT (GetP1().toQPointF().y(), d->p2.y(), d->p3.y(), GetP4().toQPointF().y(), pBt.y()); + ts += CalcT (GetP1().toQPointF().x(), GetP2().x(), GetP3().x(), GetP4().toQPointF().x(), pBt.x()); + ts += CalcT (GetP1().toQPointF().y(), GetP2().y(), GetP3().y(), GetP4().toQPointF().y(), pBt.y()); if (ts.isEmpty()) { @@ -863,8 +933,8 @@ qreal VSpline::ParamT (const QPointF &pBt) const { const qreal t = ts.at(i); const QPointF p0 = GetP1().toQPointF(); - const QPointF p1 = d->p2; - const QPointF p2 = d->p3; + const QPointF p1 = GetP2(); + const QPointF p2 = GetP3(); const QPointF p3 = GetP4().toQPointF(); //The explicit form of the Cubic Bézier curve const qreal pointX = pow(1-t, 3)*p0.x() + 3*pow(1-t, 2)*t*p1.x() + 3*(1-t)*pow(t, 2)*p2.x() + pow(t, 3)*p3.x(); diff --git a/src/libs/vgeometry/vspline.h b/src/libs/vgeometry/vspline.h index aca2919e4..1e32d8422 100644 --- a/src/libs/vgeometry/vspline.h +++ b/src/libs/vgeometry/vspline.h @@ -51,6 +51,9 @@ public: quint32 idObject = 0, Draw mode = Draw::Calculation); VSpline (VPointF p1, QPointF p2, QPointF p3, VPointF p4, qreal kCurve, quint32 idObject = 0, Draw mode = Draw::Calculation); + VSpline (VPointF p1, VPointF p4, qreal angle1, const QString &angle1Formula, qreal angle2, + const QString &angle2Formula, qreal c1Length, const QString &c1LengthFormula, qreal c2Length, + const QString &c2LengthFormula, qreal kCurve, quint32 idObject = 0, Draw mode = Draw::Calculation); virtual ~VSpline() Q_DECL_OVERRIDE; VSpline &operator=(const VSpline &spl); VPointF GetP1 () const; @@ -61,6 +64,15 @@ public: virtual qreal GetStartAngle () const Q_DECL_OVERRIDE; virtual qreal GetEndAngle() const Q_DECL_OVERRIDE; + QString GetStartAngleFormula () const; + QString GetEndAngleFormula() const; + + qreal GetC1Length() const; + qreal GetC2Length() const; + + QString C1LengthFormula() const; + QString C2LengthFormula() const; + qreal GetLength () const; qreal GetKasm1() const; qreal GetKasm2() const; diff --git a/src/libs/vgeometry/vspline_p.h b/src/libs/vgeometry/vspline_p.h index c5ed358ba..3ce66d267 100644 --- a/src/libs/vgeometry/vspline_p.h +++ b/src/libs/vgeometry/vspline_p.h @@ -42,85 +42,171 @@ class VSplineData : public QSharedData { public: - VSplineData() - :p1(VPointF()), p2(QPointF()), p3(QPointF()), p4(VPointF()), angle1(0), angle2(0), kAsm1(1), kAsm2(1), kCurve(1) - {} - - VSplineData ( const VSplineData &spline ) - :QSharedData(spline), p1(spline.p1), p2(spline.p2), p3(spline.p3), p4(spline.p4), angle1(spline.angle1), - angle2(spline.angle2), kAsm1(spline.kAsm1), kAsm2(spline.kAsm2), kCurve(spline.kCurve) - {} - - VSplineData (VPointF p1, VPointF p4, qreal angle1, qreal angle2, qreal kAsm1, qreal kAsm2, qreal kCurve) - :p1(p1), p2(QPointF()), p3(QPointF()), p4(p4), angle1(angle1), angle2(angle2), kAsm1(kAsm1), kAsm2(kAsm2), - kCurve(kCurve) - { - qreal L = 0, radius = 0, angle = 90; - QPointF point1 = this->p1.toQPointF(); - QPointF point4 = this->p4.toQPointF(); - radius = QLineF(point1, point4).length()/M_SQRT2; - L = kCurve * radius * 4 / 3 * tan( angle * M_PI / 180.0 / 4 ); - QLineF p1p2(this->p1.x(), this->p1.y(), this->p1.x() + L * kAsm1, this->p1.y()); - p1p2.setAngle(angle1); - QLineF p4p3(this->p4.x(), this->p4.y(), this->p4.x() + L * kAsm2, this->p4.y()); - p4p3.setAngle(angle2); - this->p2 = p1p2.p2(); - this->p3 = p4p3.p2(); - } - - VSplineData (VPointF p1, QPointF p2, QPointF p3, VPointF p4, qreal kCurve) - :p1(p1), p2(p2), p3(p3), p4(p4), angle1(0), angle2(0), kAsm1(1), kAsm2(1), kCurve(1) - { - this->angle1 = QLineF ( this->p1.toQPointF(), this->p2 ).angle(); - this->angle2 = QLineF ( this->p4.toQPointF(), this->p3 ).angle(); - - qreal L = 0, radius = 0, angle = 90; - QPointF point1 = this->p1.toQPointF(); - QPointF point4 = this->p4.toQPointF(); - radius = QLineF(point1, point4).length()/M_SQRT2; - L = kCurve * radius * 4 / 3 * tan( angle * M_PI / 180.0 / 4 ); - - this->kCurve = kCurve; - this->kAsm1 = QLineF ( this->p1.toQPointF(), this->p2 ).length()/L; - this->kAsm2 = QLineF ( this->p4.toQPointF(), this->p3 ).length()/L; - } - + VSplineData(); + VSplineData(const VSplineData &spline); + VSplineData(VPointF p1, VPointF p4, qreal angle1, qreal angle2, qreal kAsm1, qreal kAsm2, qreal kCurve); + VSplineData(VPointF p1, QPointF p2, QPointF p3, VPointF p4, qreal kCurve); + VSplineData(VPointF p1, VPointF p4, qreal angle1, const QString &angle1F, qreal angle2, const QString &angle2F, + qreal c1Length, const QString &c1LengthF, qreal c2Length, const QString &c2LengthF, qreal kCurve); virtual ~VSplineData(); + static qreal GetL(const QPointF &p1, const QPointF &p4, qreal kCurve); + /** @brief p1 first spline point. */ - VPointF p1; + VPointF p1; - /** @brief p2 first control point. */ - QPointF p2; - - /** @brief p3 second control point. */ - QPointF p3; - - /** @brief p4 last spline point. */ - VPointF p4; + /** @brief p4 fourth spline point. */ + VPointF p4; /** @brief angle1 first angle control line. */ - qreal angle1; + qreal angle1; + + /** @brief angle1F the first control point angle formula*/ + QString angle1F; /** @brief angle2 second angle control line. */ - qreal angle2; + qreal angle2; - /** @brief kAsm1 coefficient of length first control line. */ - qreal kAsm1; + /** @brief angle2F the second control point angle formula*/ + QString angle2F; - /** @brief kAsm2 coefficient of length second control line. */ - qreal kAsm2; + /** @brief c1Length the length from the first spline point to the first control point. */ + qreal c1Length; + + /** @brief c1LengthF the formula from the first spline point to the first control point. */ + QString c1LengthF; + + /** @brief c2Length the length from the fourth spline point to the second control point. */ + qreal c2Length; + + /** @brief c2LengthF the formula length from the fourth spline point to the second control point. */ + QString c2LengthF; /** @brief kCurve coefficient of curvature spline. */ - qreal kCurve; + qreal kCurve; private: VSplineData &operator=(const VSplineData &) Q_DECL_EQ_DELETE; }; +//--------------------------------------------------------------------------------------------------------------------- +VSplineData::VSplineData() + : p1(), + p4(), + angle1(0), + angle1F("0"), + angle2(0), + angle2F("0"), + c1Length(0), + c1LengthF("0"), + c2Length(0), + c2LengthF("0"), + kCurve(1) +{} + +//--------------------------------------------------------------------------------------------------------------------- +VSplineData::VSplineData(const VSplineData &spline) + : QSharedData(spline), + p1(spline.p1), + p4(spline.p4), + angle1(spline.angle1), + angle1F(spline.angle1F), + angle2(spline.angle2), + angle2F(spline.angle2F), + c1Length(spline.c1Length), + c1LengthF(spline.c1LengthF), + c2Length(spline.c2Length), + c2LengthF(spline.c2LengthF), + kCurve(spline.kCurve) +{} + +//--------------------------------------------------------------------------------------------------------------------- +VSplineData::VSplineData(VPointF p1, VPointF p4, qreal angle1, qreal angle2, qreal kAsm1, qreal kAsm2, qreal kCurve) + : p1(p1), + p4(p4), + angle1(angle1), + angle1F(QString().number(angle1)), + angle2(angle2), + angle2F(QString().number(angle2)), + c1Length(0), + c1LengthF("0"), + c2Length(0), + c2LengthF("0"), + kCurve(kCurve) +{ + const qreal L = GetL(p1.toQPointF(), p4.toQPointF(), kCurve); + + QLineF p1p2(p1.x(), p1.y(), p1.x() + L * kAsm1, p1.y()); + p1p2.setAngle(angle1); + c1Length = p1p2.length(); + c1LengthF = QString().number(c1Length); + + QLineF p4p3(p4.x(), p4.y(), p4.x() + L * kAsm2, p4.y()); + p4p3.setAngle(angle2); + c2Length = p4p3.length(); + c2LengthF = QString().number(c2Length); +} + +//--------------------------------------------------------------------------------------------------------------------- +VSplineData::VSplineData(VPointF p1, QPointF p2, QPointF p3, VPointF p4, qreal kCurve) + : p1(p1), + p4(p4), + angle1(0), + angle1F("0"), + angle2(0), + angle2F("0"), + c1Length(0), + c1LengthF("0"), + c2Length(0), + c2LengthF("0"), + kCurve(kCurve) +{ + QLineF p1p2(p1.toQPointF(), p2); + + angle1 = p1p2.angle(); + angle1F = QString().number(angle1); + + c1Length = p1p2.length(); + c1LengthF = QString().number(c1Length); + + QLineF p4p3(p4.toQPointF(), p3); + + angle2 = p4p3.angle(); + angle2F = QString().number(angle2); + + c2Length = p4p3.length(); + c2LengthF = QString().number(c2Length); +} + +//--------------------------------------------------------------------------------------------------------------------- +VSplineData::VSplineData(VPointF p1, VPointF p4, qreal angle1, const QString &angle1F, qreal angle2, + const QString &angle2F, qreal c1Length, const QString &c1LengthF, + qreal c2Length, const QString &c2LengthF, qreal kCurve) + : p1(p1), + p4(p4), + angle1(angle1), + angle1F(angle1F), + angle2(angle2), + angle2F(angle2F), + c1Length(c1Length), + c1LengthF(c1LengthF), + c2Length(c2Length), + c2LengthF(c2LengthF), + kCurve(kCurve) +{} + +//--------------------------------------------------------------------------------------------------------------------- VSplineData::~VSplineData() {} +//--------------------------------------------------------------------------------------------------------------------- +qreal VSplineData::GetL(const QPointF &p1, const QPointF &p4, qreal kCurve) +{ + static const qreal angle = 90; + const qreal radius = QLineF(p1, p4).length()/M_SQRT2; + return kCurve * radius * 4 / 3 * qTan( angle * M_PI_4 / 180.0 ); +} + #ifdef Q_CC_GNU #pragma GCC diagnostic pop #endif diff --git a/src/test/ValentinaTest/tst_vspline.cpp b/src/test/ValentinaTest/tst_vspline.cpp index 94105a878..5b3f03bc8 100644 --- a/src/test/ValentinaTest/tst_vspline.cpp +++ b/src/test/ValentinaTest/tst_vspline.cpp @@ -179,3 +179,51 @@ void TST_VSpline::GetSegmentPoints_TestPuzzle() // Begin comparison Comparison(points, origPoints); } + +//--------------------------------------------------------------------------------------------------------------------- +void TST_VSpline::CompareThreeWays() +{ + // Input data taken from real case + // See the file /src/app/share/collection/TestPuzzle.val + VPointF p1(1168.8582803149607, 39.999874015748034, "p1", 5.0000125984251973, 9.9999874015748045); + VPointF p4(681.33729132409951, 1815.7969526662778, "p4", 5.0000125984251973, 9.9999874015748045); + + VSpline spl1(p1, p4, 229.381, 41.6325, 0.96294100000000005, 1.00054, 1); + VSpline spl2(spl1.GetP1(), spl1.GetP2(), spl1.GetP3(), spl1.GetP4(), 1); + VSpline spl3(spl1.GetP1(), spl1.GetP4(), spl1.GetStartAngle(), "", spl2.GetEndAngle(), "", spl2.GetC1Length(), "", + spl2.GetC2Length(), "", 1); + + QWARN("Comparing first and second splines."); + CompareSplines(spl1, spl2); + + QWARN("Comparing second and third splines."); + CompareSplines(spl2, spl3); + + QWARN("Comparing third and first splines."); + CompareSplines(spl3, spl1); +} + +//--------------------------------------------------------------------------------------------------------------------- +void TST_VSpline::CompareSplines(const VSpline &spl1, const VSpline &spl2) const +{ + QCOMPARE(spl1.GetP1().toQPointF().toPoint(), spl2.GetP1().toQPointF().toPoint()); + QCOMPARE(spl1.GetP2().toPoint(), spl2.GetP2().toPoint()); + QCOMPARE(spl1.GetP3().toPoint(), spl2.GetP3().toPoint()); + QCOMPARE(spl1.GetP4().toQPointF().toPoint(), spl2.GetP4().toQPointF().toPoint()); + + QCOMPARE(spl1.GetStartAngle(), spl2.GetStartAngle()); + QCOMPARE(spl1.GetEndAngle(), spl2.GetEndAngle()); + + QCOMPARE(spl1.GetC1Length(), spl2.GetC1Length()); + QCOMPARE(spl1.GetC2Length(), spl2.GetC2Length()); + + QCOMPARE(spl1.GetLength(), spl2.GetLength()); + + QCOMPARE(spl1.GetKasm1(), spl2.GetKasm1()); + QCOMPARE(spl1.GetKasm2(), spl2.GetKasm2()); + + QCOMPARE(spl1.GetKcurve(), spl2.GetKcurve()); + + // Compare points + Comparison(spl1.GetPoints(), spl2.GetPoints()); +} diff --git a/src/test/ValentinaTest/tst_vspline.h b/src/test/ValentinaTest/tst_vspline.h index efcf8b2f4..c23adb191 100644 --- a/src/test/ValentinaTest/tst_vspline.h +++ b/src/test/ValentinaTest/tst_vspline.h @@ -31,6 +31,8 @@ #include "abstracttest.h" +class VSpline; + class TST_VSpline : public AbstractTest { Q_OBJECT @@ -43,6 +45,11 @@ private slots: void GetSegmentPoints(); void GetSegmentPoints_issue412(); void GetSegmentPoints_TestPuzzle(); + void CompareThreeWays(); + +private: + Q_DISABLE_COPY(TST_VSpline) + void CompareSplines(const VSpline &spl1, const VSpline &spl2) const; }; #endif // TST_VSPLINE_H