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