26const QgsAnchorWithHandles QgsBezierData::sInvalidAnchor;
28void QgsBezierData::addAnchor(
const QgsPoint &point )
30 mData.append( QgsAnchorWithHandles( point ) );
33void QgsBezierData::moveAnchor(
int index,
const QgsPoint &point )
35 if ( index < 0 || index >= mData.count() )
38 QgsAnchorWithHandles &data = mData[index];
41 const double dx = point.
x() - data.anchor.x();
42 const double dy = point.
y() - data.anchor.y();
43 const double dz = point.
is3D() ? ( point.
z() - data.anchor.z() ) : 0.0;
49 data.leftHandle.
setX( data.leftHandle.x() + dx );
50 data.leftHandle.setY( data.leftHandle.y() + dy );
52 data.leftHandle.setZ( data.leftHandle.z() + dz );
54 data.rightHandle.setX( data.rightHandle.x() + dx );
55 data.rightHandle.setY( data.rightHandle.y() + dy );
57 data.rightHandle.setZ( data.rightHandle.z() + dz );
60void QgsBezierData::moveHandle(
int index,
const QgsPoint &point )
62 const int anchorIndex = index / 2;
63 if ( anchorIndex < 0 || anchorIndex >= mData.count() )
67 mData[anchorIndex].leftHandle = point;
69 mData[anchorIndex].rightHandle = point;
72void QgsBezierData::insertAnchor(
int segmentIndex,
const QgsPoint &point )
74 if ( segmentIndex < 0 || segmentIndex > mData.count() )
77 mData.insert( segmentIndex, QgsAnchorWithHandles( point ) );
80void QgsBezierData::deleteAnchor(
int index )
82 if ( index < 0 || index >= mData.count() )
85 mData.removeAt( index );
88void QgsBezierData::retractHandle(
int index )
90 const int anchorIndex = index / 2;
91 if ( anchorIndex < 0 || anchorIndex >= mData.count() )
95 mData[anchorIndex].leftHandle = mData[anchorIndex].anchor;
97 mData[anchorIndex].rightHandle = mData[anchorIndex].anchor;
100void QgsBezierData::extendHandle(
int index,
const QgsPoint &point )
102 moveHandle( index, point );
105QgsPoint QgsBezierData::anchor(
int index )
const
107 if ( index < 0 || index >= mData.count() )
109 return mData[index].anchor;
112QgsPoint QgsBezierData::handle(
int index )
const
114 const int anchorIndex = index / 2;
115 if ( anchorIndex < 0 || anchorIndex >= mData.count() )
118 if ( index % 2 == 0 )
119 return mData[anchorIndex].leftHandle;
121 return mData[anchorIndex].rightHandle;
124QVector<QgsPoint> QgsBezierData::anchors()
const
126 QVector<QgsPoint> result;
127 result.reserve( mData.count() );
128 for (
const QgsAnchorWithHandles &awh : mData )
129 result.append( awh.anchor );
133QVector<QgsPoint> QgsBezierData::handles()
const
135 QVector<QgsPoint> result;
136 result.reserve( mData.count() * 2 );
137 for (
const QgsAnchorWithHandles &awh : mData )
139 result.append( awh.leftHandle );
140 result.append( awh.rightHandle );
145const QgsAnchorWithHandles &QgsBezierData::anchorWithHandles(
int index )
const
147 if ( index < 0 || index >= mData.count() )
148 return sInvalidAnchor;
156 if ( mData.count() < 2 )
159 for (
const QgsAnchorWithHandles &awh : mData )
160 result.append( awh.anchor );
165 result.append( mData.first().anchor );
168 for (
int i = 0; i < mData.count() - 1; ++i )
170 const QgsPoint &p0 = mData[i].anchor;
171 const QgsPoint &p1 = mData[i].rightHandle;
172 const QgsPoint &p2 = mData[i + 1].leftHandle;
173 const QgsPoint &p3 = mData[i + 1].anchor;
176 for (
int j = 1; j <= INTERPOLATION_POINTS; ++j )
178 const double t =
static_cast<double>( j ) / INTERPOLATION_POINTS;
186std::unique_ptr<QgsNurbsCurve> QgsBezierData::asNurbsCurve()
const
188 const int anchorCount = mData.count();
189 if ( anchorCount < 2 )
195 QVector<QgsPoint> ctrlPts;
196 ctrlPts.append( mData[0].anchor );
198 for (
int i = 0; i < anchorCount - 1; ++i )
200 ctrlPts.append( mData[i].rightHandle );
201 ctrlPts.append( mData[i + 1].leftHandle );
202 ctrlPts.append( mData[i + 1].anchor );
208 QVector<double> weights( ctrlPts.count(), 1.0 );
210 return std::make_unique<QgsNurbsCurve>( ctrlPts, 3, knots, weights );
213void QgsBezierData::clear()
218int QgsBezierData::findClosestAnchor(
const QgsPoint &point,
double tolerance )
const
220 int closestIndex = -1;
221 double minDistanceSquared = tolerance * tolerance;
223 for (
int i = 0; i < mData.count(); ++i )
225 const double dx = mData[i].anchor.x() - point.
x();
226 const double dy = mData[i].anchor.y() - point.
y();
227 const double distanceSquared = dx * dx + dy * dy;
228 if ( distanceSquared < minDistanceSquared )
230 minDistanceSquared = distanceSquared;
238int QgsBezierData::findClosestHandle(
const QgsPoint &point,
double tolerance )
const
240 int closestIndex = -1;
241 double minDistanceSquared = tolerance * tolerance;
243 for (
int i = 0; i < mData.count(); ++i )
245 const QgsAnchorWithHandles &awh = mData[i];
250 const double dx = awh.leftHandle.x() - point.
x();
251 const double dy = awh.leftHandle.y() - point.
y();
252 const double distanceSquared = dx * dx + dy * dy;
253 if ( distanceSquared < minDistanceSquared )
255 minDistanceSquared = distanceSquared;
256 closestIndex = i * 2;
263 const double dx = awh.rightHandle.x() - point.
x();
264 const double dy = awh.rightHandle.y() - point.
y();
265 const double distanceSquared = dx * dx + dy * dy;
266 if ( distanceSquared < minDistanceSquared )
268 minDistanceSquared = distanceSquared;
269 closestIndex = i * 2 + 1;
277int QgsBezierData::findClosestSegment(
const QgsPoint &point,
double tolerance )
const
279 if ( mData.count() < 2 )
283 double minDistanceSquared = tolerance * tolerance;
286 for (
int i = 0; i < mData.count() - 1; ++i )
288 const QgsPoint &p0 = mData[i].anchor;
289 const QgsPoint &p1 = mData[i].rightHandle;
290 const QgsPoint &p2 = mData[i + 1].leftHandle;
291 const QgsPoint &p3 = mData[i + 1].anchor;
294 for (
int j = 0; j <= INTERPOLATION_POINTS; ++j )
296 const double t =
static_cast<double>( j ) / INTERPOLATION_POINTS;
299 const double dx = curvePoint.
x() - point.
x();
300 const double dy = curvePoint.
y() - point.
y();
301 const double distanceSquared = dx * dx + dy * dy;
303 if ( distanceSquared < minDistanceSquared )
305 minDistanceSquared = distanceSquared;
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
static QgsPoint interpolatePointOnCubicBezier(const QgsPoint &p0, const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, double t)
Evaluates a point on a cubic Bézier curve defined by four control points.
static QVector< double > generateKnotsForBezierConversion(int nAnchors, int degree=3)
Generates a knot vector for converting piecewise Bézier curves to NURBS.
Point geometry type, with support for z-dimension and m-values.
void setX(double x)
Sets the point's x-coordinate.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
QVector< QgsPoint > QgsPointSequence
double closestSegment(const QgsPolylineXY &pl, const QgsPointXY &pt, int &vertexAfter, double epsilon)