QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
qgsmeshforcebypolylines.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmeshforcebypolylines.cpp - QgsMeshForceByPolylines
3 
4  ---------------------
5  begin : 5.9.2021
6  copyright : (C) 2021 by Vincent Cloarec
7  email : vcloarec at gmail dot com
8  ***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
17 
18 #include "qgsmesheditor.h"
19 #include "qgsgeometryutils.h"
20 #include "poly2tri.h"
21 #include "qgsmultisurface.h"
22 #include "qgsmulticurve.h"
23 #include "qgscurvepolygon.h"
24 #include "qgslinestring.h"
25 #include "qgsmeshlayerutils.h"
26 
27 
28 static int vertexPositionInFace( int vertexIndex, const QgsMeshFace &face )
29 {
30  return face.indexOf( vertexIndex );
31 }
32 
33 static int vertexPositionInFace( const QgsMesh &mesh, int vertexIndex, int faceIndex )
34 {
35  if ( faceIndex < 0 || faceIndex >= mesh.faceCount() )
36  return -1;
37 
38  return vertexPositionInFace( vertexIndex, mesh.face( faceIndex ) );
39 }
40 
41 bool QgsMeshEditForceByLine::edgeIntersection(
42  int vertex1,
43  int vertex2,
44  int &closestSnappedVertex,
45  QgsPoint &intersectionPoint,
46  bool outAllowed )
47 {
48  const QgsPointXY pt1( mCurrentPointPosition );
49  const QgsPointXY pt2( mPoint2 );
50 
51  closestSnappedVertex = -1;
52 
53  QgsTriangularMesh *triangularMesh = mEditor->triangularMesh();
54 
55  const QgsPointXY v1( triangularMesh->vertices().at( vertex1 ) );
56  const QgsPointXY v2( triangularMesh->vertices().at( vertex2 ) );
57 
58  double epsilon = std::numeric_limits<double>::epsilon() * mTolerance * mTolerance;
59 
60  QgsPointXY minDistPoint;
61  bool snapV1 = sqrt( v1.sqrDistToSegment( pt1.x(), pt1.y(), pt2.x(), pt2.y(), minDistPoint, epsilon ) ) < mTolerance;
62  bool snapV2 = sqrt( v2.sqrDistToSegment( pt1.x(), pt1.y(), pt2.x(), pt2.y(), minDistPoint, epsilon ) ) < mTolerance;
63 
64  bool intersectLine = false;
65  bool isIntersect = QgsGeometryUtils::segmentIntersection(
66  mCurrentPointPosition,
67  mPoint2,
68  triangularMesh->vertices().at( vertex1 ),
69  triangularMesh->vertices().at( vertex2 ),
70  intersectionPoint,
71  intersectLine,
72  outAllowed ? mTolerance : 0, true );
73 
74  if ( snapV1 == snapV2 ) //both or neither of them are snapped
75  {
76  double distance1FromIntersection = v1.distance( intersectionPoint );
77  double distance2FromIntersection = v2.distance( intersectionPoint );
78  if ( distance1FromIntersection <= distance2FromIntersection )
79  {
80  snapV1 &= true;
81  snapV2 = false;
82  }
83  else
84  {
85  snapV1 = false;
86  snapV2 &= true;
87  }
88  }
89 
90  if ( isIntersect && snapV1 )
91  {
92  closestSnappedVertex = vertex1;
93  intersectionPoint = triangularMesh->vertices().at( vertex1 );
94  return true;
95  }
96  else if ( isIntersect && snapV2 )
97  {
98  closestSnappedVertex = vertex2;
99  intersectionPoint = triangularMesh->vertices().at( vertex2 );
100  return true;
101  }
102 
103  return isIntersect;
104 }
105 
106 
107 static void buildHolesWithOneFace( QgsMeshEditor *meshEditor,
108  int faceIndex,
109  int firstVertex,
110  int secondVertex,
111  QList<int> &holeOnLeft,
112  QList<int> &neighborsOnLeft,
113  QList<int> &holeOnRight,
114  QList<int> &neighborsOnRight )
115 {
116  const QgsMeshFace &face = meshEditor->topologicalMesh().mesh()->face( faceIndex );
117  const QVector<int> &neighbors = meshEditor->topologicalMesh().neighborsOfFace( faceIndex );
118  const int faceSize = face.size();
119 
120  int beginPos = vertexPositionInFace( firstVertex, face );
121  int endPos = vertexPositionInFace( secondVertex, face );
122 
123  // build hole on the right
124  for ( int i = 0; i < faceSize; ++i )
125  {
126  int currentPos = ( beginPos + i ) % faceSize;
127  holeOnRight.append( face.at( currentPos ) );
128  if ( currentPos == endPos )
129  break;
130  neighborsOnRight.append( neighbors.at( currentPos ) );
131  }
132 
133  // build hole on the left
134  for ( int i = 0; i < faceSize; ++i )
135  {
136  int currentPos = ( beginPos + faceSize - i ) % faceSize;
137  holeOnLeft.append( face.at( currentPos ) );
138  if ( currentPos == endPos )
139  break;
140  int neighborPos = ( currentPos + faceSize - 1 ) % faceSize;
141  neighborsOnLeft.append( neighbors.at( neighborPos ) );
142  }
143 }
144 
145 
147 static int cutEdgeFromSnappedVertex( QgsMeshEditor *meshEditor,
148  int faceIndex,
149  int startingSnappedVertex,
150  int edgePosition,
151  QList<int> &newVerticesOnLeftHole,
152  QList<int> &newNeighborsOnLeftHole,
153  QList<int> &newVerticesOnRightHole,
154  QList<int> &newNeighborsOnRightHole )
155 {
156  const QgsMeshFace &face = meshEditor->topologicalMesh().mesh()->face( faceIndex );
157  const QVector<int> &neighbors = meshEditor->topologicalMesh().neighborsOfFace( faceIndex );
158  const int faceSize = face.size();
159 
160  const int beginPos = vertexPositionInFace( startingSnappedVertex, face );
161 
162  const int endPosOnLeft = ( edgePosition + 1 ) % faceSize;
163  const int endPosOnRight = edgePosition;
164 
165  // build hole on the left
166  for ( int i = 0; i < faceSize; ++i )
167  {
168  int currentPos = ( beginPos + faceSize - i ) % faceSize;
169  newVerticesOnLeftHole.append( face.at( currentPos ) );
170  if ( currentPos == endPosOnLeft )
171  break;
172  int neighborPos = ( currentPos + faceSize - 1 ) % faceSize;
173  newNeighborsOnLeftHole.append( neighbors.at( neighborPos ) );
174  }
175 
176  // build hole on the right
177  for ( int i = 0; i < faceSize; ++i )
178  {
179  int currentPos = ( beginPos + i ) % faceSize;
180  newVerticesOnRightHole.append( face.at( currentPos ) );
181  if ( currentPos == endPosOnRight )
182  break;
183  newNeighborsOnRightHole.append( neighbors.at( currentPos ) );
184  }
185 
186  return neighbors.at( edgePosition );
187 }
188 
189 
190 void QgsMeshEditForceByLine::setInputLine( const QgsPoint &pt1, const QgsPoint &pt2, double tolerance, bool newVertexOnIntersection )
191 {
192  clear();
193  mPoint1 = pt1;
194  mPoint2 = pt2;
195  mTolerance = tolerance;
196  mNewVertexOnIntersection = newVertexOnIntersection;
197  mFirstPointChecked = false;
198  mSecondPointChecked = false;
199  mCurrentSnappedVertex = -1;
200  mIsFinished = false;
201  mCurrentPointPosition = mPoint1;
202  mRemovedFaces.clear();
203  mHoleOnLeft.clear();
204  mNeighborOnLeft.clear();
205  mHoleOnRight.clear();
206  mNeighborOnRight.clear();
207  mNewVerticesIndexesOnLine.clear();
208  mEndOnPoint2 = false;
209  mPoint2VertexIndex = -1;
210 }
211 
212 QgsTopologicalMesh::Changes QgsMeshEditForceByLine::apply( QgsMeshEditor *meshEditor )
213 {
214  clear();
215  mEditor = meshEditor;
216 
217  if ( ! mFirstPointChecked )
218  {
219  mFirstPointChecked = true;
220 
221  if ( mInterpolateZValueOnMesh )
222  interpolateZValueOnMesh( mPoint1 );
223 
224  QgsMeshVertex v1 = meshEditor->triangularMesh()->triangularToNativeCoordinates( mPoint1 );
225  int closeEdge = -1;
226  int faceCloseEdge = -1;
227  if ( meshEditor->edgeIsClose( mPoint1, mTolerance, faceCloseEdge, closeEdge ) )
228  {
229  //if we are too close form a vertex, do nothing
230  const QgsMeshFace &face = meshEditor->topologicalMesh().mesh()->face( faceCloseEdge );
231  int vertexIndex1 = face.at( closeEdge );
232  int vertexIndex2 = face.at( ( closeEdge + 1 ) % face.size() );
233 
234  if ( ( meshEditor->triangularMesh()->vertices().at( vertexIndex1 ).distance( mPoint1 ) > mTolerance ) &&
235  ( meshEditor->triangularMesh()->vertices().at( vertexIndex2 ).distance( mPoint1 ) > mTolerance ) )
236  return meshEditor->topologicalMesh().insertVertexInFacesEdge( faceCloseEdge, closeEdge, v1 );
237  }
238  else
239  {
240  int includdingFace = meshEditor->triangularMesh()->nativeFaceIndexForPoint( mPoint1 );
241  if ( includdingFace != -1 )
242  {
243  return meshEditor->topologicalMesh().addVertexInFace( includdingFace, v1 );
244  }
245  }
246  }
247 
248  if ( ! mSecondPointChecked )
249  {
250  mSecondPointChecked = true;
251  if ( mInterpolateZValueOnMesh )
252  interpolateZValueOnMesh( mPoint2 );
253 
254  QgsMeshVertex v2 = meshEditor->triangularMesh()->triangularToNativeCoordinates( mPoint2 );
255  int closeEdge = -1;
256  int faceCloseEdge = -1;
257  if ( meshEditor->edgeIsClose( mPoint2, mTolerance, faceCloseEdge, closeEdge ) )
258  {
259  //if we are too close form a vertex, do nothing, just records the index of the vertex for point 2
260  const QgsMeshFace &face = meshEditor->topologicalMesh().mesh()->face( faceCloseEdge );
261  int vertexIndex1 = face.at( closeEdge );
262  int vertexIndex2 = face.at( ( closeEdge + 1 ) % face.size() );
263 
264  bool snap1 = meshEditor->triangularMesh()->vertices().at( vertexIndex1 ).distance( mPoint2 ) <= mTolerance ;
265  bool snap2 = meshEditor->triangularMesh()->vertices().at( vertexIndex2 ).distance( mPoint2 ) <= mTolerance ;
266 
267  if ( snap1 )
268  {
269  mEndOnPoint2 = true;
270  mPoint2VertexIndex = vertexIndex1;
271  mPoint2 = meshEditor->triangularMesh()->vertices().at( vertexIndex1 );
272  }
273  else if ( snap2 )
274  {
275  mEndOnPoint2 = true;
276  mPoint2VertexIndex = vertexIndex2;
277  mPoint2 = meshEditor->triangularMesh()->vertices().at( vertexIndex2 );
278  }
279  else
280  {
281  QgsTopologicalMesh::Changes changes = meshEditor->topologicalMesh().insertVertexInFacesEdge( faceCloseEdge, closeEdge, v2 );
282  if ( !changes.addedVertices().isEmpty() )
283  {
284  mEndOnPoint2 = true;
285  mPoint2VertexIndex = meshEditor->topologicalMesh().mesh()->vertexCount() - 1;
286  }
287 
288  return changes;
289  }
290  }
291  else
292  {
293  int includdingFace = meshEditor->triangularMesh()->nativeFaceIndexForPoint( mPoint2 );
294  if ( includdingFace != -1 )
295  {
296  QgsTopologicalMesh::Changes changes = meshEditor->topologicalMesh().addVertexInFace( includdingFace, v2 );
297  if ( !changes.addedVertices().isEmpty() )
298  {
299  mEndOnPoint2 = true;
300  mPoint2VertexIndex = meshEditor->topologicalMesh().mesh()->vertexCount() - 1;
301  }
302 
303  return changes;
304  }
305  }
306  }
307 
308  if ( buildForcedElements() )
309  {
310  meshEditor->topologicalMesh().applyChanges( *this );
311  return ( *this );
312  }
313 
315 }
316 
317 void QgsMeshEditForceByLine::finish()
318 {
319  mIsFinished = true;
320 }
321 
322 void QgsMeshEditForceByLine::interpolateZValueOnMesh( QgsPoint &point ) const
323 {
324  QgsTriangularMesh *triangularMesh = mEditor->triangularMesh();
325 
326  int includdingFace = triangularMesh->nativeFaceIndexForPoint( point );
327  if ( includdingFace != -1 )
328  {
329  int triangleFaceIndex = triangularMesh->faceIndexForPoint_v2( point );
330  if ( triangleFaceIndex != -1 )
331  {
332  QgsMeshFace triangleFace = triangularMesh->triangles().at( triangleFaceIndex );
333  QgsMeshVertex tv1 = triangularMesh->vertices().at( triangleFace.at( 0 ) );
334  QgsMeshVertex tv2 = triangularMesh->vertices().at( triangleFace.at( 1 ) );
335  QgsMeshVertex tv3 = triangularMesh->vertices().at( triangleFace.at( 2 ) );
336  double z = QgsMeshLayerUtils::interpolateFromVerticesData( tv1, tv2, tv3, tv1.z(), tv2.z(), tv3.z(), point );
337  point.setZ( z );
338  }
339  }
340 }
341 
342 void QgsMeshEditForceByLine::interpolateZValue( QgsMeshVertex &point, const QgsPoint &otherPoint1, const QgsPoint &otherPoint2 )
343 {
344  double distPoint = point.distance( otherPoint1 );
345  double totalDistance = otherPoint1.distance( otherPoint2 );
346 
347  point.setZ( otherPoint1.z() + ( otherPoint2.z() - otherPoint1.z() )*distPoint / totalDistance );
348 }
349 
350 bool QgsMeshEditForceByLine::buildForcedElements()
351 {
352  QgsTriangularMesh *triangularMesh = mEditor->triangularMesh();
353  QgsMesh *mesh = mEditor->topologicalMesh().mesh();
355  QSet<int> treatedFaces;
356 
357  int startingVertexIndex = mesh->vertices.count();
358 
359  int currentFaceIndex = -1;
360  bool searchOutside = false; //if we need to search outside last faces
361  QPair<int, int> currentEdge{-1, -1};
362 
363  int currentAddedVertex = -1; // Last added point
364  int nextCutFace = -1; //face that has to be cutted from an intersected edge (not snap on existing vertex)
365  int leftFace = -1; //the face that has been just cut in a edge
366 
367  while ( true )
368  {
369  if ( mCurrentSnappedVertex == -1 )
370  currentFaceIndex = triangularMesh->nativeFaceIndexForPoint( mCurrentPointPosition );
371 
372  if ( currentFaceIndex == leftFace )
373  currentFaceIndex = -1;
374 
375  if ( mCurrentSnappedVertex != -1 && !searchOutside )
376  {
377  //the current intersection is snapped on a existing vertex
378  currentFaceIndex = -1;
379  int previousSnappedVertex = -1;
380  int intersectionFaceIndex = -1;
381  QgsPoint intersectionPoint( 0, 0, 0 );
382  int edgePosition = -1;
383 
384  bool result = searchIntersectionEdgeFromSnappedVertex(
385  intersectionFaceIndex,
386  previousSnappedVertex,
387  mCurrentSnappedVertex,
388  intersectionPoint,
389  edgePosition,
390  treatedFaces );
391 
392  if ( !result )
393  {
394  //here maybe mPoint2 is snapped with the current snap vertex, check first this last check and stop if it true
395  if ( mCurrentSnappedVertex != -1 &&
396  mPoint2.distance( triangularMesh->vertices().at( mCurrentSnappedVertex ) ) < mTolerance )
397  break;
398 
399  //we have nothing in this part of the mesh, restart from the last interesting point to the point 2
400  searchOutside = true;
401  }
402  else
403  {
404  if ( mEndOnPoint2 && mCurrentSnappedVertex == mPoint2VertexIndex )
405  {
406  mIsFinished = true;
407  return false;
408  }
409 
410  if ( mCurrentSnappedVertex != -1 )
411  {
412  // we have snapped a vertex on the other side of a face, we can build holes
413 
414  buildHolesWithOneFace( mEditor,
415  intersectionFaceIndex,
416  previousSnappedVertex,
417  mCurrentSnappedVertex,
418  mHoleOnLeft,
419  mNeighborOnLeft,
420  mHoleOnRight,
421  mNeighborOnRight );
422 
423  mRemovedFaces.append( intersectionFaceIndex );
424 
425  if ( finishForcingLine() )
426  return true;
427  else
428  break;
429  }
430  else
431  {
432  // we cut an edge and start a new hole (without finishing it)
433 
434  nextCutFace = cutEdgeFromSnappedVertex( mEditor,
435  intersectionFaceIndex,
436  previousSnappedVertex,
437  edgePosition,
438  mHoleOnLeft,
439  mNeighborOnLeft,
440  mHoleOnRight,
441  mNeighborOnRight );
442 
443  mRemovedFaces.append( intersectionFaceIndex );
444 
445  int iv1 = mHoleOnLeft.last();
446  int iv2 = mHoleOnRight.last();
447 
448  if ( mNewVertexOnIntersection )
449  {
450  currentAddedVertex = startingVertexIndex + mVerticesToAdd.count();
451  mNewVerticesIndexesOnLine.append( mVerticesToAdd.count() );
452  if ( mInterpolateZValueOnMesh )
453  interpolateZValue( intersectionPoint,
454  triangularMesh->vertices().at( iv1 ),
455  triangularMesh->vertices().at( iv2 ) );
456  else
457  interpolateZValue( intersectionPoint, mPoint1, mPoint2 );
458  mVerticesToAdd.append( triangularMesh->triangularToNativeCoordinates( intersectionPoint ) );
459  }
460 
461  if ( nextCutFace != -1 )
462  {
463  mCurrentSnappedVertex = -1;
464  currentFaceIndex = nextCutFace;
465  currentEdge = {mHoleOnLeft.last(), mHoleOnRight.last()};
466  }
467  else
468  {
469  // we cut a boundary edge, we need to close the hole
470  if ( !mNewVertexOnIntersection )
471  {
472  currentAddedVertex = startingVertexIndex + mVerticesToAdd.count();
473  if ( mInterpolateZValueOnMesh )
474  interpolateZValue( intersectionPoint,
475  triangularMesh->vertices().at( iv1 ),
476  triangularMesh->vertices().at( iv2 ) );
477  else
478  interpolateZValue( intersectionPoint, mPoint1, mPoint2 );
479  mVerticesToAdd.append( triangularMesh->triangularToNativeCoordinates( intersectionPoint ) );
480  }
481  else
482  mNewVerticesIndexesOnLine.removeLast();
483 
484  mHoleOnLeft.append( currentAddedVertex );
485  mNeighborOnLeft.append( -1 );
486  mHoleOnRight.append( currentAddedVertex );
487  mNeighborOnRight.append( -1 );
488 
489  if ( finishForcingLine() )
490  return true;
491  else
492  break;
493 
494  leftFace = intersectionFaceIndex;
495  }
496 
497  mCurrentPointPosition = intersectionPoint;
498  }
499  }
500  }
501  else if ( nextCutFace != -1 )
502  {
503  const QgsMeshFace &face = mesh->face( nextCutFace );
504  const QVector<int> &neighbors = mEditor->topologicalMesh().neighborsOfFace( nextCutFace );
505  int faceSize = face.size();
506 
507  currentFaceIndex = nextCutFace;
508 
509  mRemovedFaces.append( nextCutFace );
510 
511  int edgePositionOnLeft = vertexPositionInFace( currentEdge.first, face );
512  int edgePositionOnRight = vertexPositionInFace( currentEdge.second, face );
513  int firstEdgeToTest = vertexPositionInFace( currentEdge.second, face );
514 
515  bool foundSomething = false;
516  //search for another edge or a snapped vertex
517  for ( int fi = 0; fi < faceSize; ++fi )
518  {
519  int iv1 = face.at( ( firstEdgeToTest + fi ) % faceSize );
520  int iv2 = face.at( ( firstEdgeToTest + fi + 1 ) % faceSize );
521 
522  if ( iv1 == currentEdge.first && iv2 == currentEdge.second )
523  continue;
524 
525  int snapVertex = -1;
526  QgsPoint intersection( 0, 0, 0 );
527  if ( edgeIntersection( iv1, iv2, snapVertex, intersection, false ) ||
528  snapVertex != -1 )
529  {
530  foundSomething = true;
531 
532  int endPositionOnRight;
533  int endPositionOnLeft;
534 
535  if ( snapVertex != -1 )
536  {
537  // closing holes
538  endPositionOnRight = vertexPositionInFace( snapVertex, face );
539  endPositionOnLeft = vertexPositionInFace( snapVertex, face );
540 
541  nextCutFace = -1;
542  currentEdge = {-1, -1};
543  mCurrentSnappedVertex = snapVertex;
544  }
545  else
546  {
547  // we cut another edge
548  endPositionOnLeft = vertexPositionInFace( iv2, face );
549  endPositionOnRight = vertexPositionInFace( iv1, face );
550 
551  nextCutFace = neighbors.at( endPositionOnRight );
552 
553  if ( mNewVertexOnIntersection )
554  {
555  currentAddedVertex = startingVertexIndex + mVerticesToAdd.count();
556  mNewVerticesIndexesOnLine.append( mVerticesToAdd.count() );
557  if ( mInterpolateZValueOnMesh )
558  interpolateZValue( intersection,
559  triangularMesh->vertices().at( iv1 ),
560  triangularMesh->vertices().at( iv2 ) );
561  else
562  interpolateZValue( intersection, mPoint1, mPoint2 );
563  mVerticesToAdd.append( triangularMesh->triangularToNativeCoordinates( intersection ) );
564  }
565  }
566 
567  int currentPos = edgePositionOnLeft;
568  while ( currentPos != endPositionOnLeft )
569  {
570  int nextPos = ( currentPos - 1 + faceSize ) % faceSize;
571  mHoleOnLeft.append( face.at( nextPos ) );
572  int neighborPos = nextPos;
573  mNeighborOnLeft.append( neighbors.at( neighborPos ) );
574  currentPos = nextPos;
575  }
576 
577  currentPos = edgePositionOnRight;
578  while ( currentPos != endPositionOnRight )
579  {
580  int nextPos = ( currentPos + 1 ) % faceSize;
581  mHoleOnRight.append( face.at( nextPos ) );
582  int neighborPos = ( nextPos + faceSize - 1 ) % faceSize;
583  mNeighborOnRight.append( neighbors.at( neighborPos ) );
584  currentPos = nextPos;
585  }
586 
587  mCurrentPointPosition = intersection;
588 
589  if ( snapVertex != -1 )
590  {
591  currentEdge = {-1, -1};
592 
593  if ( finishForcingLine() )
594  {
595  mIsFinished = mEndOnPoint2 && mPoint2VertexIndex == snapVertex;
596  return true;
597  }
598  else
599  {
600  mIsFinished = true;
601  return false;
602  }
603  }
604  else if ( nextCutFace == -1 )
605  {
606  // we had cut a boundary edge, we need to close the hole
607  if ( !mNewVertexOnIntersection )
608  {
609  currentAddedVertex = startingVertexIndex + mVerticesToAdd.count();
610  if ( mInterpolateZValueOnMesh )
611  interpolateZValue( intersection,
612  triangularMesh->vertices().at( iv1 ),
613  triangularMesh->vertices().at( iv2 ) );
614  else
615  interpolateZValue( intersection, mPoint1, mPoint2 );
616  mVerticesToAdd.append( triangularMesh->triangularToNativeCoordinates( intersection ) );
617  }
618  else
619  mNewVerticesIndexesOnLine.removeLast();
620 
621  mHoleOnLeft.append( currentAddedVertex );
622  mNeighborOnLeft.append( -1 );
623  mHoleOnRight.append( currentAddedVertex );
624  mNeighborOnRight.append( -1 );
625 
626  if ( finishForcingLine() )
627  return true;
628  else
629  {
630  mIsFinished = true;
631  return false;
632  }
633  }
634  else
635  currentEdge = {mHoleOnLeft.last(), mHoleOnRight.last()};
636 
637  break;
638  }
639  }
640 
641  if ( ! foundSomething )
642  {
643  // nothing intersected, something wrong -->leave with false
644  break;
645  }
646  }
647  else if ( currentFaceIndex == -1 || searchOutside )
648  {
649  // point1 is outside the mesh, we need to find the first face intersecting the segment of the line
650  const QgsRectangle bbox( mCurrentPointPosition, mPoint2 );
651  const QList<int> candidateFacesIndexes = triangularMesh->nativeFaceIndexForRectangle( bbox );
652  int closestFaceIndex = -1;
653  int closestEdge = -1; //the index of the first vertex of the edge (ccw)
654  int closestSnapVertex = -1;
655  double minimalDistance = std::numeric_limits<double>::max();
656  QgsPoint closestIntersectionPoint( 0, 0, 0 );
657  for ( const int candidateFaceIndex : candidateFacesIndexes )
658  {
659  if ( candidateFaceIndex == leftFace )
660  continue; //we have just left this face
661 
662  if ( treatedFaces.contains( candidateFaceIndex ) ) //we have already pass through this face
663  continue;
664 
665  const QgsMeshFace &candidateFace = mesh->face( candidateFaceIndex );
666  const int faceSize = candidateFace.size();
667 
668  for ( int i = 0; i < faceSize; ++i )
669  {
670  int iv1 = candidateFace.at( i );
671  int iv2 = candidateFace.at( ( i + 1 ) % faceSize );
672 
673  if ( iv1 == mCurrentSnappedVertex || iv2 == mCurrentSnappedVertex )
674  continue;
675 
676  int snapVertex = -1;
677  QgsPoint intersectionPoint;
678  bool isIntersect = edgeIntersection( iv1, iv2, snapVertex, intersectionPoint, true );
679 
680  if ( isIntersect )
681  {
682  double distance = intersectionPoint.distance( mCurrentPointPosition );
683  if ( distance < minimalDistance )
684  {
685  closestFaceIndex = candidateFaceIndex;
686  closestEdge = candidateFace.at( i );
687  closestSnapVertex = snapVertex;
688  closestIntersectionPoint = intersectionPoint;
689  minimalDistance = distance;
690  }
691  }
692  }
693  }
694 
695  if ( closestFaceIndex < 0 || closestEdge < 0 ) //we don't have an intersecting face
696  {
697  //nothing to do
698  break;
699  }
700  else //we have an intersecting, face
701  {
702  mCurrentSnappedVertex = closestSnapVertex;
703  treatedFaces.insert( closestFaceIndex );
704  searchOutside = false;
705  if ( mCurrentSnappedVertex == -1 )
706  {
707  //we cut an edge when entering the mesh
708  const QgsMeshFace &face = mesh->face( closestFaceIndex );
709  currentAddedVertex = startingVertexIndex + mVerticesToAdd.count();
710 
711  nextCutFace = closestFaceIndex;
712 
713  mHoleOnRight.append( currentAddedVertex );
714  mHoleOnRight.append( face.at( ( vertexPositionInFace( closestEdge, face ) + 1 ) % face.size() ) );
715  mNeighborOnRight.append( -1 );
716 
717 
718  mHoleOnLeft.append( currentAddedVertex );
719  mHoleOnLeft.append( closestEdge ) ;
720  mNeighborOnLeft.append( -1 );
721 
722  currentEdge = {mHoleOnLeft.last(), mHoleOnRight.last()};
723 
724  if ( mInterpolateZValueOnMesh )
725  interpolateZValue( closestIntersectionPoint,
726  triangularMesh->vertices().at( mHoleOnLeft.last() ),
727  triangularMesh->vertices().at( mHoleOnRight.last() ) );
728  else
729  interpolateZValue( closestIntersectionPoint, mPoint1, mPoint2 );
730 
731  mVerticesToAdd.append( triangularMesh->triangularToNativeCoordinates( closestIntersectionPoint ) );
732 
733  }
734  }
735  }
736  else if ( mCurrentSnappedVertex == -1 ) // we are in a face without snapped vertex (yet)
737  {
738  //check in we snap with any of the vertices
739  double minimalDistance = std::numeric_limits<double>::max();
740  const QgsMeshFace &face = mesh->face( currentFaceIndex );
741  for ( int i = 0; i < face.size(); ++i )
742  {
743  const QgsMeshVertex &vertex = triangularMesh->vertices().at( face.at( i ) );
744  const double distance = mCurrentPointPosition.distance( vertex );
745  if ( distance < mTolerance && distance < minimalDistance )
746  {
747  minimalDistance = distance;
748  mCurrentSnappedVertex = face.at( i );
749  }
750  }
751  searchOutside = false;
752 
753  if ( mCurrentSnappedVertex == -1 )
754  {
755  //we can't be in a face without snap vertex --> leave
756  break;
757  }
758  }
759  if ( mCurrentSnappedVertex != -1 )
760  {
761  if ( mCurrentSnappedVertex == mPoint2VertexIndex )
762  {
763  mIsFinished = true;
764  return true;
765  }
766  mCurrentPointPosition = triangularMesh->vertices().at( mCurrentSnappedVertex );
767  }
768  }
769 
770  mIsFinished = true;
771  return false;
772 }
773 
774 
775 bool QgsMeshEditForceByLine::searchIntersectionEdgeFromSnappedVertex
776 ( int &intersectionFaceIndex,
777  int &previousSnappedVertex,
778  int &currentSnappedVertexIndex,
779  QgsPoint &intersectionPoint,
780  int &edgePosition,
781  QSet<int> &treatedFaces )
782 {
783  previousSnappedVertex = currentSnappedVertexIndex;
784  QSet<int> treatedVertices;
785 
786  while ( true )
787  {
788  treatedVertices.insert( currentSnappedVertexIndex );
789  const QList<int> facesAround = mEditor->topologicalMesh().facesAroundVertex( currentSnappedVertexIndex );
790 
791  bool foundSomething = false;
792  for ( const int faceIndex : std::as_const( facesAround ) )
793  {
794  const QgsMeshFace &face = mEditor->topologicalMesh().mesh()->face( faceIndex );
795  int faceSize = face.size();
796  int vertexPos = vertexPositionInFace( currentSnappedVertexIndex, face );
797  QgsPoint oppositeIntersectionPoint;
798  int newSnapVertex = -1;
799  for ( int i = 1; i < faceSize - 1; ++i )
800  {
801  edgePosition = ( vertexPos + i ) % faceSize ;
802  foundSomething = edgeIntersection( face.at( edgePosition ),
803  face.at( ( edgePosition + 1 ) % faceSize ),
804  newSnapVertex,
805  intersectionPoint,
806  false );
807 
808  if ( mEndOnPoint2 && newSnapVertex == mPoint2VertexIndex )
809  {
810  mCurrentSnappedVertex = newSnapVertex;
811  return true;
812  }
813 
814  if ( newSnapVertex != -1 )
815  foundSomething = ( newSnapVertex != previousSnappedVertex &&
816  !treatedVertices.contains( newSnapVertex ) );
817 
818  if ( foundSomething )
819  break;
820  }
821 
822  if ( foundSomething )
823  {
824  treatedFaces.insert( faceIndex );
825  if ( newSnapVertex != -1 )
826  {
827  previousSnappedVertex = currentSnappedVertexIndex;
828  currentSnappedVertexIndex = newSnapVertex;
829  if ( newSnapVertex == face.at( ( vertexPos + 1 ) % faceSize ) || newSnapVertex == face.at( ( vertexPos - 1 + faceSize ) % faceSize ) )
830  {
831  // the two vertices already share an edge
832  mCurrentPointPosition = mEditor->triangularMesh()->vertices().at( newSnapVertex );
833  break;
834  }
835  else
836  {
837  intersectionFaceIndex = faceIndex;
838  return true;
839  }
840  }
841  else
842  {
843  intersectionFaceIndex = faceIndex;
844  previousSnappedVertex = currentSnappedVertexIndex;
845  currentSnappedVertexIndex = -1;
846  return true;
847  }
848  }
849  }
850 
851  if ( !foundSomething )
852  break;
853  }
854 
855  return false;
856 }
857 
858 bool QgsMeshEditForceByLine::triangulateHoles(
859  const QList<int> &hole,
860  const QList<int> &neighbors,
861  bool isLeftHole,
862  QList<std::array<int, 2>> &newFacesOnLine,
863  std::array<int, 2> &extremeFaces )
864 {
865  QgsMesh *mesh = mEditor->topologicalMesh().mesh();
866 
867  // Check if we don't have duplicate vertex, that could lead to a crash
868  for ( int i = 0; i < hole.count(); ++i )
869  {
870  for ( int j = i + 1; j < hole.count(); ++j )
871  {
872  if ( hole.at( i ) == hole.at( j ) )
873  return false;
874 
875  if ( mesh->vertex( hole.at( i ) ).distance( mesh->vertex( hole.at( j ) ) ) < mTolerance )
876  return false;
877  }
878  }
879 
880  int startingFaceGlobalIndex = mAddedFacesFirstIndex + mFacesToAdd.count();
881  int startingFaceLocalIndex = mFacesToAdd.count();
882 
883  std::vector<p2t::Point *> holeToFill;
884  try
885  {
886  QHash<p2t::Point *, int> mapPoly2TriPointToVertex;
887  holeToFill.resize( hole.count() + mNewVerticesIndexesOnLine.count() );
888  for ( int i = 0; i < hole.count(); ++i )
889  {
890  int vertexIndex = hole.at( i );
891  QgsMeshVertex vertex;
892  if ( vertexIndex < mesh->vertexCount() )
893  vertex = mesh->vertex( vertexIndex );
894  else
895  vertex = mVerticesToAdd.at( vertexIndex - mesh->vertexCount() );
896 
897  holeToFill[i] = new p2t::Point( vertex.x(), vertex.y() );
898  mapPoly2TriPointToVertex.insert( holeToFill[i], vertexIndex );
899  }
900 
901  const int verticesOnLineCount = mNewVerticesIndexesOnLine.count();
902  for ( int i = 0; i < verticesOnLineCount; ++i )
903  {
904  int vertexLocalIndex = mNewVerticesIndexesOnLine.at( verticesOnLineCount - i - 1 );
905  const QgsMeshVertex &vertex = mVerticesToAdd.at( vertexLocalIndex );
906  holeToFill[i + hole.count()] = new p2t::Point( vertex.x(), vertex.y() );
907  mapPoly2TriPointToVertex.insert( holeToFill[i + hole.count()], vertexLocalIndex + mesh->vertexCount() );
908  }
909 
910  std::unique_ptr<p2t::CDT> cdt( new p2t::CDT( holeToFill ) );
911  cdt->Triangulate();
912  std::vector<p2t::Triangle *> triangles = cdt->GetTriangles();
913  QVector<QgsMeshFace> newFaces( triangles.size() );
914  for ( size_t i = 0; i < triangles.size(); ++i )
915  {
916  QgsMeshFace &face = newFaces[i];
917  face.resize( 3 );
918  for ( int j = 0; j < 3; j++ )
919  {
920  int vertInd = mapPoly2TriPointToVertex.value( triangles.at( i )->GetPoint( j ), -1 );
921  if ( vertInd == -1 )
922  throw std::exception();
923  face[j] = vertInd;
924  }
925  }
926 
927  QgsMeshEditingError error;
928  QgsTopologicalMesh::TopologicalFaces topologicalFaces = QgsTopologicalMesh::createNewTopologicalFaces( newFaces, false, error );
930  throw std::exception();
931 
932  const QVector<QgsMeshFace> &facesToAdd = topologicalFaces.meshFaces();
933  mFacesToAdd.append( facesToAdd );
934  mFacesNeighborhoodToAdd.append( topologicalFaces.facesNeighborhood() );
935 
936  for ( const int vtc : hole )
937  {
938  int firstLinkedFace = mEditor->topologicalMesh().firstFaceLinked( vtc );
939  if ( mRemovedFaces.contains( firstLinkedFace ) )
940  mVerticesToFaceChanges.append( {vtc, firstLinkedFace, topologicalFaces.vertexToFace( vtc ) + startingFaceGlobalIndex} );
941  }
942 
943  // reindex neighborhood for new faces
944  for ( int i = 0; i < facesToAdd.count(); ++i )
945  {
946  QgsTopologicalMesh::FaceNeighbors &faceNeighbors = mFacesNeighborhoodToAdd[i + startingFaceLocalIndex];
947  faceNeighbors = topologicalFaces.facesNeighborhood().at( i );
948  for ( int n = 0; n < faceNeighbors.count(); ++n )
949  {
950  if ( faceNeighbors.at( n ) != -1 )
951  faceNeighbors[n] += startingFaceGlobalIndex; //reindex internal neighborhood
952  }
953  }
954 
955  // link neighborhood for boundaries of each side
956  for ( int i = 0 ; i < hole.count() - 1; ++i )
957  {
958  int vertexHoleIndex = hole.at( i );
959  int meshFaceBoundaryIndex = neighbors.at( i );
960 
961  QgsMeshVertexCirculator circulator = QgsMeshVertexCirculator( topologicalFaces, vertexHoleIndex );
962  if ( !circulator.isValid() )
963  throw std::exception();
964 
965  if ( isLeftHole )
966  circulator.goBoundaryCounterClockwise();
967  else
968  circulator.goBoundaryClockwise();
969 
970  int newFaceBoundaryLocalIndex = circulator.currentFaceIndex();
971  int newFaceBoundaryIndexInMesh = circulator.currentFaceIndex() + startingFaceGlobalIndex;
972  const QgsMeshFace &newFace = circulator.currentFace();
973  int positionInNewFaces = vertexPositionInFace( vertexHoleIndex, newFace );
974  if ( isLeftHole )
975  positionInNewFaces = ( positionInNewFaces - 1 + newFace.size() ) % newFace.size(); //take the index just before
976 
977  if ( meshFaceBoundaryIndex != -1 )
978  {
979  const QgsMeshFace &meshFace = mesh->face( meshFaceBoundaryIndex );
980 
981  int positionInMeshFaceBoundary = vertexPositionInFace( *mesh, vertexHoleIndex, meshFaceBoundaryIndex );
982  if ( !isLeftHole )
983  positionInMeshFaceBoundary = ( positionInMeshFaceBoundary - 1 + meshFace.size() ) % meshFace.size(); //take the index just before
984 
985  int oldNeighbor = mEditor->topologicalMesh().neighborsOfFace( meshFaceBoundaryIndex ).at( positionInMeshFaceBoundary );
986  mNeighborhoodChanges.append( {meshFaceBoundaryIndex, positionInMeshFaceBoundary, oldNeighbor, newFaceBoundaryIndexInMesh} );
987  }
988 
989  mFacesNeighborhoodToAdd[newFaceBoundaryLocalIndex + startingFaceLocalIndex][positionInNewFaces] = meshFaceBoundaryIndex;
990  }
991 
992  // search for the new face that are on the forcing line
993  int vertexOnLine = hole.at( 0 );
994  while ( vertexOnLine != hole.last() )
995  {
996  QgsMeshVertexCirculator circulatorOnLine( topologicalFaces, vertexOnLine );
997  int nextVertex;
998  if ( isLeftHole )
999  {
1000  circulatorOnLine.goBoundaryClockwise();
1001  nextVertex = circulatorOnLine.oppositeVertexClockwise();
1002  }
1003  else
1004  {
1005  circulatorOnLine.goBoundaryCounterClockwise();
1006  nextVertex = circulatorOnLine.oppositeVertexCounterClockwise();
1007  }
1008  const QgsMeshFace &faceOnLine = circulatorOnLine.currentFace();
1009  int positionOnFaceOnTheLine = vertexPositionInFace( vertexOnLine, faceOnLine );
1010  if ( !isLeftHole )
1011  positionOnFaceOnTheLine = ( positionOnFaceOnTheLine - 1 + faceOnLine.size() ) % faceOnLine.size();
1012 
1013  newFacesOnLine.append( {circulatorOnLine.currentFaceIndex() + startingFaceLocalIndex, positionOnFaceOnTheLine} );
1014  vertexOnLine = nextVertex;
1015  }
1016 
1017  QgsMeshVertexCirculator circulatorOnStart( topologicalFaces, hole.first() );
1018  if ( isLeftHole )
1019  circulatorOnStart.goBoundaryCounterClockwise();
1020  else
1021  circulatorOnStart.goBoundaryClockwise();
1022 
1023  QgsMeshVertexCirculator circulatorOnEnd( topologicalFaces, hole.last() );
1024  if ( isLeftHole )
1025  circulatorOnEnd.goBoundaryClockwise();
1026  else
1027  circulatorOnEnd.goBoundaryCounterClockwise();
1028 
1029 
1030  extremeFaces = {circulatorOnStart.currentFaceIndex() + startingFaceLocalIndex,
1031  circulatorOnEnd.currentFaceIndex() + startingFaceLocalIndex
1032  };
1033  qDeleteAll( holeToFill );
1034  }
1035  catch ( ... )
1036  {
1037  qDeleteAll( holeToFill );
1038  return false;
1039  }
1040 
1041 
1042 
1043  return true;
1044 }
1045 
1046 bool QgsMeshEditForceByLine::finishForcingLine()
1047 {
1048  QgsMesh *mesh = mEditor->topologicalMesh().mesh();
1049 
1050  QList<std::array<int, 2>> newLeftFacesOnLine;
1051  QList<std::array<int, 2>> newRightFacesOnLine;
1052 
1053  std::array<int, 2> extremeFacesOnLeft;
1054  std::array<int, 2> extremeFacesOnRight;
1055 
1056  if ( !triangulateHoles( mHoleOnLeft, mNeighborOnLeft, true, newLeftFacesOnLine, extremeFacesOnLeft ) )
1057  return false;
1058  if ( !triangulateHoles( mHoleOnRight, mNeighborOnRight, false, newRightFacesOnLine, extremeFacesOnRight ) )
1059  return false;
1060 
1061  //link the vertices that are on the line
1062  if ( newLeftFacesOnLine.count() != newRightFacesOnLine.count() )
1063  return false;
1064 
1065  for ( int i = 0; i < newLeftFacesOnLine.count(); ++i )
1066  {
1067  int leftFaceLocalIndex = newLeftFacesOnLine.at( i ).at( 0 );
1068  int leftPositionInFace = newLeftFacesOnLine.at( i ).at( 1 );
1069  int rightFaceLocalIndex = newRightFacesOnLine.at( i ).at( 0 );
1070  int rightPositionInFace = newRightFacesOnLine.at( i ).at( 1 );
1071 
1072  mFacesNeighborhoodToAdd[leftFaceLocalIndex][leftPositionInFace] = mAddedFacesFirstIndex + rightFaceLocalIndex;
1073  mFacesNeighborhoodToAdd[rightFaceLocalIndex][rightPositionInFace] = mAddedFacesFirstIndex + leftFaceLocalIndex;
1074  }
1075 
1076  // set the vertices to face
1077  mVertexToFaceToAdd.resize( mVerticesToAdd.count() );
1078  const int firstVertexIndex = mHoleOnLeft.first();
1079  if ( firstVertexIndex >= mesh->vertexCount() )
1080  {
1081  const int firstVertexLocalIndex = firstVertexIndex - mesh->vertexCount();
1082  mVertexToFaceToAdd[firstVertexLocalIndex] = newLeftFacesOnLine.first().at( 0 ) + mesh->faceCount();
1083  }
1084 
1085  const int lastVertexIndex = mHoleOnLeft.last();
1086  if ( lastVertexIndex >= mesh->vertexCount() )
1087  {
1088  const int lastVertexLocalIndex = lastVertexIndex - mesh->vertexCount();
1089  mVertexToFaceToAdd[lastVertexLocalIndex] = newLeftFacesOnLine.last().at( 0 ) + mesh->faceCount();
1090  }
1091 
1092  for ( int i = 0; i < mNewVerticesIndexesOnLine.count(); ++i )
1093  {
1094  mVertexToFaceToAdd[mNewVerticesIndexesOnLine.at( i )] = newLeftFacesOnLine.at( i ).at( 0 ) + mesh->faceCount();
1095  }
1096 
1097  for ( const int fi : std::as_const( mRemovedFaces ) )
1098  {
1099  mFacesToRemove.append( mesh->face( fi ) );
1100  mFaceIndexesToRemove.append( fi );
1101  mFacesNeighborhoodToRemove.append( mEditor->topologicalMesh().neighborsOfFace( fi ) );
1102  }
1103 
1104  mRemovedFaces.clear();
1105  mHoleOnLeft.clear();
1106  mNeighborOnLeft.clear();
1107  mHoleOnRight.clear();
1108  mNeighborOnRight.clear();
1109  mNewVerticesIndexesOnLine.clear();
1110  return true;
1111 }
1112 
1113 
1115 {
1116  return QObject::tr( "Force mesh by polyline" );
1117 }
1118 
1120 {
1121  return mCurrentPolyline >= mPolylines.count() && QgsMeshEditForceByLine::isFinished();
1122 }
1123 
1124 QgsTopologicalMesh::Changes QgsMeshEditForceByPolylines::apply( QgsMeshEditor *meshEditor )
1125 {
1126  if ( mPolylines.isEmpty() )
1127  {
1128  mIsFinished = true;
1129  return QgsTopologicalMesh::Changes();
1130  }
1131 
1132  if ( mCurrentPolyline == 0 && mCurrentSegment == 0 )
1133  {
1134  setInputLine( mPolylines.at( 0 ).at( 0 ),
1135  mPolylines.at( 0 ).at( 1 ),
1136  mTolerance, mNewVertexOnIntersection );
1137 
1138  incrementSegment();
1139  return QgsMeshEditForceByLine::apply( meshEditor );
1140  }
1141 
1143  {
1144  setInputLine( mPolylines.at( mCurrentPolyline ).at( mCurrentSegment ),
1145  mPolylines.at( mCurrentPolyline ).at( mCurrentSegment + 1 ),
1146  mTolerance, mNewVertexOnIntersection );
1147 
1148  incrementSegment();
1149  }
1150  return QgsMeshEditForceByLine::apply( meshEditor );
1151 }
1152 
1154 {
1155  std::vector<const QgsCurve *> curves;
1157  {
1158  std::vector< const QgsCurvePolygon * > polygons;
1159  if ( geom.isMultipart() )
1160  {
1161  const QgsMultiSurface *ms = qgsgeometry_cast< const QgsMultiSurface * >( geom.constGet() );
1162  for ( int i = 0; i < ms->numGeometries(); ++i )
1163  polygons.emplace_back( qgsgeometry_cast< const QgsCurvePolygon * >( ms->geometryN( i ) ) );
1164  }
1165  else
1166  polygons.emplace_back( qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() ) );
1167 
1168  for ( const QgsCurvePolygon *polygon : polygons )
1169  {
1170  if ( !polygon )
1171  continue;
1172 
1173  if ( polygon->exteriorRing() )
1174  curves.emplace_back( polygon->exteriorRing() );
1175 
1176  for ( int i = 0; i < polygon->numInteriorRings(); ++i )
1177  curves.emplace_back( polygon->interiorRing( i ) );
1178  }
1179  }
1180  else
1181  {
1182  if ( geom.isMultipart() )
1183  {
1184  const QgsMultiCurve *mc = qgsgeometry_cast< const QgsMultiCurve * >( geom.constGet() );
1185  for ( int i = 0; i < mc->numGeometries(); ++i )
1186  curves.emplace_back( qgsgeometry_cast< const QgsCurve * >( mc->geometryN( i ) ) );
1187  }
1188  else
1189  curves.emplace_back( qgsgeometry_cast< const QgsCurve * >( geom.constGet() ) );
1190  }
1191 
1192  for ( const QgsCurve *curve : curves )
1193  {
1194  if ( !curve )
1195  continue;
1196 
1197  QgsPointSequence linePoints;
1198  curve->points( linePoints );
1199  if ( linePoints.count() < 2 )
1200  continue;
1201  if ( !curve->is3D() )
1202  {
1203  for ( int i = 0; i < linePoints.count(); ++i )
1204  {
1205  const QgsPoint &point = linePoints.at( i );
1206  linePoints[i] = QgsPoint( point.x(), point.y(), mDefaultZValue );
1207  }
1208  }
1209  mPolylines.append( linePoints );
1210  }
1211 }
1212 
1213 void QgsMeshEditForceByPolylines::addLinesFromGeometries( const QList<QgsGeometry> geometries )
1214 {
1215  for ( const QgsGeometry &geom : geometries )
1216  addLineFromGeometry( geom );
1217 }
1218 
1220 {
1221  mTolerance = tolerance;
1222 }
1223 
1225 {
1226  mNewVertexOnIntersection = addVertex;
1227 }
1228 
1229 void QgsMeshEditForceByLine::setDefaultZValue( double defaultZValue )
1230 {
1231  mDefaultZValue = defaultZValue;
1232 }
1233 
1234 void QgsMeshEditForceByLine::setInterpolateZValueOnMesh( bool interpolateZValueOnMesh )
1235 {
1236  mInterpolateZValueOnMesh = interpolateZValueOnMesh;
1237 }
1238 
1239 void QgsMeshEditForceByPolylines::incrementSegment()
1240 {
1241  mCurrentSegment++;
1242  if ( mCurrentSegment >= mPolylines.at( mCurrentPolyline ).count() - 1 )
1243  {
1244  mCurrentSegment = 0;
1245  mCurrentPolyline++;
1246  }
1247 }
QgsCurve
Abstract base class for curved geometry type.
Definition: qgscurve.h:35
QgsTopologicalMesh::TopologicalFaces::vertexToFace
int vertexToFace(int vertexIndex) const
Returns a face linked to the vertices with index vertexIndex.
Definition: qgstopologicalmesh.cpp:1631
qgsmeshlayerutils.h
QgsMeshVertexCirculator::currentFace
QgsMeshFace currentFace() const
Returns the current face, empty face if the circulator pass a boundary or circulator is invalid.
Definition: qgstopologicalmesh.cpp:139
QgsTopologicalMesh::Changes::addedVertices
QVector< QgsMeshVertex > addedVertices() const
Returns the added vertices with this changes.
Definition: qgstopologicalmesh.cpp:1014
QgsMeshVertexCirculator::goBoundaryClockwise
bool goBoundaryClockwise() const
Sets the circulator on the boundary face turning clockwise, return false is there isn't boundary face...
Definition: qgstopologicalmesh.cpp:147
QgsTriangularMesh::nativeFaceIndexForPoint
int nativeFaceIndexForPoint(const QgsPointXY &point) const
Finds index of native face at given point It uses spatial indexing.
Definition: qgstriangularmesh.cpp:427
QgsTopologicalMesh::TopologicalFaces::facesNeighborhood
QVector< FaceNeighbors > facesNeighborhood() const
Returns the face neighborhood of the faces, indexing is local.
Definition: qgstopologicalmesh.cpp:1626
QgsTopologicalMesh::Changes::mAddedFacesFirstIndex
int mAddedFacesFirstIndex
Definition: qgstopologicalmesh.h:134
QgsTriangularMesh::vertices
const QVector< QgsMeshVertex > & vertices() const
Returns vertices in map coordinate system.
Definition: qgstriangularmesh.cpp:352
QgsTopologicalMesh::TopologicalFaces
Class that contains independent faces an topological information about this faces.
Definition: qgstopologicalmesh.h:62
qgslinestring.h
QgsTopologicalMesh::Changes
Class that contains topological differences between two states of a topological mesh,...
Definition: qgstopologicalmesh.h:96
QgsPoint::distance
double distance(double x, double y) const SIP_HOLDGIL
Returns the Cartesian 2D distance between this point and a specified x, y coordinate.
Definition: qgspoint.h:360
QgsTopologicalMesh::FaceNeighbors
QVector< int > FaceNeighbors
Definition: qgstopologicalmesh.h:51
QgsPoint
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:48
QgsMeshEditForceByLine::setAddVertexOnIntersection
void setAddVertexOnIntersection(bool addVertex)
Sets whether vertices will be added when the lines will intersect internal edges of faces,...
Definition: qgsmeshforcebypolylines.cpp:1224
QgsMeshEditForceByPolylines::isFinished
bool isFinished() const override
Returns whether the advanced edit is finished, if not, this edit has to be applied again with QgsMesh...
Definition: qgsmeshforcebypolylines.cpp:1119
QgsMeshEditForceByPolylines::text
QString text() const override
Returns a short text string describing what this advanced edit does. Default implementation return a ...
Definition: qgsmeshforcebypolylines.cpp:1114
QgsMeshVertexCirculator::isValid
bool isValid() const
Returns whether the vertex circulator is valid.
Definition: qgstopologicalmesh.cpp:217
QgsCurvePolygon
Curve polygon geometry type.
Definition: qgscurvepolygon.h:34
QgsPoint::z
double z
Definition: qgspoint.h:71
QgsTriangularMesh::triangles
const QVector< QgsMeshFace > & triangles() const
Returns triangles.
Definition: qgstriangularmesh.cpp:357
QgsMesh
Mesh - vertices, edges and faces.
Definition: qgsmeshdataprovider.h:58
QgsMultiSurface
Multi surface geometry collection.
Definition: qgsmultisurface.h:32
QgsMeshEditor::edgeIsClose
bool edgeIsClose(QgsPointXY point, double tolerance, int &faceIndex, int &edgePosition)
Returns true if an edge of face is closest than the tolerance from the point in triangular mesh coord...
Definition: qgsmesheditor.cpp:387
QgsMeshVertexCirculator
Convenient class that turn around a vertex and provide information about faces and vertices.
Definition: qgstopologicalmesh.h:379
QgsMeshVertexCirculator::goBoundaryCounterClockwise
bool goBoundaryCounterClockwise() const
Sets the circulator on the boundary face turning counter clockwise, return false is there isn't bound...
Definition: qgstopologicalmesh.cpp:164
QgsRectangle
A rectangle specified with double values.
Definition: qgsrectangle.h:41
QgsWkbTypes::PolygonGeometry
@ PolygonGeometry
Definition: qgswkbtypes.h:144
QgsGeometryCollection::numGeometries
int numGeometries() const SIP_HOLDGIL
Returns the number of geometries within the collection.
Definition: qgsgeometrycollection.h:58
QgsTopologicalMesh::neighborsOfFace
QVector< int > neighborsOfFace(int faceIndex) const
Returns the indexes of neighbor faces of the face with index faceIndex.
Definition: qgstopologicalmesh.cpp:1777
QgsGeometry::isMultipart
bool isMultipart() const SIP_HOLDGIL
Returns true if WKB of the geometry is of WKBMulti* type.
Definition: qgsgeometry.cpp:389
QgsTopologicalMesh::facesAroundVertex
QList< int > facesAroundVertex(int vertexIndex) const
Returns the indexes of faces that are around the vertex with index vertexIndex.
Definition: qgstopologicalmesh.cpp:1782
QgsMeshEditor
Class that makes edit operation on a mesh.
Definition: qgsmesheditor.h:67
QgsMeshAdvancedEditing::clear
void clear()
Removes all data provided to the editing or created by the editing.
Definition: qgsmeshadvancedediting.cpp:46
QgsTopologicalMesh::TopologicalFaces::meshFaces
QVector< QgsMeshFace > meshFaces() const
Returns faces.
Definition: qgstopologicalmesh.h:67
QgsTopologicalMesh::Changes::mFacesNeighborhoodToAdd
QVector< FaceNeighbors > mFacesNeighborhoodToAdd
Definition: qgstopologicalmesh.h:137
QgsPoint::y
double y
Definition: qgspoint.h:70
QgsMeshEditor::triangularMesh
QgsTriangularMesh * triangularMesh()
Returns a pointer to the triangular mesh.
Definition: qgsmesheditor.cpp:1019
QgsTopologicalMesh::Changes::mNeighborhoodChanges
QList< std::array< int, 4 > > mNeighborhoodChanges
Definition: qgstopologicalmesh.h:140
QgsMeshAdvancedEditing::isFinished
virtual bool isFinished() const
Returns whether the advanced edit is finished, if not, this edit has to be applied again with QgsMesh...
Definition: qgsmeshadvancedediting.cpp:55
QgsMesh::faceCount
int faceCount() const
Returns number of faces.
Definition: qgsmeshdataprovider.cpp:205
qgsmeshforcebypolylines.h
QgsTopologicalMesh::Changes::mFacesToRemove
QVector< QgsMeshFace > mFacesToRemove
Definition: qgstopologicalmesh.h:138
QgsMesh::vertexCount
int vertexCount() const
Returns number of vertices.
Definition: qgsmeshdataprovider.cpp:200
QgsMultiCurve
Multi curve geometry collection.
Definition: qgsmulticurve.h:29
QgsTriangularMesh::nativeFaceIndexForRectangle
QList< int > nativeFaceIndexForRectangle(const QgsRectangle &rectangle) const
Finds indexes of native faces which bounding boxes intersect given bounding box It uses spatial index...
Definition: qgstriangularmesh.cpp:439
QgsMeshEditForceByPolylines::addLinesFromGeometries
void addLinesFromGeometries(const QList< QgsGeometry > geometries)
Adds a list of input forcing lines geometry in rendering coordinates.
Definition: qgsmeshforcebypolylines.cpp:1213
QgsMeshEditingError
Class that represents an error during mesh editing.
Definition: qgsmesheditor.h:42
QgsMesh::face
QgsMeshFace face(int index) const
Returns a face at the index.
Definition: qgsmeshdataprovider.cpp:145
QgsTopologicalMesh::Changes::mFacesToAdd
QVector< QgsMeshFace > mFacesToAdd
Definition: qgstopologicalmesh.h:136
QgsTopologicalMesh::addVertexInFace
Changes addVertexInFace(int faceIndex, const QgsMeshVertex &vertex)
Adds a vertex in the face with index faceIndex.
Definition: qgstopologicalmesh.cpp:2264
QgsTopologicalMesh::Changes::mVertexToFaceToAdd
QVector< int > mVertexToFaceToAdd
Definition: qgstopologicalmesh.h:143
QgsTriangularMesh::faceIndexForPoint_v2
int faceIndexForPoint_v2(const QgsPointXY &point) const
Finds index of triangle at given point It uses spatial indexing and don't use geos to be faster.
Definition: qgstriangularmesh.cpp:447
QgsGeometryUtils::segmentIntersection
static bool segmentIntersection(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &q1, const QgsPoint &q2, QgsPoint &intersectionPoint, bool &isIntersection, double tolerance=1e-8, bool acceptImproperIntersection=false) SIP_HOLDGIL
Compute the intersection between two segments.
Definition: qgsgeometryutils.cpp:277
QgsMeshFace
QVector< int > QgsMeshFace
List of vertex indexes.
Definition: qgsmeshdataprovider.h:42
qgscurvepolygon.h
QgsTopologicalMesh::Changes::mVerticesToFaceChanges
QList< std::array< int, 3 > > mVerticesToFaceChanges
Definition: qgstopologicalmesh.h:147
QgsGeometry::constGet
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
Definition: qgsgeometry.cpp:136
qgsmesheditor.h
QgsTopologicalMesh::insertVertexInFacesEdge
Changes insertVertexInFacesEdge(int faceIndex, int position, const QgsMeshVertex &vertex)
Inserts a vertex in the edge of face with index faceIndex at position .
Definition: qgstopologicalmesh.cpp:2327
qgsgeometryutils.h
QgsPointXY
A class to represent a 2D point.
Definition: qgspointxy.h:58
QgsTopologicalMesh::Changes::mFaceIndexesToRemove
QList< int > mFaceIndexesToRemove
Definition: qgstopologicalmesh.h:135
QgsMeshAdvancedEditing::mIsFinished
bool mIsFinished
Definition: qgsmeshadvancedediting.h:72
QgsGeometryCollection::geometryN
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
Definition: qgsgeometrycollection.h:86
QgsPointSequence
QVector< QgsPoint > QgsPointSequence
Definition: qgsabstractgeometry.h:52
QgsMeshEditForceByLine::setInputLine
void setInputLine(const QgsPoint &pt1, const QgsPoint &pt2, double tolerance, bool newVertexOnIntersection)
Sets the input forcing line in rendering coordinates.
Definition: qgsmeshforcebypolylines.cpp:190
QgsGeometry
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
QgsTopologicalMesh::Changes::mVerticesToAdd
QVector< QgsMeshVertex > mVerticesToAdd
Definition: qgstopologicalmesh.h:142
QgsMesh::vertices
QVector< QgsMeshVertex > vertices
Definition: qgsmeshdataprovider.h:112
QgsMeshEditForceByLine::setInterpolateZValueOnMesh
void setInterpolateZValueOnMesh(bool interpolateZValueOnMesh)
Sets whether the new created vertices will have their value interpolated from the existing mesh.
Definition: qgsmeshforcebypolylines.cpp:1234
QgsTriangularMesh::triangularToNativeCoordinates
QgsMeshVertex triangularToNativeCoordinates(const QgsMeshVertex &vertex) const
Transforms the vertex from triangular mesh coordinates system to native coordinates system.
Definition: qgstriangularmesh.cpp:282
QgsWkbTypes::geometryType
static GeometryType geometryType(Type type) SIP_HOLDGIL
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Definition: qgswkbtypes.h:968
qgsmulticurve.h
QgsTopologicalMesh::applyChanges
void applyChanges(const Changes &changes)
Applies the changes.
Definition: qgstopologicalmesh.cpp:354
QgsMeshEditingError::errorType
Qgis::MeshEditingErrorType errorType
Definition: qgsmesheditor.h:52
QgsTriangularMesh
Triangular/Derived Mesh is mesh with vertices in map coordinates.
Definition: qgstriangularmesh.h:51
QgsMeshEditForceByLine::setTolerance
void setTolerance(double tolerance)
Sets the tolerance in redering coordinate system unit.
Definition: qgsmeshforcebypolylines.cpp:1219
QgsTopologicalMesh::createNewTopologicalFaces
static TopologicalFaces createNewTopologicalFaces(const QVector< QgsMeshFace > &faces, bool uniqueSharedVertexAllowed, QgsMeshEditingError &error)
Creates new topological faces that are not yet included in the mesh.
Definition: qgstopologicalmesh.cpp:1681
Qgis::MeshEditingErrorType::NoError
@ NoError
No type.
QgsMeshEditForceByLine::setDefaultZValue
void setDefaultZValue(double defaultZValue)
Sets the default value of Z coordinate to use for new vertices, this value will be used if the Z valu...
Definition: qgsmeshforcebypolylines.cpp:1229
QgsTopologicalMesh::Changes::mFacesNeighborhoodToRemove
QVector< FaceNeighbors > mFacesNeighborhoodToRemove
Definition: qgstopologicalmesh.h:139
QgsPoint::setZ
void setZ(double z) SIP_HOLDGIL
Sets the point's z-coordinate.
Definition: qgspoint.h:321
QgsTopologicalMesh::mesh
QgsMesh * mesh() const
Returns a pointer to the wrapped mesh.
Definition: qgstopologicalmesh.cpp:592
QgsMesh::vertex
QgsMeshVertex vertex(int index) const
Returns a vertex at the index.
Definition: qgsmeshdataprovider.cpp:138
QgsPoint::x
double x
Definition: qgspoint.h:69
QgsMeshEditor::topologicalMesh
QgsTopologicalMesh & topologicalMesh()
Returns a reference to the topological mesh.
Definition: qgsmesheditor.cpp:1014
qgsmultisurface.h
QgsMeshVertexCirculator::currentFaceIndex
int currentFaceIndex() const
Returns the current face index, -1 if the circulator has passed a boundary or circulator is invalid.
Definition: qgstopologicalmesh.cpp:134
QgsGeometry::wkbType
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
Definition: qgsgeometry.cpp:357
QgsMeshEditForceByPolylines::addLineFromGeometry
void addLineFromGeometry(const QgsGeometry &geom)
Adds a input forcing line geometry in rendering coordinates.
Definition: qgsmeshforcebypolylines.cpp:1153
QgsTopologicalMesh::firstFaceLinked
int firstFaceLinked(int vertexIndex) const
Returns the index of the first face linked, returns -1 if it is a free vertex or out of range index.
Definition: qgstopologicalmesh.cpp:597