New classes VCubicBezier and VAbstractCubicBezier.
--HG-- branch : feature
This commit is contained in:
parent
f69186b7b3
commit
1395e39c39
441
src/libs/vgeometry/vabstractcubicbezier.cpp
Normal file
441
src/libs/vgeometry/vabstractcubicbezier.cpp
Normal 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();
|
||||||
|
}
|
58
src/libs/vgeometry/vabstractcubicbezier.h
Normal file
58
src/libs/vgeometry/vabstractcubicbezier.h
Normal 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
|
142
src/libs/vgeometry/vcubicbezier.cpp
Normal file
142
src/libs/vgeometry/vcubicbezier.cpp
Normal 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());
|
||||||
|
}
|
69
src/libs/vgeometry/vcubicbezier.h
Normal file
69
src/libs/vgeometry/vcubicbezier.h
Normal 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
|
105
src/libs/vgeometry/vcubicbezier_p.h
Normal file
105
src/libs/vgeometry/vcubicbezier_p.h
Normal 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
|
|
@ -9,7 +9,9 @@ SOURCES += \
|
||||||
$$PWD/vspline.cpp \
|
$$PWD/vspline.cpp \
|
||||||
$$PWD/vsplinepath.cpp \
|
$$PWD/vsplinepath.cpp \
|
||||||
$$PWD/vsplinepoint.cpp \
|
$$PWD/vsplinepoint.cpp \
|
||||||
$$PWD/vellipticalarc.cpp
|
$$PWD/vellipticalarc.cpp \
|
||||||
|
$$PWD/vcubicbezier.cpp \
|
||||||
|
$$PWD/vabstractcubicbezier.cpp
|
||||||
|
|
||||||
win32-msvc*:SOURCES += $$PWD/stable.cpp
|
win32-msvc*:SOURCES += $$PWD/stable.cpp
|
||||||
|
|
||||||
|
@ -31,4 +33,7 @@ HEADERS += \
|
||||||
$$PWD/vgeometrydef.h \
|
$$PWD/vgeometrydef.h \
|
||||||
$$PWD/vellipticalarc.h \
|
$$PWD/vellipticalarc.h \
|
||||||
$$PWD/vellipticalarc_p.h \
|
$$PWD/vellipticalarc_p.h \
|
||||||
$$PWD/vabstractcurve_p.h
|
$$PWD/vabstractcurve_p.h \
|
||||||
|
$$PWD/vcubicbezier.h \
|
||||||
|
$$PWD/vcubicbezier_p.h \
|
||||||
|
$$PWD/vabstractcubicbezier.h
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
enum class Draw : char { Calculation, Modeling, Layout };
|
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 };
|
enum class SplinePointPosition : char { FirstPoint, LastPoint };
|
||||||
|
|
||||||
#endif // VGEOMETRYDEF_H
|
#endif // VGEOMETRYDEF_H
|
||||||
|
|
|
@ -32,12 +32,14 @@
|
||||||
#include <QPainterPath>
|
#include <QPainterPath>
|
||||||
#include <QtCore/qmath.h>
|
#include <QtCore/qmath.h>
|
||||||
|
|
||||||
|
#define M_2PI 6.28318530717958647692528676655900576
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------------------------------------------
|
||||||
/**
|
/**
|
||||||
* @brief VSpline default constructor
|
* @brief VSpline default constructor
|
||||||
*/
|
*/
|
||||||
VSpline::VSpline()
|
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.
|
* @param spline spline from which the copy.
|
||||||
*/
|
*/
|
||||||
VSpline::VSpline ( const VSpline & spline )
|
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,
|
VSpline::VSpline (VPointF p1, VPointF p4, qreal angle1, qreal angle2, qreal kAsm1, qreal kAsm2, qreal kCurve,
|
||||||
quint32 idObject, Draw mode)
|
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();
|
CreateName();
|
||||||
}
|
}
|
||||||
|
@ -76,7 +79,7 @@ VSpline::VSpline (VPointF p1, VPointF p4, qreal angle1, qreal angle2, qreal kAsm
|
||||||
* @param p4 second point spline.
|
* @param p4 second point spline.
|
||||||
*/
|
*/
|
||||||
VSpline::VSpline (VPointF p1, QPointF p2, QPointF p3, VPointF p4, quint32 idObject, Draw mode)
|
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();
|
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,
|
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 &angle2Formula, qreal c1Length, const QString &c1LengthFormula, qreal c2Length,
|
||||||
const QString &c2LengthFormula, quint32 idObject, Draw mode)
|
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,
|
d(new VSplineData(p1, p4, angle1, angle1Formula, angle2,angle2Formula, c1Length, c1LengthFormula, c2Length,
|
||||||
c2LengthFormula))
|
c2LengthFormula))
|
||||||
{
|
{
|
||||||
CreateName();
|
CreateName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------------------------------------------
|
||||||
VSpline::~VSpline()
|
VSpline::~VSpline()
|
||||||
{}
|
{}
|
||||||
|
@ -249,391 +251,7 @@ QPointF VSpline::CutSpline(qreal length, VSpline &spl1, VSpline &spl2) const
|
||||||
*/
|
*/
|
||||||
QVector<QPointF> VSpline::GetPoints () const
|
QVector<QPointF> VSpline::GetPoints () const
|
||||||
{
|
{
|
||||||
return GetPoints(GetP1().toQPointF(), GetP2(), GetP3(), GetP4().toQPointF());
|
return GetCubicBezierPoints(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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------------------------------------------
|
||||||
|
@ -663,7 +281,7 @@ QVector<QPointF> VSpline::SplinePoints(const QPointF &p1, const QPointF &p4, qre
|
||||||
p4p3.setAngle(angle2);
|
p4p3.setAngle(angle2);
|
||||||
QPointF p2 = p1p2.p2();
|
QPointF p2 = p1p2.p2();
|
||||||
QPointF p3 = p4p3.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;
|
return *this;
|
||||||
}
|
}
|
||||||
VAbstractCurve::operator=(spline);
|
VAbstractCubicBezier::operator=(spline);
|
||||||
d = spline.d;
|
d = spline.d;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
#ifndef VSPLINE_H
|
#ifndef VSPLINE_H
|
||||||
#define VSPLINE_H
|
#define VSPLINE_H
|
||||||
|
|
||||||
#include "vabstractcurve.h"
|
#include "vabstractcubicbezier.h"
|
||||||
#include "vpointf.h"
|
#include "vpointf.h"
|
||||||
#include <QLineF>
|
#include <QLineF>
|
||||||
#include <QPointF>
|
#include <QPointF>
|
||||||
|
@ -37,12 +37,10 @@
|
||||||
class QPainterPath;
|
class QPainterPath;
|
||||||
class VSplineData;
|
class VSplineData;
|
||||||
|
|
||||||
#define M_2PI 6.28318530717958647692528676655900576
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief VSpline class that implements the spline.
|
* @brief VSpline class that implements the spline.
|
||||||
*/
|
*/
|
||||||
class VSpline :public VAbstractCurve
|
class VSpline :public VAbstractCubicBezier
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
VSpline();
|
VSpline();
|
||||||
|
@ -54,16 +52,16 @@ public:
|
||||||
VSpline (VPointF p1, VPointF p4, qreal angle1, const QString &angle1Formula, qreal angle2,
|
VSpline (VPointF p1, VPointF p4, qreal angle1, const QString &angle1Formula, qreal angle2,
|
||||||
const QString &angle2Formula, qreal c1Length, const QString &c1LengthFormula, qreal c2Length,
|
const QString &angle2Formula, qreal c1Length, const QString &c1LengthFormula, qreal c2Length,
|
||||||
const QString &c2LengthFormula, quint32 idObject = 0, Draw mode = Draw::Calculation);
|
const QString &c2LengthFormula, quint32 idObject = 0, Draw mode = Draw::Calculation);
|
||||||
virtual ~VSpline() Q_DECL_OVERRIDE;
|
virtual ~VSpline();
|
||||||
VSpline &operator=(const VSpline &spl);
|
VSpline &operator=(const VSpline &spl);
|
||||||
|
|
||||||
VPointF GetP1 () const;
|
virtual VPointF GetP1 () const Q_DECL_OVERRIDE;
|
||||||
void SetP1 (const VPointF &p);
|
void SetP1 (const VPointF &p);
|
||||||
|
|
||||||
QPointF GetP2 () const;
|
QPointF GetP2 () const;
|
||||||
QPointF GetP3 () const;
|
QPointF GetP3 () const;
|
||||||
|
|
||||||
VPointF GetP4 () const;
|
virtual VPointF GetP4 () const Q_DECL_OVERRIDE;
|
||||||
void SetP4 (const VPointF &p);
|
void SetP4 (const VPointF &p);
|
||||||
|
|
||||||
virtual qreal GetStartAngle () const Q_DECL_OVERRIDE;
|
virtual qreal GetStartAngle () const Q_DECL_OVERRIDE;
|
||||||
|
@ -84,27 +82,20 @@ public:
|
||||||
void SetC1Length(qreal length, const QString &formula);
|
void SetC1Length(qreal length, const QString &formula);
|
||||||
void SetC2Length(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 GetKasm1() const;
|
||||||
qreal GetKasm2() const;
|
qreal GetKasm2() const;
|
||||||
qreal GetKcurve() const;
|
qreal GetKcurve() const;
|
||||||
qreal LengthT(qreal t) const;
|
qreal LengthT(qreal t) const;
|
||||||
QPointF CutSpline ( qreal length, QPointF &spl1p2, QPointF &spl1p3, QPointF &spl2p2, QPointF &spl2p3) const;
|
QPointF CutSpline ( qreal length, QPointF &spl1p2, QPointF &spl1p3, QPointF &spl2p2, QPointF &spl2p3) const;
|
||||||
QPointF CutSpline ( qreal length, VSpline &spl1, VSpline &spl2) 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
|
// cppcheck-suppress unusedFunction
|
||||||
static QVector<QPointF> SplinePoints(const QPointF &p1, const QPointF &p4, qreal angle1, qreal angle2, qreal kAsm1,
|
static QVector<QPointF> SplinePoints(const QPointF &p1, const QPointF &p4, qreal angle1, qreal angle2, qreal kAsm1,
|
||||||
qreal kAsm2, qreal kCurve);
|
qreal kAsm2, qreal kCurve);
|
||||||
qreal ParamT(const QPointF &pBt) const;
|
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:
|
private:
|
||||||
QSharedDataPointer<VSplineData> d;
|
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,
|
QVector<qreal> CalcT(qreal curveCoord1, qreal curveCoord2, qreal curveCoord3, qreal curveCoord4,
|
||||||
qreal pointCoord) const;
|
qreal pointCoord) const;
|
||||||
static qint32 Cubic(QVector<qreal> &x, qreal a, qreal b, qreal c);
|
static qint32 Cubic(QVector<qreal> &x, qreal a, qreal b, qreal c);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user