From 1395e39c392a5fffdebf079d54058deeaf9973db Mon Sep 17 00:00:00 2001 From: Roman Telezhynskyi Date: Tue, 8 Mar 2016 19:48:10 +0200 Subject: [PATCH] New classes VCubicBezier and VAbstractCubicBezier. --HG-- branch : feature --- src/libs/vgeometry/vabstractcubicbezier.cpp | 441 ++++++++++++++++++++ src/libs/vgeometry/vabstractcubicbezier.h | 58 +++ src/libs/vgeometry/vcubicbezier.cpp | 142 +++++++ src/libs/vgeometry/vcubicbezier.h | 69 +++ src/libs/vgeometry/vcubicbezier_p.h | 105 +++++ src/libs/vgeometry/vgeometry.pri | 9 +- src/libs/vgeometry/vgeometrydef.h | 2 +- src/libs/vgeometry/vspline.cpp | 404 +----------------- src/libs/vgeometry/vspline.h | 27 +- 9 files changed, 843 insertions(+), 414 deletions(-) create mode 100644 src/libs/vgeometry/vabstractcubicbezier.cpp create mode 100644 src/libs/vgeometry/vabstractcubicbezier.h create mode 100644 src/libs/vgeometry/vcubicbezier.cpp create mode 100644 src/libs/vgeometry/vcubicbezier.h create mode 100644 src/libs/vgeometry/vcubicbezier_p.h diff --git a/src/libs/vgeometry/vabstractcubicbezier.cpp b/src/libs/vgeometry/vabstractcubicbezier.cpp new file mode 100644 index 000000000..4cee6de99 --- /dev/null +++ b/src/libs/vgeometry/vabstractcubicbezier.cpp @@ -0,0 +1,441 @@ +/************************************************************************ + ** + ** @file vabstractcubicbezier.cpp + ** @author Roman Telezhynskyi + ** @date 8 3, 2016 + ** + ** @brief + ** @copyright + ** This source code is part of the Valentine project, a pattern making + ** program, whose allow create and modeling patterns of clothing. + ** Copyright (C) 2016 Valentina project + ** All Rights Reserved. + ** + ** Valentina is free software: you can redistribute it and/or modify + ** it under the terms of the GNU General Public License as published by + ** the Free Software Foundation, either version 3 of the License, or + ** (at your option) any later version. + ** + ** Valentina is distributed in the hope that it will be useful, + ** but WITHOUT ANY WARRANTY; without even the implied warranty of + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ** GNU General Public License for more details. + ** + ** You should have received a copy of the GNU General Public License + ** along with Valentina. If not, see . + ** + *************************************************************************/ + +#include "vabstractcubicbezier.h" +#include "../vgeometry/vpointf.h" + +#include + +//--------------------------------------------------------------------------------------------------------------------- +VAbstractCubicBezier::VAbstractCubicBezier(const GOType &type, const quint32 &idObject, const Draw &mode) + : VAbstractCurve(type, idObject, mode) +{ +} + +//--------------------------------------------------------------------------------------------------------------------- +VAbstractCubicBezier::VAbstractCubicBezier(const VAbstractCubicBezier &curve) + : VAbstractCurve(curve) +{ +} + +//--------------------------------------------------------------------------------------------------------------------- +VAbstractCubicBezier &VAbstractCubicBezier::operator=(const VAbstractCubicBezier &curve) +{ + if ( &curve == this ) + { + return *this; + } + VAbstractCurve::operator=(curve); + return *this; +} + +//--------------------------------------------------------------------------------------------------------------------- +VAbstractCubicBezier::~VAbstractCubicBezier() +{ +} + +//--------------------------------------------------------------------------------------------------------------------- +void VAbstractCubicBezier::CreateName() +{ + QString name = SPL_ + QString("%1_%2").arg(GetP1().name()).arg(GetP4().name()); + if (GetDuplicate() > 0) + { + name += QString("_%1").arg(GetDuplicate()); + } + + setName(name); +} + +//--------------------------------------------------------------------------------------------------------------------- +/** + * @brief CalcSqDistance calculate squared distance. + * @param x1 х coordinate first point. + * @param y1 у coordinate first point. + * @param x2 х coordinate second point. + * @param y2 у coordinate second point. + * @return squared length. + */ +qreal VAbstractCubicBezier::CalcSqDistance(qreal x1, qreal y1, qreal x2, qreal y2) +{ + const qreal dx = x2 - x1; + const qreal dy = y2 - y1; + return dx * dx + dy * dy; +} + +//--------------------------------------------------------------------------------------------------------------------- +/** + * @brief PointBezier_r find spline point using four point of spline. + * @param x1 х coordinate first point. + * @param y1 у coordinate first point. + * @param x2 х coordinate first control point. + * @param y2 у coordinate first control point. + * @param x3 х coordinate second control point. + * @param y3 у coordinate second control point. + * @param x4 х coordinate last point. + * @param y4 у coordinate last point. + * @param level level of recursion. In the begin 0. + * @param px list х coordinat spline points. + * @param py list у coordinat spline points. + */ +void VAbstractCubicBezier::PointBezier_r(qreal x1, qreal y1, qreal x2, qreal y2, qreal x3, qreal y3, qreal x4, qreal y4, + qint16 level, QVector &px, QVector &py) +{ + if (px.size() >= 2) + { + for (int i=1; i < px.size(); ++i) + { + if (QPointF(px.at(i-1), py.at(i-1)) == QPointF(px.at(i), py.at(i))) + { + qDebug("All neighbors points in path must be unique."); + } + } + } + + const double curve_collinearity_epsilon = 1e-30; + const double curve_angle_tolerance_epsilon = 0.01; + const double m_angle_tolerance = 0.0; + enum curve_recursion_limit_e { curve_recursion_limit = 32 }; + const double m_cusp_limit = 0.0; + double m_approximation_scale = 1.0; + double m_distance_tolerance_square; + + m_distance_tolerance_square = 0.5 / m_approximation_scale; + m_distance_tolerance_square *= m_distance_tolerance_square; + + if (level > curve_recursion_limit) + { + return; + } + + // Calculate all the mid-points of the line segments + //---------------------- + const double x12 = (x1 + x2) / 2; + const double y12 = (y1 + y2) / 2; + const double x23 = (x2 + x3) / 2; + const double y23 = (y2 + y3) / 2; + const double x34 = (x3 + x4) / 2; + const double y34 = (y3 + y4) / 2; + const double x123 = (x12 + x23) / 2; + const double y123 = (y12 + y23) / 2; + const double x234 = (x23 + x34) / 2; + const double y234 = (y23 + y34) / 2; + const double x1234 = (x123 + x234) / 2; + const double y1234 = (y123 + y234) / 2; + + + // Try to approximate the full cubic curve by a single straight line + //------------------ + const double dx = x4-x1; + const double dy = y4-y1; + + double d2 = fabs((x2 - x4) * dy - (y2 - y4) * dx); + double d3 = fabs((x3 - x4) * dy - (y3 - y4) * dx); + + switch ((static_cast(d2 > curve_collinearity_epsilon) << 1) + + static_cast(d3 > curve_collinearity_epsilon)) + { + case 0: + { + // All collinear OR p1==p4 + //---------------------- + double k = dx*dx + dy*dy; + if (k < 0.000000001) + { + d2 = CalcSqDistance(x1, y1, x2, y2); + d3 = CalcSqDistance(x4, y4, x3, y3); + } + else + { + k = 1 / k; + { + const double da1 = x2 - x1; + const double da2 = y2 - y1; + d2 = k * (da1*dx + da2*dy); + } + { + const double da1 = x3 - x1; + const double da2 = y3 - y1; + d3 = k * (da1*dx + da2*dy); + } + if (d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1) + { + // Simple collinear case, 1---2---3---4 + // We can leave just two endpoints + return; + } + if (d2 <= 0) + { + d2 = CalcSqDistance(x2, y2, x1, y1); + } + else if (d2 >= 1) + { + d2 = CalcSqDistance(x2, y2, x4, y4); + } + else + { + d2 = CalcSqDistance(x2, y2, x1 + d2*dx, y1 + d2*dy); + } + + if (d3 <= 0) + { + d3 = CalcSqDistance(x3, y3, x1, y1); + } + else if (d3 >= 1) + { + d3 = CalcSqDistance(x3, y3, x4, y4); + } + else + { + d3 = CalcSqDistance(x3, y3, x1 + d3*dx, y1 + d3*dy); + } + } + if (d2 > d3) + { + if (d2 < m_distance_tolerance_square) + { + px.append(x2); + py.append(y2); + return; + } + } + else + { + if (d3 < m_distance_tolerance_square) + { + px.append(x3); + py.append(y3); + return; + } + } + break; + } + case 1: + { + // p1,p2,p4 are collinear, p3 is significant + //---------------------- + if (d3 * d3 <= m_distance_tolerance_square * (dx*dx + dy*dy)) + { + if (m_angle_tolerance < curve_angle_tolerance_epsilon) + { + px.append(x23); + py.append(y23); + return; + } + + // Angle Condition + //---------------------- + double da1 = fabs(atan2(y4 - y3, x4 - x3) - atan2(y3 - y2, x3 - x2)); + if (da1 >= M_PI) + { + da1 = 2*M_PI - da1; + } + + if (da1 < m_angle_tolerance) + { + px.append(x2); + py.append(y2); + + px.append(x3); + py.append(y3); + return; + } + + if (m_cusp_limit > 0.0 || m_cusp_limit < 0.0) + { + if (da1 > m_cusp_limit) + { + px.append(x3); + py.append(y3); + return; + } + } + } + break; + } + case 2: + { + // p1,p3,p4 are collinear, p2 is significant + //---------------------- + if (d2 * d2 <= m_distance_tolerance_square * (dx*dx + dy*dy)) + { + if (m_angle_tolerance < curve_angle_tolerance_epsilon) + { + px.append(x23); + py.append(y23); + return; + } + + // Angle Condition + //---------------------- + double da1 = fabs(atan2(y3 - y2, x3 - x2) - atan2(y2 - y1, x2 - x1)); + if (da1 >= M_PI) + { + da1 = 2*M_PI - da1; + } + + if (da1 < m_angle_tolerance) + { + px.append(x2); + py.append(y2); + + px.append(x3); + py.append(y3); + return; + } + + if (m_cusp_limit > 0.0 || m_cusp_limit < 0.0) + { + if (da1 > m_cusp_limit) + { + px.append(x2); + py.append(y2); + return; + } + } + } + break; + } + case 3: + { + // Regular case + //----------------- + if ((d2 + d3)*(d2 + d3) <= m_distance_tolerance_square * (dx*dx + dy*dy)) + { + // If the curvature doesn't exceed the distance_tolerance value + // we tend to finish subdivisions. + //---------------------- + if (m_angle_tolerance < curve_angle_tolerance_epsilon) + { + px.append(x23); + py.append(y23); + return; + } + + // Angle & Cusp Condition + //---------------------- + const double k = atan2(y3 - y2, x3 - x2); + double da1 = fabs(k - atan2(y2 - y1, x2 - x1)); + double da2 = fabs(atan2(y4 - y3, x4 - x3) - k); + if (da1 >= M_PI) + { + da1 = 2*M_PI - da1; + } + if (da2 >= M_PI) + { + da2 = 2*M_PI - da2; + } + + if (da1 + da2 < m_angle_tolerance) + { + // Finally we can stop the recursion + //---------------------- + + px.append(x23); + py.append(y23); + return; + } + + if (m_cusp_limit > 0.0 || m_cusp_limit < 0.0) + { + if (da1 > m_cusp_limit) + { + px.append(x2); + py.append(y2); + return; + } + + if (da2 > m_cusp_limit) + { + px.append(x3); + py.append(y3); + return; + } + } + } + break; + } + default: + break; + } + + // Continue subdivision + //---------------------- + PointBezier_r(x1, y1, x12, y12, x123, y123, x1234, y1234, static_cast(level + 1), px, py); + PointBezier_r(x1234, y1234, x234, y234, x34, y34, x4, y4, static_cast(level + 1), px, py); +} + +//--------------------------------------------------------------------------------------------------------------------- +/** + * @brief GetCubicBezierPoints return list with cubic bezier curve points. + * @param p1 first spline point. + * @param p2 first control point. + * @param p3 second control point. + * @param p4 last spline point. + * @return list of points. + */ +QVector VAbstractCubicBezier::GetCubicBezierPoints(const QPointF &p1, const QPointF &p2, const QPointF &p3, + const QPointF &p4) +{ + QVector pvector; + QVector x; + QVector y; + QVector& wx = x; + QVector& wy = y; + x.append ( p1.x () ); + y.append ( p1.y () ); + PointBezier_r ( p1.x (), p1.y (), p2.x (), p2.y (), + p3.x (), p3.y (), p4.x (), p4.y (), 0, wx, wy ); + x.append ( p4.x () ); + y.append ( p4.y () ); + for ( qint32 i = 0; i < x.count(); ++i ) + { + pvector.append( QPointF ( x.at(i), y.at(i)) ); + } + return pvector; +} + +//--------------------------------------------------------------------------------------------------------------------- +/** + * @brief LengthBezier return spline length using 4 spline point. + * @param p1 first spline point + * @param p2 first control point. + * @param p3 second control point. + * @param p4 last spline point. + * @return length. + */ +qreal VAbstractCubicBezier::LengthBezier(const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &p4) +{ + QPainterPath splinePath; + QVector points = GetCubicBezierPoints(p1, p2, p3, p4); + splinePath.moveTo(points.at(0)); + for (qint32 i = 1; i < points.count(); ++i) + { + splinePath.lineTo(points.at(i)); + } + return splinePath.length(); +} diff --git a/src/libs/vgeometry/vabstractcubicbezier.h b/src/libs/vgeometry/vabstractcubicbezier.h new file mode 100644 index 000000000..f6d74156d --- /dev/null +++ b/src/libs/vgeometry/vabstractcubicbezier.h @@ -0,0 +1,58 @@ +/************************************************************************ + ** + ** @file vabstractcubicbezier.h + ** @author Roman Telezhynskyi + ** @date 8 3, 2016 + ** + ** @brief + ** @copyright + ** This source code is part of the Valentine project, a pattern making + ** program, whose allow create and modeling patterns of clothing. + ** Copyright (C) 2016 Valentina project + ** All Rights Reserved. + ** + ** Valentina is free software: you can redistribute it and/or modify + ** it under the terms of the GNU General Public License as published by + ** the Free Software Foundation, either version 3 of the License, or + ** (at your option) any later version. + ** + ** Valentina is distributed in the hope that it will be useful, + ** but WITHOUT ANY WARRANTY; without even the implied warranty of + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ** GNU General Public License for more details. + ** + ** You should have received a copy of the GNU General Public License + ** along with Valentina. If not, see . + ** + *************************************************************************/ + +#ifndef VABSTRACTCUBICBEZIER_H +#define VABSTRACTCUBICBEZIER_H + +#include "vabstractcurve.h" + +class VPointF; + +class VAbstractCubicBezier : public VAbstractCurve +{ +public: + VAbstractCubicBezier(const GOType &type, const quint32 &idObject = NULL_ID, const Draw &mode = Draw::Calculation); + VAbstractCubicBezier(const VAbstractCubicBezier &curve); + VAbstractCubicBezier& operator= (const VAbstractCubicBezier &curve); + virtual ~VAbstractCubicBezier(); + + virtual VPointF GetP1 () const =0; + virtual VPointF GetP4 () const =0; + +protected: + virtual void CreateName() Q_DECL_OVERRIDE; + + static qreal CalcSqDistance(qreal x1, qreal y1, qreal x2, qreal y2); + static void PointBezier_r(qreal x1, qreal y1, qreal x2, qreal y2, qreal x3, qreal y3, qreal x4, + qreal y4, qint16 level, QVector &px, QVector &py); + static QVector GetCubicBezierPoints(const QPointF &p1, const QPointF &p2, const QPointF &p3, + const QPointF &p4); + static qreal LengthBezier(const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &p4); +}; + +#endif // VABSTRACTCUBICBEZIER_H diff --git a/src/libs/vgeometry/vcubicbezier.cpp b/src/libs/vgeometry/vcubicbezier.cpp new file mode 100644 index 000000000..cf8d7559a --- /dev/null +++ b/src/libs/vgeometry/vcubicbezier.cpp @@ -0,0 +1,142 @@ +/************************************************************************ + ** + ** @file vcubicbezier.cpp + ** @author Roman Telezhynskyi + ** @date 8 3, 2016 + ** + ** @brief + ** @copyright + ** This source code is part of the Valentine project, a pattern making + ** program, whose allow create and modeling patterns of clothing. + ** Copyright (C) 2016 Valentina project + ** All Rights Reserved. + ** + ** Valentina is free software: you can redistribute it and/or modify + ** it under the terms of the GNU General Public License as published by + ** the Free Software Foundation, either version 3 of the License, or + ** (at your option) any later version. + ** + ** Valentina is distributed in the hope that it will be useful, + ** but WITHOUT ANY WARRANTY; without even the implied warranty of + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ** GNU General Public License for more details. + ** + ** You should have received a copy of the GNU General Public License + ** along with Valentina. If not, see . + ** + *************************************************************************/ + +#include "vcubicbezier.h" +#include "vcubicbezier_p.h" + +//--------------------------------------------------------------------------------------------------------------------- +VCubicBezier::VCubicBezier() + : VAbstractCubicBezier(GOType::CubicBezier), d(new VCubicBezierData) +{ +} + +//--------------------------------------------------------------------------------------------------------------------- +VCubicBezier::VCubicBezier(const VCubicBezier &curve) + : VAbstractCubicBezier(curve), d(curve.d) +{ +} + +//--------------------------------------------------------------------------------------------------------------------- +VCubicBezier::VCubicBezier(const VPointF &p1, const VPointF &p2, const VPointF &p3, const VPointF &p4, quint32 idObject, + Draw mode) + : VAbstractCubicBezier(GOType::CubicBezier, idObject, mode), d(new VCubicBezierData(p1, p2, p3, p4)) +{ + CreateName(); +} + +//--------------------------------------------------------------------------------------------------------------------- +VCubicBezier &VCubicBezier::operator=(const VCubicBezier &curve) +{ + if ( &curve == this ) + { + return *this; + } + VAbstractCubicBezier::operator=(curve); + d = curve.d; + return *this; +} + +//--------------------------------------------------------------------------------------------------------------------- +VPointF VCubicBezier::GetP1() const +{ + return d->p1; +} + +//--------------------------------------------------------------------------------------------------------------------- +void VCubicBezier::SetP1(const VPointF &p) +{ + d->p1 = p; +} + +//--------------------------------------------------------------------------------------------------------------------- +VPointF VCubicBezier::GetP2() const +{ + return d->p2; +} + +//--------------------------------------------------------------------------------------------------------------------- +void VCubicBezier::SetP2(const VPointF &p) +{ + d->p2 = p; +} + +//--------------------------------------------------------------------------------------------------------------------- +VPointF VCubicBezier::GetP3() const +{ + return d->p3; +} + +//--------------------------------------------------------------------------------------------------------------------- +void VCubicBezier::SetP3(const VPointF &p) +{ + d->p3 = p; +} + +//--------------------------------------------------------------------------------------------------------------------- +VPointF VCubicBezier::GetP4() const +{ + return d->p4; +} + +//--------------------------------------------------------------------------------------------------------------------- +void VCubicBezier::SetP4(const VPointF &p) +{ + d->p4 = p; +} + +//--------------------------------------------------------------------------------------------------------------------- +qreal VCubicBezier::GetStartAngle() const +{ + return QLineF(GetP1().toQPointF(), GetP2().toQPointF()).angle(); +} + +//--------------------------------------------------------------------------------------------------------------------- +qreal VCubicBezier::GetEndAngle() const +{ + return QLineF(GetP4().toQPointF(), GetP3().toQPointF()).angle(); +} + +//--------------------------------------------------------------------------------------------------------------------- +/** + * @brief GetLength return length of cubic bezier curve. + * @return length. + */ +qreal VCubicBezier::GetLength() const +{ + return LengthBezier (GetP1().toQPointF(), GetP2().toQPointF(), GetP3().toQPointF(), GetP4().toQPointF()); +} + +//--------------------------------------------------------------------------------------------------------------------- +/** + * @brief GetPoints return list with cubic bezier curve points. + * @return list of points. + */ +QVector VCubicBezier::GetPoints() const +{ + return GetCubicBezierPoints(GetP1().toQPointF(), GetP2().toQPointF(), GetP3().toQPointF(), GetP4().toQPointF()); +} diff --git a/src/libs/vgeometry/vcubicbezier.h b/src/libs/vgeometry/vcubicbezier.h new file mode 100644 index 000000000..720e0d9db --- /dev/null +++ b/src/libs/vgeometry/vcubicbezier.h @@ -0,0 +1,69 @@ +/************************************************************************ + ** + ** @file vcubicbezier.h + ** @author Roman Telezhynskyi + ** @date 8 3, 2016 + ** + ** @brief + ** @copyright + ** This source code is part of the Valentine project, a pattern making + ** program, whose allow create and modeling patterns of clothing. + ** Copyright (C) 2016 Valentina project + ** All Rights Reserved. + ** + ** Valentina is free software: you can redistribute it and/or modify + ** it under the terms of the GNU General Public License as published by + ** the Free Software Foundation, either version 3 of the License, or + ** (at your option) any later version. + ** + ** Valentina is distributed in the hope that it will be useful, + ** but WITHOUT ANY WARRANTY; without even the implied warranty of + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ** GNU General Public License for more details. + ** + ** You should have received a copy of the GNU General Public License + ** along with Valentina. If not, see . + ** + *************************************************************************/ + +#ifndef VCUBICBEZIER_H +#define VCUBICBEZIER_H + +#include "vabstractcubicbezier.h" +#include "vpointf.h" + +class VCubicBezierData; + +class VCubicBezier : public VAbstractCubicBezier +{ +public: + VCubicBezier(); + VCubicBezier(const VCubicBezier &curve); + VCubicBezier(const VPointF &p1, const VPointF &p2, const VPointF &p3, const VPointF &p4, quint32 idObject = 0, + Draw mode = Draw::Calculation); + VCubicBezier &operator=(const VCubicBezier &curve); + + virtual VPointF GetP1() const Q_DECL_OVERRIDE; + void SetP1(const VPointF &p); + + VPointF GetP2() const; + void SetP2(const VPointF &p); + + VPointF GetP3() const; + void SetP3(const VPointF &p); + + virtual VPointF GetP4() const Q_DECL_OVERRIDE; + void SetP4(const VPointF &p); + + virtual qreal GetStartAngle() const Q_DECL_OVERRIDE; + virtual qreal GetEndAngle() const Q_DECL_OVERRIDE; + virtual qreal GetLength() const Q_DECL_OVERRIDE; + virtual QVector GetPoints() const Q_DECL_OVERRIDE; + +private: + QSharedDataPointer d; +}; + +Q_DECLARE_TYPEINFO(VCubicBezier, Q_MOVABLE_TYPE); + +#endif // VCUBICBEZIER_H diff --git a/src/libs/vgeometry/vcubicbezier_p.h b/src/libs/vgeometry/vcubicbezier_p.h new file mode 100644 index 000000000..168d2d127 --- /dev/null +++ b/src/libs/vgeometry/vcubicbezier_p.h @@ -0,0 +1,105 @@ +/************************************************************************ + ** + ** @file vcubicbezier_p.h + ** @author Roman Telezhynskyi + ** @date 8 3, 2016 + ** + ** @brief + ** @copyright + ** This source code is part of the Valentine project, a pattern making + ** program, whose allow create and modeling patterns of clothing. + ** Copyright (C) 2016 Valentina project + ** All Rights Reserved. + ** + ** Valentina is free software: you can redistribute it and/or modify + ** it under the terms of the GNU General Public License as published by + ** the Free Software Foundation, either version 3 of the License, or + ** (at your option) any later version. + ** + ** Valentina is distributed in the hope that it will be useful, + ** but WITHOUT ANY WARRANTY; without even the implied warranty of + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ** GNU General Public License for more details. + ** + ** You should have received a copy of the GNU General Public License + ** along with Valentina. If not, see . + ** + *************************************************************************/ + +#ifndef VCUBICBEZIER_P_H +#define VCUBICBEZIER_P_H + +#include +#include +#include + +#include "vpointf.h" +#include "../vmisc/vabstractapplication.h" + +#ifdef Q_CC_GNU + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Weffc++" +#endif + +class VCubicBezierData : public QSharedData +{ +public: + VCubicBezierData(); + VCubicBezierData(const VCubicBezierData &spline); + VCubicBezierData(const VPointF &p1, const VPointF &p2, const VPointF &p3, const VPointF &p4); + virtual ~VCubicBezierData(); + + /** @brief p1 first spline point. */ + VPointF p1; + + /** @brief p2 fourth spline point. */ + VPointF p2; + + /** @brief p3 fourth spline point. */ + VPointF p3; + + /** @brief p4 fourth spline point. */ + VPointF p4; + +private: + VCubicBezierData &operator=(const VCubicBezierData &) Q_DECL_EQ_DELETE; +}; + +//--------------------------------------------------------------------------------------------------------------------- +VCubicBezierData::VCubicBezierData() + : p1(), + p2(), + p3(), + p4() +{ +} + +//--------------------------------------------------------------------------------------------------------------------- +VCubicBezierData::VCubicBezierData(const VCubicBezierData &curve) + : QSharedData(curve), + p1(curve.p1), + p2(curve.p2), + p3(curve.p3), + p4(curve.p4) +{ +} + +//--------------------------------------------------------------------------------------------------------------------- +VCubicBezierData::VCubicBezierData(const VPointF &p1, const VPointF &p2, const VPointF &p3, const VPointF &p4) + : p1(p1), + p2(p2), + p3(p3), + p4(p4) +{ +} + +//--------------------------------------------------------------------------------------------------------------------- +VCubicBezierData::~VCubicBezierData() +{ +} + +#ifdef Q_CC_GNU +#pragma GCC diagnostic pop +#endif + +#endif // VCUBICBEZIER_P_H diff --git a/src/libs/vgeometry/vgeometry.pri b/src/libs/vgeometry/vgeometry.pri index 1c80d0ed3..94c1f4a34 100644 --- a/src/libs/vgeometry/vgeometry.pri +++ b/src/libs/vgeometry/vgeometry.pri @@ -9,7 +9,9 @@ SOURCES += \ $$PWD/vspline.cpp \ $$PWD/vsplinepath.cpp \ $$PWD/vsplinepoint.cpp \ - $$PWD/vellipticalarc.cpp + $$PWD/vellipticalarc.cpp \ + $$PWD/vcubicbezier.cpp \ + $$PWD/vabstractcubicbezier.cpp win32-msvc*:SOURCES += $$PWD/stable.cpp @@ -31,4 +33,7 @@ HEADERS += \ $$PWD/vgeometrydef.h \ $$PWD/vellipticalarc.h \ $$PWD/vellipticalarc_p.h \ - $$PWD/vabstractcurve_p.h + $$PWD/vabstractcurve_p.h \ + $$PWD/vcubicbezier.h \ + $$PWD/vcubicbezier_p.h \ + $$PWD/vabstractcubicbezier.h diff --git a/src/libs/vgeometry/vgeometrydef.h b/src/libs/vgeometry/vgeometrydef.h index 9c8840e72..67e60764f 100644 --- a/src/libs/vgeometry/vgeometrydef.h +++ b/src/libs/vgeometry/vgeometrydef.h @@ -32,7 +32,7 @@ #include enum class Draw : char { Calculation, Modeling, Layout }; -enum class GOType : char { Point, Arc, EllipticalArc, Spline, SplinePath, Unknown }; +enum class GOType : char { Point, Arc, EllipticalArc, Spline, SplinePath, CubicBezier, Unknown }; enum class SplinePointPosition : char { FirstPoint, LastPoint }; #endif // VGEOMETRYDEF_H diff --git a/src/libs/vgeometry/vspline.cpp b/src/libs/vgeometry/vspline.cpp index 699819b93..d4c7eaf3b 100644 --- a/src/libs/vgeometry/vspline.cpp +++ b/src/libs/vgeometry/vspline.cpp @@ -32,12 +32,14 @@ #include #include +#define M_2PI 6.28318530717958647692528676655900576 + //--------------------------------------------------------------------------------------------------------------------- /** * @brief VSpline default constructor */ VSpline::VSpline() - :VAbstractCurve(GOType::Spline), d(new VSplineData) + :VAbstractCubicBezier(GOType::Spline), d(new VSplineData) {} //--------------------------------------------------------------------------------------------------------------------- @@ -46,7 +48,7 @@ VSpline::VSpline() * @param spline spline from which the copy. */ VSpline::VSpline ( const VSpline & spline ) - :VAbstractCurve(spline), d(spline.d) + :VAbstractCubicBezier(spline), d(spline.d) {} //--------------------------------------------------------------------------------------------------------------------- @@ -62,7 +64,8 @@ VSpline::VSpline ( const VSpline & spline ) */ VSpline::VSpline (VPointF p1, VPointF p4, qreal angle1, qreal angle2, qreal kAsm1, qreal kAsm2, qreal kCurve, quint32 idObject, Draw mode) - :VAbstractCurve(GOType::Spline, idObject, mode), d(new VSplineData(p1, p4, angle1, angle2, kAsm1, kAsm2, kCurve)) + : VAbstractCubicBezier(GOType::Spline, idObject, mode), + d(new VSplineData(p1, p4, angle1, angle2, kAsm1, kAsm2, kCurve)) { CreateName(); } @@ -76,7 +79,7 @@ VSpline::VSpline (VPointF p1, VPointF p4, qreal angle1, qreal angle2, qreal kAsm * @param p4 second point spline. */ VSpline::VSpline (VPointF p1, QPointF p2, QPointF p3, VPointF p4, quint32 idObject, Draw mode) - :VAbstractCurve(GOType::Spline, idObject, mode), d(new VSplineData(p1, p2, p3, p4)) + :VAbstractCubicBezier(GOType::Spline, idObject, mode), d(new VSplineData(p1, p2, p3, p4)) { CreateName(); } @@ -101,14 +104,13 @@ VSpline::VSpline (VPointF p1, QPointF p2, QPointF p3, VPointF p4, quint32 idObje 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, quint32 idObject, Draw mode) - : VAbstractCurve(GOType::Spline, idObject, mode), + : VAbstractCubicBezier(GOType::Spline, idObject, mode), d(new VSplineData(p1, p4, angle1, angle1Formula, angle2,angle2Formula, c1Length, c1LengthFormula, c2Length, c2LengthFormula)) { CreateName(); } - //--------------------------------------------------------------------------------------------------------------------- VSpline::~VSpline() {} @@ -249,391 +251,7 @@ QPointF VSpline::CutSpline(qreal length, VSpline &spl1, VSpline &spl2) const */ QVector VSpline::GetPoints () const { - return GetPoints(GetP1().toQPointF(), GetP2(), GetP3(), GetP4().toQPointF()); -} - -//--------------------------------------------------------------------------------------------------------------------- -/** - * @brief GetPoints return list with spline points. - * @param p1 first spline point. - * @param p2 first control point. - * @param p3 second control point. - * @param p4 last spline point. - * @return list of points. - */ -QVector VSpline::GetPoints (const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &p4) -{ - QVector pvector; - QVector x; - QVector y; - QVector& wx = x; - QVector& wy = y; - x.append ( p1.x () ); - y.append ( p1.y () ); - PointBezier_r ( p1.x (), p1.y (), p2.x (), p2.y (), - p3.x (), p3.y (), p4.x (), p4.y (), 0, wx, wy ); - x.append ( p4.x () ); - y.append ( p4.y () ); - for ( qint32 i = 0; i < x.count(); ++i ) - { - pvector.append( QPointF ( x.at(i), y.at(i)) ); - } - return pvector; -} - -//--------------------------------------------------------------------------------------------------------------------- -/** - * @brief LengthBezier return spline length using 4 spline point. - * @param p1 first spline point - * @param p2 first control point. - * @param p3 second control point. - * @param p4 last spline point. - * @return length. - */ -qreal VSpline::LengthBezier ( const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &p4 ) -{ - QPainterPath splinePath; - QVector points = GetPoints (p1, p2, p3, p4); - splinePath.moveTo(points.at(0)); - for (qint32 i = 1; i < points.count(); ++i) - { - splinePath.lineTo(points.at(i)); - } - return splinePath.length(); -} - -//--------------------------------------------------------------------------------------------------------------------- -/** - * @brief PointBezier_r find spline point using four point of spline. - * @param x1 х coordinate first point. - * @param y1 у coordinate first point. - * @param x2 х coordinate first control point. - * @param y2 у coordinate first control point. - * @param x3 х coordinate second control point. - * @param y3 у coordinate second control point. - * @param x4 х coordinate last point. - * @param y4 у coordinate last point. - * @param level level of recursion. In the begin 0. - * @param px list х coordinat spline points. - * @param py list у coordinat spline points. - */ -void VSpline::PointBezier_r ( qreal x1, qreal y1, qreal x2, qreal y2, - qreal x3, qreal y3, qreal x4, qreal y4, - qint16 level, QVector &px, QVector &py) -{ - if (px.size() >= 2) - { - for (int i=1; i < px.size(); ++i) - { - if (QPointF(px.at(i-1), py.at(i-1)) == QPointF(px.at(i), py.at(i))) - { - qDebug("All neighbors points in path must be unique."); - } - } - } - - const double curve_collinearity_epsilon = 1e-30; - const double curve_angle_tolerance_epsilon = 0.01; - const double m_angle_tolerance = 0.0; - enum curve_recursion_limit_e { curve_recursion_limit = 32 }; - const double m_cusp_limit = 0.0; - double m_approximation_scale = 1.0; - double m_distance_tolerance_square; - - m_distance_tolerance_square = 0.5 / m_approximation_scale; - m_distance_tolerance_square *= m_distance_tolerance_square; - - if (level > curve_recursion_limit) - { - return; - } - - // Calculate all the mid-points of the line segments - //---------------------- - const double x12 = (x1 + x2) / 2; - const double y12 = (y1 + y2) / 2; - const double x23 = (x2 + x3) / 2; - const double y23 = (y2 + y3) / 2; - const double x34 = (x3 + x4) / 2; - const double y34 = (y3 + y4) / 2; - const double x123 = (x12 + x23) / 2; - const double y123 = (y12 + y23) / 2; - const double x234 = (x23 + x34) / 2; - const double y234 = (y23 + y34) / 2; - const double x1234 = (x123 + x234) / 2; - const double y1234 = (y123 + y234) / 2; - - - // Try to approximate the full cubic curve by a single straight line - //------------------ - const double dx = x4-x1; - const double dy = y4-y1; - - double d2 = fabs((x2 - x4) * dy - (y2 - y4) * dx); - double d3 = fabs((x3 - x4) * dy - (y3 - y4) * dx); - - switch ((static_cast(d2 > curve_collinearity_epsilon) << 1) + - static_cast(d3 > curve_collinearity_epsilon)) - { - case 0: - { - // All collinear OR p1==p4 - //---------------------- - double k = dx*dx + dy*dy; - if (k < 0.000000001) - { - d2 = CalcSqDistance(x1, y1, x2, y2); - d3 = CalcSqDistance(x4, y4, x3, y3); - } - else - { - k = 1 / k; - { - const double da1 = x2 - x1; - const double da2 = y2 - y1; - d2 = k * (da1*dx + da2*dy); - } - { - const double da1 = x3 - x1; - const double da2 = y3 - y1; - d3 = k * (da1*dx + da2*dy); - } - if (d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1) - { - // Simple collinear case, 1---2---3---4 - // We can leave just two endpoints - return; - } - if (d2 <= 0) - { - d2 = CalcSqDistance(x2, y2, x1, y1); - } - else if (d2 >= 1) - { - d2 = CalcSqDistance(x2, y2, x4, y4); - } - else - { - d2 = CalcSqDistance(x2, y2, x1 + d2*dx, y1 + d2*dy); - } - - if (d3 <= 0) - { - d3 = CalcSqDistance(x3, y3, x1, y1); - } - else if (d3 >= 1) - { - d3 = CalcSqDistance(x3, y3, x4, y4); - } - else - { - d3 = CalcSqDistance(x3, y3, x1 + d3*dx, y1 + d3*dy); - } - } - if (d2 > d3) - { - if (d2 < m_distance_tolerance_square) - { - px.append(x2); - py.append(y2); - return; - } - } - else - { - if (d3 < m_distance_tolerance_square) - { - px.append(x3); - py.append(y3); - return; - } - } - break; - } - case 1: - { - // p1,p2,p4 are collinear, p3 is significant - //---------------------- - if (d3 * d3 <= m_distance_tolerance_square * (dx*dx + dy*dy)) - { - if (m_angle_tolerance < curve_angle_tolerance_epsilon) - { - px.append(x23); - py.append(y23); - return; - } - - // Angle Condition - //---------------------- - double da1 = fabs(atan2(y4 - y3, x4 - x3) - atan2(y3 - y2, x3 - x2)); - if (da1 >= M_PI) - { - da1 = 2*M_PI - da1; - } - - if (da1 < m_angle_tolerance) - { - px.append(x2); - py.append(y2); - - px.append(x3); - py.append(y3); - return; - } - - if (m_cusp_limit > 0.0 || m_cusp_limit < 0.0) - { - if (da1 > m_cusp_limit) - { - px.append(x3); - py.append(y3); - return; - } - } - } - break; - } - case 2: - { - // p1,p3,p4 are collinear, p2 is significant - //---------------------- - if (d2 * d2 <= m_distance_tolerance_square * (dx*dx + dy*dy)) - { - if (m_angle_tolerance < curve_angle_tolerance_epsilon) - { - px.append(x23); - py.append(y23); - return; - } - - // Angle Condition - //---------------------- - double da1 = fabs(atan2(y3 - y2, x3 - x2) - atan2(y2 - y1, x2 - x1)); - if (da1 >= M_PI) - { - da1 = 2*M_PI - da1; - } - - if (da1 < m_angle_tolerance) - { - px.append(x2); - py.append(y2); - - px.append(x3); - py.append(y3); - return; - } - - if (m_cusp_limit > 0.0 || m_cusp_limit < 0.0) - { - if (da1 > m_cusp_limit) - { - px.append(x2); - py.append(y2); - return; - } - } - } - break; - } - case 3: - { - // Regular case - //----------------- - if ((d2 + d3)*(d2 + d3) <= m_distance_tolerance_square * (dx*dx + dy*dy)) - { - // If the curvature doesn't exceed the distance_tolerance value - // we tend to finish subdivisions. - //---------------------- - if (m_angle_tolerance < curve_angle_tolerance_epsilon) - { - px.append(x23); - py.append(y23); - return; - } - - // Angle & Cusp Condition - //---------------------- - const double k = atan2(y3 - y2, x3 - x2); - double da1 = fabs(k - atan2(y2 - y1, x2 - x1)); - double da2 = fabs(atan2(y4 - y3, x4 - x3) - k); - if (da1 >= M_PI) - { - da1 = 2*M_PI - da1; - } - if (da2 >= M_PI) - { - da2 = 2*M_PI - da2; - } - - if (da1 + da2 < m_angle_tolerance) - { - // Finally we can stop the recursion - //---------------------- - - px.append(x23); - py.append(y23); - return; - } - - if (m_cusp_limit > 0.0 || m_cusp_limit < 0.0) - { - if (da1 > m_cusp_limit) - { - px.append(x2); - py.append(y2); - return; - } - - if (da2 > m_cusp_limit) - { - px.append(x3); - py.append(y3); - return; - } - } - } - break; - } - default: - break; - } - - // Continue subdivision - //---------------------- - PointBezier_r(x1, y1, x12, y12, x123, y123, x1234, y1234, static_cast(level + 1), px, py); - PointBezier_r(x1234, y1234, x234, y234, x34, y34, x4, y4, static_cast(level + 1), px, py); -} - -//--------------------------------------------------------------------------------------------------------------------- -/** - * @brief CalcSqDistance calculate squared distance. - * @param x1 х coordinate first point. - * @param y1 у coordinate first point. - * @param x2 х coordinate second point. - * @param y2 у coordinate second point. - * @return squared length. - */ -qreal VSpline::CalcSqDistance (qreal x1, qreal y1, qreal x2, qreal y2) -{ - const qreal dx = x2 - x1; - const qreal dy = y2 - y1; - return dx * dx + dy * dy; -} - -//--------------------------------------------------------------------------------------------------------------------- -/** - * @brief CreateName create spline name. - */ -void VSpline::CreateName() -{ - QString name = SPL_ + QString("%1_%2").arg(this->GetP1().name()).arg(this->GetP4().name()); - if (GetDuplicate() > 0) - { - name += QString("_%1").arg(GetDuplicate()); - } - - setName(name); + return GetCubicBezierPoints(GetP1().toQPointF(), GetP2(), GetP3(), GetP4().toQPointF()); } //--------------------------------------------------------------------------------------------------------------------- @@ -663,7 +281,7 @@ QVector VSpline::SplinePoints(const QPointF &p1, const QPointF &p4, qre p4p3.setAngle(angle2); QPointF p2 = p1p2.p2(); QPointF p3 = p4p3.p2(); - return GetPoints(p1, p2, p3, p4); + return GetCubicBezierPoints(p1, p2, p3, p4); } //--------------------------------------------------------------------------------------------------------------------- @@ -673,7 +291,7 @@ VSpline &VSpline::operator =(const VSpline &spline) { return *this; } - VAbstractCurve::operator=(spline); + VAbstractCubicBezier::operator=(spline); d = spline.d; return *this; } diff --git a/src/libs/vgeometry/vspline.h b/src/libs/vgeometry/vspline.h index 7b09a10ec..5114694ab 100644 --- a/src/libs/vgeometry/vspline.h +++ b/src/libs/vgeometry/vspline.h @@ -29,7 +29,7 @@ #ifndef VSPLINE_H #define VSPLINE_H -#include "vabstractcurve.h" +#include "vabstractcubicbezier.h" #include "vpointf.h" #include #include @@ -37,12 +37,10 @@ class QPainterPath; class VSplineData; -#define M_2PI 6.28318530717958647692528676655900576 - /** * @brief VSpline class that implements the spline. */ -class VSpline :public VAbstractCurve +class VSpline :public VAbstractCubicBezier { public: VSpline(); @@ -54,17 +52,17 @@ public: 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, quint32 idObject = 0, Draw mode = Draw::Calculation); - virtual ~VSpline() Q_DECL_OVERRIDE; + virtual ~VSpline(); VSpline &operator=(const VSpline &spl); - VPointF GetP1 () const; - void SetP1 (const VPointF &p); + virtual VPointF GetP1 () const Q_DECL_OVERRIDE; + void SetP1 (const VPointF &p); QPointF GetP2 () const; QPointF GetP3 () const; - VPointF GetP4 () const; - void SetP4 (const VPointF &p); + virtual VPointF GetP4 () const Q_DECL_OVERRIDE; + void SetP4 (const VPointF &p); virtual qreal GetStartAngle () const Q_DECL_OVERRIDE; virtual qreal GetEndAngle() const Q_DECL_OVERRIDE; @@ -84,27 +82,20 @@ public: void SetC1Length(qreal length, const QString &formula); void SetC2Length(qreal length, const QString &formula); - qreal GetLength () const; + virtual qreal GetLength () const Q_DECL_OVERRIDE; qreal GetKasm1() const; qreal GetKasm2() const; qreal GetKcurve() const; qreal LengthT(qreal t) const; QPointF CutSpline ( qreal length, QPointF &spl1p2, QPointF &spl1p3, QPointF &spl2p2, QPointF &spl2p3) const; QPointF CutSpline ( qreal length, VSpline &spl1, VSpline &spl2) const; - QVector GetPoints () const; + virtual QVector GetPoints () const Q_DECL_OVERRIDE; // cppcheck-suppress unusedFunction static QVector SplinePoints(const QPointF &p1, const QPointF &p4, qreal angle1, qreal angle2, qreal kAsm1, qreal kAsm2, qreal kCurve); qreal ParamT(const QPointF &pBt) const; -protected: - static QVector GetPoints (const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &p4 ); - virtual void CreateName() Q_DECL_OVERRIDE; private: QSharedDataPointer d; - static qreal LengthBezier (const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &p4 ); - static void PointBezier_r ( qreal x1, qreal y1, qreal x2, qreal y2, qreal x3, qreal y3, qreal x4, qreal y4, - qint16 level, QVector &px, QVector &py); - static qreal CalcSqDistance ( qreal x1, qreal y1, qreal x2, qreal y2); QVector CalcT(qreal curveCoord1, qreal curveCoord2, qreal curveCoord3, qreal curveCoord4, qreal pointCoord) const; static qint32 Cubic(QVector &x, qreal a, qreal b, qreal c);