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