30 #include <QJsonObject> 
   32 #include <QPainterPath> 
   34 #include <nlohmann/json.hpp> 
   44   bool hasZ = p1.
is3D();
 
   77   int pointCount = std::min( x.size(), y.size() );
 
   78   if ( x.size() == pointCount )
 
   84     mX = x.mid( 0, pointCount );
 
   86   if ( y.size() == pointCount )
 
   92     mY = y.mid( 0, pointCount );
 
   94   if ( !z.isEmpty() && z.count() >= pointCount )
 
   97     if ( z.size() == pointCount )
 
  103       mZ = z.mid( 0, pointCount );
 
  106   if ( !m.isEmpty() && m.count() >= pointCount )
 
  109     if ( m.size() == pointCount )
 
  115       mM = m.mid( 0, pointCount );
 
  135   if ( mX.count() != otherLine->mX.count() )
 
  138   for ( 
int i = 0; i < mX.count(); ++i )
 
  156   auto result = std::make_unique< QgsCircularString >();
 
  158   return result.release();
 
  163   const QgsCircularString *otherLine = qgsgeometry_cast<const QgsCircularString *>( other );
 
  167   const int size = mX.size();
 
  168   const int otherSize = otherLine->mX.size();
 
  169   if ( size > otherSize )
 
  173   else if ( size < otherSize )
 
  180   else if ( !
is3D() && otherLine->
is3D() )
 
  182   const bool considerZ = 
is3D();
 
  190   for ( 
int i = 0; i < size; i++ )
 
  192     const double x = mX[i];
 
  193     const double otherX = otherLine->mX[i];
 
  198     else if ( x > otherX )
 
  203     const double y = mY[i];
 
  204     const double otherY = otherLine->mY[i];
 
  209     else if ( y > otherY )
 
  216       const double z = mZ[i];
 
  217       const double otherZ = otherLine->mZ[i];
 
  223       else if ( z > otherZ )
 
  231       const double m = mM[i];
 
  232       const double otherM = otherLine->mM[i];
 
  238       else if ( m > otherM )
 
  249   return QStringLiteral( 
"CircularString" );
 
  276   for ( 
int i = 0; i < ( nPoints - 2 ) ; i += 2 )
 
  280       bbox = segmentBoundingBox( 
QgsPoint( mX[i], mY[i] ), 
QgsPoint( mX[i + 1], mY[i + 1] ), 
QgsPoint( mX[i + 2], mY[i + 2] ) );
 
  289   if ( nPoints > 0 && nPoints % 2 == 0 )
 
  302   const int size = mX.size();
 
  303   if ( index < 1 || index >= size - 1 )
 
  306   const bool useZ = 
is3D();
 
  309   QVector<double> newX( size );
 
  310   QVector<double> newY( size );
 
  311   QVector<double> newZ( useZ ? size : 0 );
 
  312   QVector<double> newM( useM ? size : 0 );
 
  313   auto it = std::copy( mX.constBegin() + index, mX.constEnd() - 1, newX.begin() );
 
  314   it = std::copy( mX.constBegin(), mX.constBegin() + index, it );
 
  315   *it = *newX.constBegin();
 
  316   mX = std::move( newX );
 
  318   it = std::copy( mY.constBegin() + index, mY.constEnd() - 1, newY.begin() );
 
  319   it = std::copy( mY.constBegin(), mY.constBegin() + index, it );
 
  320   *it = *newY.constBegin();
 
  321   mY = std::move( newY );
 
  324     it = std::copy( mZ.constBegin() + index, mZ.constEnd() - 1, newZ.begin() );
 
  325     it = std::copy( mZ.constBegin(), mZ.constBegin() + index, it );
 
  326     *it = *newZ.constBegin();
 
  327     mZ = std::move( newZ );
 
  331     it = std::copy( mM.constBegin() + index, mM.constEnd() - 1, newM.begin() );
 
  332     it = std::copy( mM.constBegin(), mM.constBegin() + index, it );
 
  333     *it = *newM.constBegin();
 
  334     mM = std::move( newM );
 
  340   double centerX, centerY, radius;
 
  349   bbox.combineExtentWith( pt3.
x(), pt3.
y() );
 
  351   QgsPointSequence compassPoints = compassPointsOnSegment( p1Angle, p2Angle, p3Angle, centerX, centerY, radius );
 
  352   QgsPointSequence::const_iterator cpIt = compassPoints.constBegin();
 
  353   for ( ; cpIt != compassPoints.constEnd(); ++cpIt )
 
  355     bbox.combineExtentWith( cpIt->x(), cpIt->y() );
 
  360 QgsPointSequence QgsCircularString::compassPointsOnSegment( 
double p1Angle, 
double p2Angle, 
double p3Angle, 
double centerX, 
double centerY, 
double radius )
 
  364   QgsPoint nPoint( centerX, centerY + radius );
 
  365   QgsPoint ePoint( centerX + radius, centerY );
 
  366   QgsPoint sPoint( centerX, centerY - radius );
 
  367   QgsPoint wPoint( centerX - radius, centerY );
 
  369   if ( p3Angle >= p1Angle )
 
  371     if ( p2Angle > p1Angle && p2Angle < p3Angle )
 
  373       if ( p1Angle <= 90 && p3Angle >= 90 )
 
  375         pointList.append( nPoint );
 
  377       if ( p1Angle <= 180 && p3Angle >= 180 )
 
  379         pointList.append( wPoint );
 
  381       if ( p1Angle <= 270 && p3Angle >= 270 )
 
  383         pointList.append( sPoint );
 
  388       pointList.append( ePoint );
 
  389       if ( p1Angle >= 90 || p3Angle <= 90 )
 
  391         pointList.append( nPoint );
 
  393       if ( p1Angle >= 180 || p3Angle <= 180 )
 
  395         pointList.append( wPoint );
 
  397       if ( p1Angle >= 270 || p3Angle <= 270 )
 
  399         pointList.append( sPoint );
 
  405     if ( p2Angle < p1Angle && p2Angle > p3Angle )
 
  407       if ( p1Angle >= 270 && p3Angle <= 270 )
 
  409         pointList.append( sPoint );
 
  411       if ( p1Angle >= 180 && p3Angle <= 180 )
 
  413         pointList.append( wPoint );
 
  415       if ( p1Angle >= 90 && p3Angle <= 90 )
 
  417         pointList.append( nPoint );
 
  422       pointList.append( ePoint );
 
  423       if ( p1Angle <= 270 || p3Angle >= 270 )
 
  425         pointList.append( sPoint );
 
  427       if ( p1Angle <= 180 || p3Angle >= 180 )
 
  429         pointList.append( wPoint );
 
  431       if ( p1Angle <= 90 || p3Angle >= 90 )
 
  433         pointList.append( nPoint );
 
  458   mX.resize( nVertices );
 
  459   mY.resize( nVertices );
 
  460   hasZ ? mZ.resize( nVertices ) : mZ.clear();
 
  461   hasM ? mM.resize( nVertices ) : mM.clear();
 
  462   for ( 
int i = 0; i < nVertices; ++i )
 
  489   parts.second = 
parts.second.remove( 
'(' ).remove( 
')' );
 
  490   QString secondWithoutParentheses = 
parts.second;
 
  491   secondWithoutParentheses = secondWithoutParentheses.simplified().remove( 
' ' );
 
  492   if ( ( 
parts.second.compare( QLatin1String( 
"EMPTY" ), Qt::CaseInsensitive ) == 0 ) ||
 
  493        secondWithoutParentheses.isEmpty() )
 
  506   int binarySize = 
sizeof( char ) + 
sizeof( quint32 ) + 
sizeof( quint32 );
 
  517   wkb << static_cast<quint32>( 
wkbType() );
 
  529     wkt += QLatin1String( 
"EMPTY" );
 
  542   std::unique_ptr< QgsLineString > line( 
curveToLine() );
 
  543   QDomElement gml = line->asGml2( doc, 
precision, ns, axisOrder );
 
  552   QDomElement elemCurve = doc.createElementNS( ns, QStringLiteral( 
"Curve" ) );
 
  557   QDomElement elemSegments = doc.createElementNS( ns, QStringLiteral( 
"segments" ) );
 
  558   QDomElement elemArcString = doc.createElementNS( ns, QStringLiteral( 
"ArcString" ) );
 
  560   elemSegments.appendChild( elemArcString );
 
  561   elemCurve.appendChild( elemSegments );
 
  569   std::unique_ptr< QgsLineString > line( 
curveToLine() );
 
  582     error = QObject::tr( 
"CircularString has less than 3 points and is not empty." );
 
  593   for ( 
int i = 0; i < ( nPoints - 2 ) ; i += 2 )
 
  624   for ( 
int i = 0; i < ( nPoints - 2 ) ; i += 2 )
 
  638   bool res = 
snapToGridPrivate( hSpacing, vSpacing, dSpacing, mSpacing, mX, mY, mZ, mM,
 
  639                                 result->mX, result->mY, result->mZ, result->mM );
 
  641     return result.release();
 
  648   if ( mX.count() <= 3 )
 
  651   double prevX = mX.at( 0 );
 
  652   double prevY = mY.at( 0 );
 
  654   bool useZ = hasZ && useZValues;
 
  655   double prevZ = useZ ? mZ.at( 0 ) : 0;
 
  657   int remaining = mX.count();
 
  660   while ( i + 1 < remaining )
 
  662     double currentCurveX = mX.at( i );
 
  663     double currentCurveY = mY.at( i );
 
  664     double currentX = mX.at( i + 1 );
 
  665     double currentY = mY.at( i + 1 );
 
  666     double currentZ = useZ ? mZ.at( i + 1 ) : 0;
 
  699   return std::min( mX.size(), mY.size() );
 
  704   const int size = mX.size();
 
  708   const double *x = mX.constData();
 
  709   const double *y = mY.constData();
 
  710   const bool useZ = 
is3D();
 
  712   const double *z = useZ ? mZ.constData() : 
nullptr;
 
  713   const double *m = useM ? mM.constData() : 
nullptr;
 
  715   for ( 
int i = 0; i < size; i += 2 )
 
  744   if ( i < 0 || std::min( mX.size(), mY.size() ) <= i )
 
  749   double x = mX.at( i );
 
  750   double y = mY.at( i );
 
  781   if ( index >= 0 && index < mX.size() )
 
  782     return mX.at( index );
 
  789   if ( index >= 0 && index < mY.size() )
 
  790     return mY.at( index );
 
  802   int size = mX.size();
 
  804   double *srcX = mX.data();
 
  805   double *srcY = mY.data();
 
  806   double *srcM = hasM ? mM.data() : 
nullptr;
 
  807   double *srcZ = hasZ ? mZ.data() : 
nullptr;
 
  810   for ( 
int i = 0; i < size; ++i )
 
  814     double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
 
  815     double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
 
  843   int size = mX.size();
 
  845   double *srcX = mX.data(); 
 
  846   double *srcY = mY.data(); 
 
  847   double *srcM = hasM ? mM.data() : 
nullptr; 
 
  848   double *srcZ = hasZ ? mZ.data() : 
nullptr; 
 
  850   double *destX = srcX;
 
  851   double *destY = srcY;
 
  852   double *destM = srcM;
 
  853   double *destZ = srcZ;
 
  855   int filteredPoints = 0;
 
  856   for ( 
int i = 0; i < size; ++i )
 
  860     double z = hasZ ? *srcZ++ : std::numeric_limits<double>::quiet_NaN();
 
  861     double m = hasM ? *srcM++ : std::numeric_limits<double>::quiet_NaN();
 
  863     if ( filter( 
QgsPoint( x, y, z, m ) ) )
 
  875   mX.resize( filteredPoints );
 
  876   mY.resize( filteredPoints );
 
  878     mZ.resize( filteredPoints );
 
  880     mM.resize( filteredPoints );
 
  889   int size = mX.size();
 
  891   double *srcX = mX.data();
 
  892   double *srcY = mY.data();
 
  893   double *srcM = hasM ? mM.data() : 
nullptr;
 
  894   double *srcZ = hasZ ? mZ.data() : 
nullptr;
 
  896   for ( 
int i = 0; i < size; ++i )
 
  900     double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
 
  901     double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
 
  915   const bool useZ = 
is3D();
 
  918   const int size = mX.size();
 
  920     return std::make_tuple( std::make_unique< QgsCircularString >(), std::make_unique< QgsCircularString >() );
 
  922   index = std::clamp( index, 0, size - 1 );
 
  924   const int part1Size = index + 1;
 
  925   QVector< double > x1( part1Size );
 
  926   QVector< double > y1( part1Size );
 
  927   QVector< double > z1( useZ ? part1Size : 0 );
 
  928   QVector< double > m1( useM ? part1Size : 0 );
 
  930   const double *sourceX = mX.constData();
 
  931   const double *sourceY = mY.constData();
 
  932   const double *sourceZ = useZ ? mZ.constData() : 
nullptr;
 
  933   const double *sourceM = useM ? mM.constData() : 
nullptr;
 
  935   double *destX = x1.data();
 
  936   double *destY = y1.data();
 
  937   double *destZ = useZ ? z1.data() : 
nullptr;
 
  938   double *destM = useM ? m1.data() : 
nullptr;
 
  940   std::copy( sourceX, sourceX + part1Size, destX );
 
  941   std::copy( sourceY, sourceY + part1Size, destY );
 
  943     std::copy( sourceZ, sourceZ + part1Size, destZ );
 
  945     std::copy( sourceM, sourceM + part1Size, destM );
 
  947   const int part2Size = size - index;
 
  949     return std::make_tuple( std::make_unique< QgsCircularString >( x1, y1, z1, m1 ), std::make_unique< QgsCircularString >() );
 
  951   QVector< double > x2( part2Size );
 
  952   QVector< double > y2( part2Size );
 
  953   QVector< double > z2( useZ ? part2Size : 0 );
 
  954   QVector< double > m2( useM ? part2Size : 0 );
 
  957   destZ = useZ ? z2.data() : 
nullptr;
 
  958   destM = useM ? m2.data() : 
nullptr;
 
  959   std::copy( sourceX + index, sourceX + size, destX );
 
  960   std::copy( sourceY + index, sourceY + size, destY );
 
  962     std::copy( sourceZ + index, sourceZ + size, destZ );
 
  964     std::copy( sourceM + index, sourceM + size, destM );
 
  967     return std::make_tuple( std::make_unique< QgsCircularString >(), std::make_unique< QgsCircularString >( x2, y2, z2, m2 ) );
 
  969     return std::make_tuple( std::make_unique< QgsCircularString >( x1, y1, z1, m1 ), std::make_unique< QgsCircularString >( x2, y2, z2, m2 ) );
 
  976   for ( 
int i = 0; i < nPts; ++i )
 
  978     pts.push_back( 
pointN( i ) );
 
  998   bool hasZ = firstPt.
is3D();
 
 1003   mX.resize( 
points.size() );
 
 1004   mY.resize( 
points.size() );
 
 1007     mZ.resize( 
points.size() );
 
 1015     mM.resize( 
points.size() );
 
 1022   for ( 
int i = 0; i < 
points.size(); ++i )
 
 1028       double z = 
points.at( i ).z();
 
 1029       mZ[i] = std::isnan( z ) ? 0 : z;
 
 1033       double m = 
points.at( i ).m();
 
 1034       mM[i] = std::isnan( m ) ? 0 : m;
 
 1041   if ( !line || line->
isEmpty() )
 
 1084       mZ.insert( mZ.count(), mX.size() - mZ.size(), std::numeric_limits<double>::quiet_NaN() );
 
 1097       mM.insert( mM.count(), mX.size() - mM.size(), std::numeric_limits<double>::quiet_NaN() );
 
 1115   double *zArray = mZ.data();
 
 1119   bool useDummyZ = !hasZ || !transformZ;
 
 1122     zArray = 
new double[nPoints];
 
 1123     for ( 
int i = 0; i < nPoints; ++i )
 
 1142   for ( 
int i = 0; i < nPoints; ++i )
 
 1145     t.map( mX.at( i ), mY.at( i ), &x, &y );
 
 1150       mZ[i] = mZ.at( i ) * zScale + zTranslate;
 
 1154       mM[i] = mM.at( i ) * mScale + mTranslate;
 
 1159 void arcTo( QPainterPath &path, QPointF pt1, QPointF pt2, QPointF pt3 )
 
 1161   double centerX, centerY, radius;
 
 1163                                         radius, centerX, centerY );
 
 1168   double diameter = 2 * radius;
 
 1169   path.arcTo( centerX - radius, centerY - radius, diameter, diameter, -p1Angle, -sweepAngle );
 
 1180   if ( path.isEmpty() || path.currentPosition() != QPointF( mX[0], mY[0] ) )
 
 1182     path.moveTo( QPointF( mX[0], mY[0] ) );
 
 1185   for ( 
int i = 0; i < ( nPoints - 2 ) ; i += 2 )
 
 1187     arcTo( path, QPointF( mX[i], mY[i] ), QPointF( mX[i + 1], mY[i + 1] ), QPointF( mX[i + 2], mY[i + 2] ) );
 
 1191   if ( nPoints % 2 == 0 )
 
 1193     path.lineTo( mX[ nPoints - 1 ], mY[ nPoints - 1 ] );
 
 1204   if ( position.
vertex >= mX.size() || position.
vertex < 1 )
 
 1209   mX.insert( position.
vertex, vertex.
x() );
 
 1210   mY.insert( position.
vertex, vertex.
y() );
 
 1213     mZ.insert( position.
vertex, vertex.
z() );
 
 1217     mM.insert( position.
vertex, vertex.
m() );
 
 1220   bool vertexNrEven = ( position.
vertex % 2 == 0 );
 
 1235   if ( position.
vertex < 0 || position.
vertex >= mX.size() )
 
 1240   mX[position.
vertex] = newPos.
x();
 
 1241   mY[position.
vertex] = newPos.
y();
 
 1244     mZ[position.
vertex] = newPos.
z();
 
 1248     mM[position.
vertex] = newPos.
m();
 
 1257   if ( nVertices < 4 ) 
 
 1262   if ( position.
vertex < 0 || position.
vertex > ( nVertices - 1 ) )
 
 1267   if ( position.
vertex < ( nVertices - 2 ) )
 
 1300   double minDist = std::numeric_limits<double>::max();
 
 1303   int minDistLeftOf = 0;
 
 1305   double currentDist = 0.0;
 
 1308   for ( 
int i = 0; i < ( nPoints - 2 ) ; i += 2 )
 
 1310     currentDist = closestPointOnArc( mX[i], mY[i], mX[i + 1], mY[i + 1], mX[i + 2], mY[i + 2], pt, segmentPt, vertexAfter, 
leftOf, epsilon );
 
 1311     if ( currentDist < minDist )
 
 1313       minDist = currentDist;
 
 1314       minDistSegmentPoint = segmentPt;
 
 1323   if ( minDist == std::numeric_limits<double>::max() )
 
 1326   segmentPt = minDistSegmentPoint;
 
 1327   vertexAfter = minDistVertexAfter;
 
 1328   vertexAfter.
part = 0;
 
 1329   vertexAfter.
ring = 0;
 
 1344   type = ( node % 2 == 0 ) ? Qgis::VertexType::Segment : Qgis::VertexType::Curve;
 
 1352   for ( 
int i = 0; i < maxIndex; i += 2 )
 
 1355     QgsPoint p2( mX[i + 1], mY[i + 1] );
 
 1356     QgsPoint p3( mX[i + 2], mY[i + 2] );
 
 1366     sum += 0.5 * ( mX[i] * mY[i + 2] - mY[i] * mX[i + 2] );
 
 1369     double midPointX = ( p1.
x() + p3.
x() ) / 2.0;
 
 1370     double midPointY = ( p1.
y() + p3.
y() ) / 2.0;
 
 1372     double radius, centerX, centerY;
 
 1376     double r2 = radius * radius;
 
 1387     double cov = 0.5 - d * std::sqrt( r2 - d * d ) / ( M_PI * r2 ) - M_1_PI * std::asin( d / radius );
 
 1388     double circleChordArea = 0;
 
 1389     if ( circlePointLeftOfLine == centerPointLeftOfLine )
 
 1391       circleChordArea = M_PI * r2 * ( 1 - cov );
 
 1395       circleChordArea = M_PI * r2 * cov;
 
 1398     if ( !circlePointLeftOfLine )
 
 1400       sum += circleChordArea;
 
 1404       sum -= circleChordArea;
 
 1414 double QgsCircularString::closestPointOnArc( 
double x1, 
double y1, 
double x2, 
double y2, 
double x3, 
double y3,
 
 1417   double radius, centerX, centerY;
 
 1442     segmentPt = ( distPtPt1 <= distPtPt3 ) ? pt1 : pt3;
 
 1443     vertexAfter.
vertex = ( distPtPt1 <= distPtPt3 ) ? 1 : 2;
 
 1450     segmentPt.
setX( pt.
x() );
 
 1451     segmentPt.
setY( pt.
y() );
 
 1457     double sqrDistancePointToCenter = ( pt.
x() - centerX ) * ( pt.
x() - centerX ) + ( pt.
y() - centerY ) * ( pt.
y() - centerY );
 
 1458     *
leftOf = clockwise ? ( sqrDistancePointToCenter > radius * radius ? -1 : 1 )
 
 1459               : ( sqrDistancePointToCenter < radius * radius ? -1 : 1 );
 
 1465 void QgsCircularString::insertVertexBetween( 
int after, 
int before, 
int pointOnCircle )
 
 1467   double xAfter = mX.at( after );
 
 1468   double yAfter = mY.at( after );
 
 1469   double xBefore = mX.at( before );
 
 1470   double yBefore = mY.at( before );
 
 1471   double xOnCircle = mX.at( pointOnCircle );
 
 1472   double yOnCircle = mY.at( pointOnCircle );
 
 1474   double radius, centerX, centerY;
 
 1477   double x = ( xAfter + xBefore ) / 2.0;
 
 1478   double y = ( yAfter + yBefore ) / 2.0;
 
 1481   mX.insert( before, newVertex.
x() );
 
 1482   mY.insert( before, newVertex.
y() );
 
 1486     mZ.insert( before, ( mZ[after] + mZ[before] ) / 2.0 );
 
 1490     mM.insert( before, ( mM[after] + mM[before] ) / 2.0 );
 
 1503   int before = vId.
vertex - 1;
 
 1505   int after = vId.
vertex + 1;
 
 1507   if ( vId.
vertex % 2 != 0 ) 
 
 1537       int vertex1 = vId.
vertex - 2;
 
 1538       int vertex2 = vId.
vertex - 1;
 
 1539       int vertex3 = vId.
vertex;
 
 1541                       QgsPoint( mX[vertex1], mY[vertex1] ), 
QgsPoint( mX[vertex2], mY[vertex2] ), 
QgsPoint( mX[vertex3], mY[vertex3] ) );
 
 1542       int vertex4 = vId.
vertex + 1;
 
 1543       int vertex5 = vId.
vertex + 2;
 
 1545                       QgsPoint( mX[vertex3], mY[vertex3] ), 
QgsPoint( mX[vertex4], mY[vertex4] ), 
QgsPoint( mX[vertex5], mY[vertex5] ) );
 
 1554   if ( startVertex.
vertex < 0 || startVertex.
vertex >= mX.count() - 2 )
 
 1557   if ( startVertex.
vertex % 2 == 1 )
 
 1560   double x1 = mX.at( startVertex.
vertex );
 
 1561   double y1 = mY.at( startVertex.
vertex );
 
 1562   double x2 = mX.at( startVertex.
vertex + 1 );
 
 1563   double y2 = mY.at( startVertex.
vertex + 1 );
 
 1564   double x3 = mX.at( startVertex.
vertex + 2 );
 
 1565   double y3 = mY.at( startVertex.
vertex + 2 );
 
 1572   std::reverse( copy->mX.begin(), copy->mX.end() );
 
 1573   std::reverse( copy->mY.begin(), copy->mY.end() );
 
 1576     std::reverse( copy->mZ.begin(), copy->mZ.end() );
 
 1580     std::reverse( copy->mM.begin(), copy->mM.end() );
 
 1590   double distanceTraversed = 0;
 
 1592   if ( totalPoints == 0 )
 
 1601   const double *x = mX.constData();
 
 1602   const double *y = mY.constData();
 
 1603   const double *z = 
is3D() ? mZ.constData() : 
nullptr;
 
 1604   const double *m = 
isMeasure() ? mM.constData() : 
nullptr;
 
 1606   double prevX = *x++;
 
 1607   double prevY = *y++;
 
 1608   double prevZ = z ? *z++ : 0.0;
 
 1609   double prevM = m ? *m++ : 0.0;
 
 1613     return new QgsPoint( pointType, prevX, prevY, prevZ, prevM );
 
 1616   for ( 
int i = 0; i < ( totalPoints - 2 ) ; i += 2 )
 
 1625     double z2 = z ? *z++ : 0.0;
 
 1626     double m2 = m ? *m++ : 0.0;
 
 1630     double z3 = z ? *z++ : 0.0;
 
 1631     double m3 = m ? *m++ : 0.0;
 
 1637       const double distanceToPoint = std::min( distance - distanceTraversed, 
segmentLength );
 
 1639                            QgsPoint( pointType, x2, y2, z2, m2 ),
 
 1640                            QgsPoint( pointType, x3, y3, z3, m3 ), distanceToPoint ) );
 
 1656   if ( startDistance < 0 && endDistance < 0 )
 
 1659   endDistance = std::max( startDistance, endDistance );
 
 1662   if ( totalPoints == 0 )
 
 1665   QVector< QgsPoint > substringPoints;
 
 1666   substringPoints.reserve( totalPoints );
 
 1674   const double *x = mX.constData();
 
 1675   const double *y = mY.constData();
 
 1676   const double *z = 
is3D() ? mZ.constData() : 
nullptr;
 
 1677   const double *m = 
isMeasure() ? mM.constData() : 
nullptr;
 
 1679   double distanceTraversed = 0;
 
 1680   double prevX = *x++;
 
 1681   double prevY = *y++;
 
 1682   double prevZ = z ? *z++ : 0.0;
 
 1683   double prevM = m ? *m++ : 0.0;
 
 1684   bool foundStart = 
false;
 
 1686   if ( startDistance < 0 )
 
 1689   for ( 
int i = 0; i < ( totalPoints - 2 ) ; i += 2 )
 
 1698     double z2 = z ? *z++ : 0.0;
 
 1699     double m2 = m ? *m++ : 0.0;
 
 1703     double z3 = z ? *z++ : 0.0;
 
 1704     double m3 = m ? *m++ : 0.0;
 
 1706     bool addedSegmentEnd = 
false;
 
 1708     if ( distanceTraversed <= startDistance && startDistance < distanceTraversed + 
segmentLength )
 
 1711       const double distanceToStart = startDistance - distanceTraversed;
 
 1713                                   QgsPoint( pointType, x2, y2, z2, m2 ),
 
 1714                                   QgsPoint( pointType, x3, y3, z3, m3 ), distanceToStart );
 
 1717       const bool endPointOnSegment = distanceTraversed + 
segmentLength > endDistance;
 
 1718       if ( endPointOnSegment )
 
 1720         const double distanceToEnd = endDistance - distanceTraversed;
 
 1721         const double midPointDistance = ( distanceToEnd - distanceToStart ) * 0.5 + distanceToStart;
 
 1724                             QgsPoint( pointType, x2, y2, z2, m2 ),
 
 1725                             QgsPoint( pointType, x3, y3, z3, m3 ), midPointDistance )
 
 1727                             QgsPoint( pointType, x2, y2, z2, m2 ),
 
 1728                             QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd );
 
 1729         addedSegmentEnd = 
true;
 
 1733         const double midPointDistance = ( 
segmentLength - distanceToStart ) * 0.5 + distanceToStart;
 
 1736                             QgsPoint( pointType, x2, y2, z2, m2 ),
 
 1737                             QgsPoint( pointType, x3, y3, z3, m3 ), midPointDistance )
 
 1738                         << 
QgsPoint( pointType, x3, y3, z3, m3 );
 
 1739         addedSegmentEnd = 
true;
 
 1743     if ( !addedSegmentEnd && foundStart && ( distanceTraversed + 
segmentLength > endDistance ) )
 
 1746       const double distanceToEnd = endDistance - distanceTraversed;
 
 1749                       QgsPoint( pointType, x2, y2, z2, m2 ),
 
 1750                       QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd / 2.0 )
 
 1753                           QgsPoint( pointType, x2, y2, z2, m2 ),
 
 1754                           QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd );
 
 1756     else if ( !addedSegmentEnd && foundStart )
 
 1758       substringPoints << 
QgsPoint( pointType, x2, y2, z2, m2 )
 
 1759                       << 
QgsPoint( pointType, x3, y3, z3, m3 );
 
 1767     if ( distanceTraversed >= endDistance )
 
 1772   if ( !foundStart && 
qgsDoubleNear( distanceTraversed, startDistance ) )
 
 1774     substringPoints << 
QgsPoint( pointType, prevX, prevY, prevZ, prevM )
 
 1775                     << 
QgsPoint( pointType, prevX, prevY, prevZ, prevM )
 
 1776                     << 
QgsPoint( pointType, prevX, prevY, prevZ, prevM );
 
 1779   std::unique_ptr< QgsCircularString > result = std::make_unique< QgsCircularString >();
 
 1780   result->setPoints( substringPoints );
 
 1781   return result.release();
 
 1794   mZ.reserve( nPoints );
 
 1795   for ( 
int i = 0; i < nPoints; ++i )
 
 1812   mM.reserve( nPoints );
 
 1813   for ( 
int i = 0; i < nPoints; ++i )
 
 1846   std::swap( mX, mY );