QGIS API Documentation  3.23.0-Master (dd0cd13a00)
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 }
Curve polygon geometry type.
Abstract base class for curved geometry type.
Definition: qgscurve.h:36
int numGeometries() const SIP_HOLDGIL
Returns the number of geometries within the collection.
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
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.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:125
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
bool isMultipart() const SIP_HOLDGIL
Returns true if WKB of the geometry is of WKBMulti* type.
void clear()
Removes all data provided to the editing or created by the editing.
virtual bool isFinished() const
Returns whether the advanced edit is finished, if not, this edit has to be applied again with QgsMesh...
void setAddVertexOnIntersection(bool addVertex)
Sets whether vertices will be added when the lines will intersect internal edges of faces,...
void setInputLine(const QgsPoint &pt1, const QgsPoint &pt2, double tolerance, bool newVertexOnIntersection)
Sets the input forcing line in rendering coordinates.
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...
void setTolerance(double tolerance)
Sets the tolerance in redering coordinate system unit.
void setInterpolateZValueOnMesh(bool interpolateZValueOnMesh)
Sets whether the new created vertices will have their value interpolated from the existing mesh.
QString text() const override
Returns a short text string describing what this advanced edit does. Default implementation return a ...
void addLinesFromGeometries(const QList< QgsGeometry > geometries)
Adds a list of input forcing lines geometry in rendering coordinates.
bool isFinished() const override
Returns whether the advanced edit is finished, if not, this edit has to be applied again with QgsMesh...
void addLineFromGeometry(const QgsGeometry &geom)
Adds a input forcing line geometry in rendering coordinates.
Class that represents an error during mesh editing.
Definition: qgsmesheditor.h:43
Qgis::MeshEditingErrorType errorType
Definition: qgsmesheditor.h:52
Class that makes edit operation on a mesh.
Definition: qgsmesheditor.h:68
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...
QgsTriangularMesh * triangularMesh()
Returns a pointer to the triangular mesh.
QgsTopologicalMesh & topologicalMesh()
Returns a reference to the topological mesh.
Convenient class that turn around a vertex and provide information about faces and vertices.
bool isValid() const
Returns whether the vertex circulator is valid.
bool goBoundaryCounterClockwise() const
Sets the circulator on the boundary face turning counter clockwise, return false is there isn't bound...
int currentFaceIndex() const
Returns the current face index, -1 if the circulator has passed a boundary or circulator is invalid.
bool goBoundaryClockwise() const
Sets the circulator on the boundary face turning clockwise, return false is there isn't boundary face...
QgsMeshFace currentFace() const
Returns the current face, empty face if the circulator pass a boundary or circulator is invalid.
Multi curve geometry collection.
Definition: qgsmulticurve.h:30
Multi surface geometry collection.
A class to represent a 2D point.
Definition: qgspointxy.h:59
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
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:343
Q_GADGET double x
Definition: qgspoint.h:52
void setZ(double z) SIP_HOLDGIL
Sets the point's z-coordinate.
Definition: qgspoint.h:304
double z
Definition: qgspoint.h:54
double y
Definition: qgspoint.h:53
A rectangle specified with double values.
Definition: qgsrectangle.h:42
Class that contains topological differences between two states of a topological mesh,...
QVector< FaceNeighbors > mFacesNeighborhoodToRemove
QVector< QgsMeshVertex > addedVertices() const
Returns the added vertices with this changes.
QList< std::array< int, 4 > > mNeighborhoodChanges
QVector< QgsMeshFace > mFacesToAdd
QVector< FaceNeighbors > mFacesNeighborhoodToAdd
QList< std::array< int, 3 > > mVerticesToFaceChanges
QVector< QgsMeshVertex > mVerticesToAdd
QVector< QgsMeshFace > mFacesToRemove
Class that contains independent faces an topological information about this faces.
int vertexToFace(int vertexIndex) const
Returns a face linked to the vertices with index vertexIndex.
QVector< FaceNeighbors > facesNeighborhood() const
Returns the face neighborhood of the faces, indexing is local.
QVector< QgsMeshFace > meshFaces() const
Returns faces.
void applyChanges(const Changes &changes)
Applies the changes.
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.
QVector< int > neighborsOfFace(int faceIndex) const
Returns the indexes of neighbor faces of the face with index faceIndex.
QVector< int > FaceNeighbors
Changes addVertexInFace(int faceIndex, const QgsMeshVertex &vertex)
Adds a vertex in the face with index faceIndex.
QList< int > facesAroundVertex(int vertexIndex) const
Returns the indexes of faces that are around the vertex with index vertexIndex.
Changes insertVertexInFacesEdge(int faceIndex, int position, const QgsMeshVertex &vertex)
Inserts a vertex in the edge of face with index faceIndex at position .
QgsMesh * mesh() const
Returns a pointer to the wrapped mesh.
static TopologicalFaces createNewTopologicalFaces(const QVector< QgsMeshFace > &faces, bool uniqueSharedVertexAllowed, QgsMeshEditingError &error)
Creates new topological faces that are not yet included in the mesh.
Triangular/Derived Mesh is mesh with vertices in map coordinates.
const QVector< QgsMeshFace > & triangles() const
Returns triangles.
int nativeFaceIndexForPoint(const QgsPointXY &point) const
Finds index of native face at given point It uses spatial indexing.
const QVector< QgsMeshVertex > & vertices() const
Returns vertices in map coordinate system.
QgsMeshVertex triangularToNativeCoordinates(const QgsMeshVertex &vertex) const
Transforms the vertex from triangular mesh coordinates system to native coordinates system.
QList< int > nativeFaceIndexForRectangle(const QgsRectangle &rectangle) const
Finds indexes of native faces which bounding boxes intersect given bounding box It uses spatial index...
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.
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:938
QVector< QgsPoint > QgsPointSequence
QVector< int > QgsMeshFace
List of vertex indexes.
Mesh - vertices, edges and faces.
int vertexCount() const
Returns number of vertices.
QVector< QgsMeshVertex > vertices
QgsMeshFace face(int index) const
Returns a face at the index.
int faceCount() const
Returns number of faces.
QgsMeshVertex vertex(int index) const
Returns a vertex at the index.