New classes VCubicBezier and VAbstractCubicBezier.

--HG--
branch : feature
This commit is contained in:
Roman Telezhynskyi 2016-03-08 19:48:10 +02:00
parent f69186b7b3
commit 1395e39c39
9 changed files with 843 additions and 414 deletions

View file

@ -0,0 +1,441 @@
/************************************************************************
**
** @file vabstractcubicbezier.cpp
** @author Roman Telezhynskyi <dismine(at)gmail.com>
** @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
** <https://bitbucket.org/dismine/valentina> 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 <http://www.gnu.org/licenses/>.
**
*************************************************************************/
#include "vabstractcubicbezier.h"
#include "../vgeometry/vpointf.h"
#include <QPainterPath>
//---------------------------------------------------------------------------------------------------------------------
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<qreal> &px, QVector<qreal> &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<int>(d2 > curve_collinearity_epsilon) << 1) +
static_cast<int>(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<qint16>(level + 1), px, py);
PointBezier_r(x1234, y1234, x234, y234, x34, y34, x4, y4, static_cast<qint16>(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<QPointF> VAbstractCubicBezier::GetCubicBezierPoints(const QPointF &p1, const QPointF &p2, const QPointF &p3,
const QPointF &p4)
{
QVector<QPointF> pvector;
QVector<qreal> x;
QVector<qreal> y;
QVector<qreal>& wx = x;
QVector<qreal>& 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<QPointF> 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();
}

View file

@ -0,0 +1,58 @@
/************************************************************************
**
** @file vabstractcubicbezier.h
** @author Roman Telezhynskyi <dismine(at)gmail.com>
** @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
** <https://bitbucket.org/dismine/valentina> 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 <http://www.gnu.org/licenses/>.
**
*************************************************************************/
#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<qreal> &px, QVector<qreal> &py);
static QVector<QPointF> 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

View file

@ -0,0 +1,142 @@
/************************************************************************
**
** @file vcubicbezier.cpp
** @author Roman Telezhynskyi <dismine(at)gmail.com>
** @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
** <https://bitbucket.org/dismine/valentina> 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 <http://www.gnu.org/licenses/>.
**
*************************************************************************/
#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<QPointF> VCubicBezier::GetPoints() const
{
return GetCubicBezierPoints(GetP1().toQPointF(), GetP2().toQPointF(), GetP3().toQPointF(), GetP4().toQPointF());
}

View file

@ -0,0 +1,69 @@
/************************************************************************
**
** @file vcubicbezier.h
** @author Roman Telezhynskyi <dismine(at)gmail.com>
** @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
** <https://bitbucket.org/dismine/valentina> 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 <http://www.gnu.org/licenses/>.
**
*************************************************************************/
#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<QPointF> GetPoints() const Q_DECL_OVERRIDE;
private:
QSharedDataPointer<VCubicBezierData> d;
};
Q_DECLARE_TYPEINFO(VCubicBezier, Q_MOVABLE_TYPE);
#endif // VCUBICBEZIER_H

View file

@ -0,0 +1,105 @@
/************************************************************************
**
** @file vcubicbezier_p.h
** @author Roman Telezhynskyi <dismine(at)gmail.com>
** @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
** <https://bitbucket.org/dismine/valentina> 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 <http://www.gnu.org/licenses/>.
**
*************************************************************************/
#ifndef VCUBICBEZIER_P_H
#define VCUBICBEZIER_P_H
#include <QSharedData>
#include <QLineF>
#include <QtCore/qmath.h>
#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

View file

@ -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

View file

@ -32,7 +32,7 @@
#include <QString>
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

View file

@ -32,12 +32,14 @@
#include <QPainterPath>
#include <QtCore/qmath.h>
#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<QPointF> 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<QPointF> VSpline::GetPoints (const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &p4)
{
QVector<QPointF> pvector;
QVector<qreal> x;
QVector<qreal> y;
QVector<qreal>& wx = x;
QVector<qreal>& 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<QPointF> 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<qreal> &px, QVector<qreal> &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<int>(d2 > curve_collinearity_epsilon) << 1) +
static_cast<int>(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<qint16>(level + 1), px, py);
PointBezier_r(x1234, y1234, x234, y234, x34, y34, x4, y4, static_cast<qint16>(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<QPointF> 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;
}

View file

@ -29,7 +29,7 @@
#ifndef VSPLINE_H
#define VSPLINE_H
#include "vabstractcurve.h"
#include "vabstractcubicbezier.h"
#include "vpointf.h"
#include <QLineF>
#include <QPointF>
@ -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<QPointF> GetPoints () const;
virtual QVector<QPointF> GetPoints () const Q_DECL_OVERRIDE;
// cppcheck-suppress unusedFunction
static QVector<QPointF> 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<QPointF> GetPoints (const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &p4 );
virtual void CreateName() Q_DECL_OVERRIDE;
private:
QSharedDataPointer<VSplineData> 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<qreal> &px, QVector<qreal> &py);
static qreal CalcSqDistance ( qreal x1, qreal y1, qreal x2, qreal y2);
QVector<qreal> CalcT(qreal curveCoord1, qreal curveCoord2, qreal curveCoord3, qreal curveCoord4,
qreal pointCoord) const;
static qint32 Cubic(QVector<qreal> &x, qreal a, qreal b, qreal c);