QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
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
28static int vertexPositionInFace( int vertexIndex, const QgsMeshFace &face )
29{
30 return face.indexOf( vertexIndex );
31}
32
33static 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
41bool 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;
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
107static 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
147static 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
190void 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
212QgsTopologicalMesh::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
317void QgsMeshEditForceByLine::finish()
318{
319 mIsFinished = true;
320}
321
322void 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
342void 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
350bool 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
775bool 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
858bool 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
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
1046bool 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 );
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
1124QgsTopologicalMesh::Changes QgsMeshEditForceByPolylines::apply( QgsMeshEditor *meshEditor )
1125{
1126 if ( mPolylines.isEmpty() )
1127 {
1128 mIsFinished = true;
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
1213void 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
1230{
1231 mDefaultZValue = defaultZValue;
1232}
1233
1234void QgsMeshEditForceByLine::setInterpolateZValueOnMesh( bool interpolateZValueOnMesh )
1235{
1236 mInterpolateZValueOnMesh = interpolateZValueOnMesh;
1237}
1238
1239void 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:164
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:968
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.