QGIS API Documentation 3.99.0-Master (21b3aa880ba)
Loading...
Searching...
No Matches
qgstopologicalmesh.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgstopologicalmesh.cpp - QgsTopologicalMesh
3
4 ---------------------
5 begin : 18.6.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 ***************************************************************************/
16
17#include "qgstopologicalmesh.h"
18
19#include "poly2tri.h"
20#include "qgis.h"
21#include "qgscircle.h"
22#include "qgsgeometryutils.h"
23#include "qgsmesheditor.h"
24#include "qgsmessagelog.h"
25
26#include <QQueue>
27#include <QSet>
28
29/*static*/ int QgsTopologicalMesh::vertexPositionInFace( const QgsMesh &mesh, int vertexIndex, int faceIndex )
30{
31 if ( faceIndex < 0 || faceIndex >= mesh.faceCount() )
32 return -1;
33
34 return QgsTopologicalMesh::vertexPositionInFace( vertexIndex, mesh.face( faceIndex ) );
35}
36
37
38static double crossProduct( const QgsMeshVertex &vc, const QgsMeshVertex &v1, const QgsMeshVertex &v2 )
39{
40 double ux1 = v1.x() - vc.x();
41 double uy1 = v1.y() - vc.y();
42 double vx1 = v2.x() - vc.x();
43 double vy1 = v2.y() - vc.y();
44
45 return ux1 * vy1 - uy1 * vx1;
46}
47
48static double crossProduct( int centralVertex, int vertex1, int vertex2, const QgsMesh &mesh )
49{
50 QgsMeshVertex vc = mesh.vertices.at( centralVertex );
51 QgsMeshVertex v1 = mesh.vertices.at( vertex1 );
52 QgsMeshVertex v2 = mesh.vertices.at( vertex2 );
53
54 return crossProduct( vc, v1, v2 );
55}
56
57
59 : mFaces( topologicalMesh.mMesh->faces )
60 , mFacesNeighborhood( topologicalMesh.mFacesNeighborhood )
61 , mVertexIndex( vertexIndex )
62{
63 if ( vertexIndex >= 0 && vertexIndex < topologicalMesh.mMesh->vertexCount() )
64 {
65 mCurrentFace = topologicalMesh.mVertexToFace[vertexIndex];
66 mIsValid = QgsTopologicalMesh::vertexPositionInFace( *topologicalMesh.mesh(), vertexIndex, mCurrentFace ) != -1;
67 }
68 else
69 {
70 mIsValid = false;
71 }
72
73 if ( mIsValid )
74 mLastValidFace = mCurrentFace;
75}
76
77QgsMeshVertexCirculator::QgsMeshVertexCirculator( const QgsTopologicalMesh::TopologicalFaces &topologicalFaces, int faceIndex, int vertexIndex )
78 : mFaces( topologicalFaces.mFaces )
79 , mFacesNeighborhood( topologicalFaces.mFacesNeighborhood )
80 , mVertexIndex( vertexIndex )
81{
82 const QgsMeshFace &face = topologicalFaces.mFaces.at( faceIndex );
83 mIsValid = QgsTopologicalMesh::vertexPositionInFace( vertexIndex, face ) != -1;
84
85 mCurrentFace = faceIndex;
86 mLastValidFace = mCurrentFace;
87}
88
90 : mFaces( topologicalFaces.mFaces )
91 , mFacesNeighborhood( topologicalFaces.mFacesNeighborhood )
92 , mVertexIndex( vertexIndex )
93{
94 if ( topologicalFaces.mVerticesToFace.contains( vertexIndex ) )
95 mCurrentFace = topologicalFaces.mVerticesToFace.values( vertexIndex ).first();
96 mLastValidFace = mCurrentFace;
97 mIsValid = mCurrentFace != -1;
98}
99
101{
102 if ( mCurrentFace == -1 )
103 mCurrentFace = mLastValidFace;
104 else
105 {
106 int currentPos = positionInCurrentFace();
107 Q_ASSERT( currentPos != -1 );
108
109 const QgsMeshFace &currentFace = mFaces.at( mCurrentFace );
110 int faceSize = currentFace.size();
111 mLastValidFace = mCurrentFace;
112 mCurrentFace = mFacesNeighborhood.at( mCurrentFace ).at( ( currentPos + faceSize - 1 ) % currentFace.count() );
113 }
114
115 return mCurrentFace;
116}
117
119{
120 if ( mCurrentFace == -1 )
121 mCurrentFace = mLastValidFace;
122 else
123 {
124 int currentPos = positionInCurrentFace();
125 Q_ASSERT( currentPos != -1 );
126
127 const QgsMeshFace &currentFace = mFaces.at( mCurrentFace );
128 int faceSize = currentFace.size();
129 mLastValidFace = mCurrentFace;
130 mCurrentFace = mFacesNeighborhood.at( mCurrentFace ).at( ( currentPos ) % faceSize );
131 }
132
133 return mCurrentFace;
134}
135
137{
138 return mCurrentFace;
139}
140
142{
143 if ( mCurrentFace != -1 )
144 return mFaces.at( mCurrentFace );
145 else
146 return QgsMeshFace();
147}
148
150{
151 if ( !isValid() )
152 return false;
153
154 if ( mCurrentFace == -1 )
155 mCurrentFace = mLastValidFace;
156
157 int firstFace = mCurrentFace;
158
159 while ( turnClockwise() != -1 && currentFaceIndex() != firstFace ) {}
160 if ( mCurrentFace == firstFace )
161 return false; // a complete turn, so the vertex is not a boundary vertex, something went wrong
162
163 return ( turnCounterClockwise() != -1 );
164}
165
167{
168 if ( !isValid() )
169 return false;
170
171 if ( mCurrentFace == -1 )
172 mCurrentFace = mLastValidFace;
173
174 int firstFace = mCurrentFace;
175
176 while ( turnCounterClockwise() != -1 && currentFaceIndex() != firstFace ) {}
177 if ( mCurrentFace == firstFace )
178 return false; // a complete turn, so the vertex is not a boundary vertex, something went wrong
179
180 return ( turnClockwise() != -1 );
181}
182
184{
185 if ( mCurrentFace == -1 )
186 return -1;
187
188 const QgsMeshFace &face = currentFace();
189
190 if ( face.isEmpty() )
191 return -1;
192
193 int vertexPosition = QgsTopologicalMesh::vertexPositionInFace( mVertexIndex, currentFace() );
194
195 if ( vertexPosition == -1 )
196 return -1;
197
198 return face.at( ( vertexPosition + 1 ) % face.count() );
199}
200
202{
203 if ( mCurrentFace == -1 )
204 return -1;
205
206 const QgsMeshFace &face = currentFace();
207
208 if ( face.isEmpty() )
209 return -1;
210
211 int vertexPosition = QgsTopologicalMesh::vertexPositionInFace( mVertexIndex, currentFace() );
212
213 if ( vertexPosition == -1 )
214 return -1;
215
216 return face.at( ( vertexPosition - 1 + face.count() ) % face.count() );
217}
218
220{
221 return mIsValid;
222}
223
225{
226 QList<int> ret;
227 if ( !isValid() )
228 return ret;
229
230 if ( mCurrentFace != -1 )
231 ret.append( mCurrentFace );
232 while ( turnCounterClockwise() != ret.first() && currentFaceIndex() != -1 )
233 ret.append( currentFaceIndex() );
234
235
236 if ( currentFaceIndex() == -1 ) //we encounter a boundary, restart with other direction
237 {
238 ret.clear();
239 if ( turnClockwise() == -1 )
240 return ret;
241 ret.append( currentFaceIndex() );
242 while ( turnClockwise() != -1 )
243 {
244 ret.append( currentFaceIndex() );
245 }
246 }
247
248 return ret;
249}
250
252{
253 if ( mDegree != -1 )
254 return mDegree;
255
256 mDegree = 0;
257 if ( !mIsValid )
258 return mDegree;
259
260 // if we are on the boundary, we count one more to take account of the circulator will
261 // not cover the last vertex (the other vertex on boundary)
263 mDegree = 2;
264 else
265 mDegree = 1;
266
267 int firstFace = currentFaceIndex();
268
269 while ( turnClockwise() != firstFace && currentFaceIndex() != -1 )
270 ++mDegree;
271
272 return mDegree;
273}
274
275
276int QgsMeshVertexCirculator::positionInCurrentFace() const
277{
278 if ( mCurrentFace < 0 || mCurrentFace >= mFaces.count() )
279 return -1;
280
281 return QgsTopologicalMesh::vertexPositionInFace( mVertexIndex, mFaces.at( mCurrentFace ) );
282}
283
285{
286 Changes changes;
287 changes.mFacesToAdd = topologicalFaces.mFaces;
288 changes.mAddedFacesFirstIndex = mMesh->faceCount();
289
290 changes.mFacesNeighborhoodToAdd.resize( changes.mFacesToAdd.count() );
291 for ( int i = 0; i < changes.mFacesToAdd.count(); ++i )
292 changes.mFacesNeighborhoodToAdd[i] = QVector<int>( changes.mFacesToAdd.at( i ).count(), -1 );
293
294 for ( int boundary : topologicalFaces.mBoundaries )
295 {
296 //if the boundary is a free vertex in the destination mesh, no need to check
297 if ( mVertexToFace.at( boundary ) == -1 )
298 continue;
299
300 const QList<int> &linkedFaces = topologicalFaces.mVerticesToFace.values( boundary );
301 for ( int linkedFace : linkedFaces )
302 {
303 QgsMeshVertexCirculator newFacesCirculator( topologicalFaces, linkedFace, boundary );
304 //search for face boundary on clockwise side of new faces
305 newFacesCirculator.goBoundaryClockwise();
306 int oppositeVertexForNewFace = newFacesCirculator.oppositeVertexClockwise();
307 if ( mVertexToFace.at( oppositeVertexForNewFace ) == -1 )
308 continue;
309
310 QgsMeshVertexCirculator meshCirculator = vertexCirculator( boundary );
311 meshCirculator.goBoundaryCounterClockwise();
312 int oppositeVertexForMeshFace = meshCirculator.oppositeVertexCounterClockwise();
313
314 const QgsMeshFace &newFaceBoundary = newFacesCirculator.currentFace();
315 int boundaryPositionInNewFace = vertexPositionInFace( boundary, newFaceBoundary );
316
317 if ( oppositeVertexForMeshFace != oppositeVertexForNewFace )
318 {
319 changes.mFacesNeighborhoodToAdd[newFacesCirculator.currentFaceIndex()][boundaryPositionInNewFace] = -1 ;
320 }
321 else
322 {
323 const QgsMeshFace &meshFaceBoundary = meshCirculator.currentFace();
324 int boundaryPositionInMeshFace = vertexPositionInFace( meshCirculator.oppositeVertexCounterClockwise(), meshFaceBoundary );
325
326 changes.mNeighborhoodChanges.append( std::array<int, 4>(
327 {
328 meshCirculator.currentFaceIndex(),
329 boundaryPositionInMeshFace,
330 -1,
331 changes.addedFaceIndexInMesh( newFacesCirculator.currentFaceIndex() )
332 } ) );
333
334 changes.mFacesNeighborhoodToAdd[newFacesCirculator.currentFaceIndex()][boundaryPositionInNewFace] = meshCirculator.currentFaceIndex();
335 }
336 }
337 }
338
339 for ( int f = 0; f < changes.mFacesToAdd.count(); ++f )
340 for ( int n = 0; n < changes.mFacesToAdd.at( f ).count(); ++n )
341 if ( changes.mFacesNeighborhoodToAdd.at( f ).at( n ) == -1 )
342 changes.mFacesNeighborhoodToAdd[f][n] = changes.addedFaceIndexInMesh( topologicalFaces.mFacesNeighborhood.at( f ).at( n ) );
343
344 const QList<int> &verticesToFaceToChange = topologicalFaces.mVerticesToFace.keys();
345 for ( const int vtc : verticesToFaceToChange )
346 if ( mVertexToFace.at( vtc ) == -1 )
347 changes.mVerticesToFaceChanges.append( {vtc,
348 mVertexToFace.at( vtc ),
349 changes.addedFaceIndexInMesh( topologicalFaces.mVerticesToFace.values( vtc ).first() )
350 } );
351
352 applyChanges( changes );
353
354 return changes;
355}
356
358{
359 int initialVerticesCount = mMesh->vertices.count();
360 if ( !changes.mVerticesToAdd.empty() )
361 {
362 int newSize = mMesh->vertices.count() + changes.mVerticesToAdd.count();
363 mMesh->vertices.resize( newSize );
364 mVertexToFace.resize( newSize );
365 }
366
367 if ( !changes.mFacesToAdd.empty() )
368 {
369 int newSize = mMesh->faceCount() + changes.mFacesToAdd.count();
370 mMesh->faces.resize( newSize );
371 mFacesNeighborhood.resize( newSize );
372 }
373
374 for ( int i = 0; i < changes.mFacesToRemove.count(); ++i )
375 {
376 mMesh->faces[changes.removedFaceIndexInMesh( i )] = QgsMeshFace();
377 mFacesNeighborhood[changes.removedFaceIndexInMesh( i )] = FaceNeighbors();//changes.facesNeighborhoodToRemove[i];
378 }
379
380 for ( int i = 0; i < changes.mVerticesToRemoveIndexes.count(); ++i )
381 {
382 int vertexIndex = changes.mVerticesToRemoveIndexes.at( i );
383 if ( mVertexToFace.at( vertexIndex ) == -1 )
384 dereferenceAsFreeVertex( vertexIndex );
385 mMesh->vertices[vertexIndex] = QgsMeshVertex();
386 mVertexToFace[vertexIndex] = -1;
387 }
388
389 for ( int i = 0; i < changes.mVerticesToAdd.count(); ++i )
390 {
391 mMesh->vertices[initialVerticesCount + i] = changes.mVerticesToAdd.at( i );
392 mVertexToFace[initialVerticesCount + i] = changes.mVertexToFaceToAdd.at( i );
393 if ( changes.mVertexToFaceToAdd.at( i ) == -1 )
394 referenceAsFreeVertex( initialVerticesCount + i );
395 }
396
397 for ( int i = 0; i < changes.mFacesToAdd.count(); ++i )
398 {
399 mMesh->faces[changes.addedFaceIndexInMesh( i )] = changes.mFacesToAdd.at( i );
400 mFacesNeighborhood[changes.addedFaceIndexInMesh( i )] = changes.mFacesNeighborhoodToAdd.at( i );
401 }
402
403 for ( const std::array<int, 4> neighborChange : std::as_const( changes.mNeighborhoodChanges ) )
404 {
405 const int faceIndex = neighborChange.at( 0 );
406 const int positionInFace = neighborChange.at( 1 );
407 const int valueToApply = neighborChange.at( 3 );
408 mFacesNeighborhood[faceIndex][positionInFace] = valueToApply;
409 }
410
411 for ( const std::array<int, 3> vertexToFaceChange : std::as_const( changes.mVerticesToFaceChanges ) )
412 {
413 int vertexIndex = vertexToFaceChange.at( 0 );
414 mVertexToFace[vertexToFaceChange.at( 0 )] = vertexToFaceChange.at( 2 );
415
416 if ( vertexToFaceChange.at( 2 ) == -1 &&
417 vertexToFaceChange.at( 1 ) != -1 &&
418 !mMesh->vertices.at( vertexIndex ).isEmpty() )
419 referenceAsFreeVertex( vertexIndex );
420
421 if ( vertexToFaceChange.at( 1 ) == -1 && vertexToFaceChange.at( 2 ) != -1 )
422 dereferenceAsFreeVertex( vertexIndex );
423 }
424
425 for ( int i = 0; i < changes.mChangeCoordinateVerticesIndexes.count(); ++i )
426 {
427 int vertexIndex = changes.mChangeCoordinateVerticesIndexes.at( i );
428 if ( !changes.mNewZValues.isEmpty() )
429 mMesh->vertices[vertexIndex].setZ( changes.mNewZValues.at( i ) );
430 if ( !changes.mNewXYValues.isEmpty() )
431 {
432 const QgsPointXY &pt = changes.mNewXYValues.at( i );
433 mMesh->vertices[vertexIndex].setX( pt.x() );
434 mMesh->vertices[vertexIndex].setY( pt.y() );
435 }
436 }
437}
438
440{
441 for ( const std::array<int, 4> neighborChange : std::as_const( changes.mNeighborhoodChanges ) )
442 {
443 const int faceIndex = neighborChange.at( 0 );
444 const int positionInFace = neighborChange.at( 1 );
445 const int valueToApply = neighborChange.at( 2 );
446 mFacesNeighborhood[faceIndex][positionInFace] = valueToApply;
447 }
448
449 for ( int i = 0; i < changes.mFacesToRemove.count(); ++i )
450 {
451 mMesh->faces[changes.removedFaceIndexInMesh( i )] = changes.mFacesToRemove.at( i );
452 mFacesNeighborhood[changes.removedFaceIndexInMesh( i )] = changes.mFacesNeighborhoodToRemove.at( i );
453 }
454
455 for ( int i = 0; i < changes.mVerticesToRemoveIndexes.count(); ++i )
456 {
457 int vertexIndex = changes.mVerticesToRemoveIndexes.at( i );
458 mMesh->vertices[vertexIndex] = changes.mRemovedVertices.at( i );
459 mVertexToFace[vertexIndex] = changes.mVerticesToFaceRemoved.at( i );
460 if ( mVertexToFace.at( vertexIndex ) == -1 )
461 referenceAsFreeVertex( vertexIndex );
462 }
463
464 int verticesToFaceChangesCount = changes.mVerticesToFaceChanges.count();
465 for ( int i = 0; i < verticesToFaceChangesCount; ++i )
466 {
467 const std::array<int, 3> vertexToFaceChange = changes.mVerticesToFaceChanges.at( verticesToFaceChangesCount - i - 1 );
468 int vertexIndex = vertexToFaceChange.at( 0 );
469 mVertexToFace[vertexIndex] = vertexToFaceChange.at( 1 );
470
471 if ( vertexToFaceChange.at( 2 ) == -1 && vertexToFaceChange.at( 1 ) != -1 )
472 dereferenceAsFreeVertex( vertexIndex );
473
474 if ( vertexToFaceChange.at( 1 ) == -1 &&
475 vertexToFaceChange.at( 2 ) != -1 &&
476 !mMesh->vertex( vertexIndex ).isEmpty() )
477 referenceAsFreeVertex( vertexIndex );
478 }
479
480 if ( !changes.mFacesToAdd.empty() )
481 {
482 int newSize = mMesh->faceCount() - changes.mFacesToAdd.count();
483 mMesh->faces.resize( newSize );
484 mFacesNeighborhood.resize( newSize );
485 }
486
487 if ( !changes.mVerticesToAdd.isEmpty() )
488 {
489 int newSize = mMesh->vertexCount() - changes.mVerticesToAdd.count();
490
491 for ( int i = newSize; i < mMesh->vertexCount(); ++i )
492 if ( mVertexToFace.at( i ) == -1 )
493 dereferenceAsFreeVertex( i );
494
495 mMesh->vertices.resize( newSize );
496 mVertexToFace.resize( newSize );
497 }
498
499 for ( int i = 0; i < changes.mChangeCoordinateVerticesIndexes.count(); ++i )
500 {
501 int vertexIndex = changes.mChangeCoordinateVerticesIndexes.at( i );
502 if ( !changes.mOldZValues.isEmpty() )
503 mMesh->vertices[vertexIndex].setZ( changes.mOldZValues.at( i ) );
504 if ( !changes.mOldXYValues.isEmpty() )
505 {
506 const QgsPointXY &pt = changes.mOldXYValues.at( i );
507 mMesh->vertices[vertexIndex].setX( pt.x() );
508 mMesh->vertices[vertexIndex].setY( pt.y() );
509 }
510 }
511}
512
514{
515 return QgsMeshVertexCirculator( *this, vertexIndex );
516}
517
518QSet<int> QgsTopologicalMesh::concernedFacesBy( const QList<int> &faceIndexes ) const
519{
520 QSet<int> faces;
521 for ( const int faceIndex : faceIndexes )
522 {
523 const QgsMeshFace &face = mMesh->face( faceIndex );
524 for ( int i = 0; i < face.count(); ++i )
525 {
526 const QList<int> around = facesAroundVertex( face.at( i ) );
527 faces.unite( QSet< int >( around.begin(), around.end() ) );
528 }
529 }
530 return faces;
531}
532
533void QgsTopologicalMesh::dereferenceAsFreeVertex( int vertexIndex )
534{
535 mFreeVertices.remove( vertexIndex );
536}
537
538void QgsTopologicalMesh::referenceAsFreeVertex( int vertexIndex )
539{
540 // QSet used to retrieve free vertex without going through all the vertices container.
541 // But maybe later we could use more sophisticated reference (spatial index?), to retrieve free vertex in an extent
542 mFreeVertices.insert( vertexIndex );
543}
544
546{
547 for ( int faceIndex = 0 ; faceIndex < mMesh->faces.count( ); ++faceIndex )
548 {
549 const QgsMeshFace &face = mMesh->faces.at( faceIndex );
550 const FaceNeighbors &neighborhood = mFacesNeighborhood.at( faceIndex );
551 if ( face.count() != neighborhood.count() )
553 for ( int i = 0; i < face.count(); ++i )
554 {
555 int vertexIndex = face.at( i );
556 // check if each vertices is linked to a face (not free vertex)
557 if ( mVertexToFace.at( vertexIndex ) == -1 )
559
560 int neighborIndex = neighborhood.at( i );
561 if ( neighborIndex != -1 )
562 {
563 const QgsMeshFace &neighborFace = mMesh->faces.at( neighborIndex );
564 if ( neighborFace.isEmpty() )
566 int neighborSize = neighborFace.size();
567 const FaceNeighbors &neighborhoodOfNeighbor = mFacesNeighborhood.at( neighborIndex );
568 int posInNeighbor = QgsTopologicalMesh::vertexPositionInFace( *mMesh, vertexIndex, neighborIndex );
569 if ( neighborhoodOfNeighbor.isEmpty() || neighborhoodOfNeighbor.at( ( posInNeighbor + neighborSize - 1 ) % neighborSize ) != faceIndex )
571 }
572 }
573 }
574
575 for ( int vertexIndex = 0; vertexIndex < mMesh->vertexCount(); ++vertexIndex )
576 {
577 if ( mVertexToFace.at( vertexIndex ) != -1 )
578 {
579 if ( !mMesh->face( mVertexToFace.at( vertexIndex ) ).contains( vertexIndex ) )
581
582 if ( facesAroundVertex( vertexIndex ).count() == 0 )
584 }
585 }
586
587 return QgsMeshEditingError();
588}
589
591{
592 QgsMesh temp = mesh;
594 createTopologicalMesh( &temp, maxVerticesPerFace, error );
595 return error;
596}
597
599{
600 return mMesh;
601}
602
603int QgsTopologicalMesh::firstFaceLinked( int vertexIndex ) const
604{
605 if ( vertexIndex < 0 || vertexIndex >= mMesh->vertexCount() )
606 return -1;
607 return mVertexToFace.at( vertexIndex );
608}
609
610bool QgsTopologicalMesh::isVertexOnBoundary( int vertexIndex ) const
611{
612 QgsMeshVertexCirculator circulator = vertexCirculator( vertexIndex );
613
614 if ( circulator.isValid() )
615 return circulator.goBoundaryClockwise();
616
617 return false;
618}
619
620bool QgsTopologicalMesh::isVertexFree( int vertexIndex ) const
621{
622 if ( vertexIndex < 0 || vertexIndex >= mMesh->vertexCount() )
623 return false;
624
625 if ( mMesh->vertices.at( vertexIndex ).isEmpty() )
626 return false;
627
628 return mVertexToFace.at( vertexIndex ) == -1;
629}
630
632{
633 return QList<int>( mFreeVertices.begin(), mFreeVertices.end() );
634}
635
636QgsMeshEditingError QgsTopologicalMesh::checkTopologyOfVerticesAsFace( const QVector<QgsMeshVertex> &vertices, bool &clockwise )
637{
638 int size = vertices.size();
639 int direction = 0;
640 for ( int i = 0; i < size; ++i )
641 {
642 int iv0 = i;
643 int iv1 = ( i + 1 ) % size;
644 int iv2 = ( i + 2 ) % size;
645
646 const QgsMeshVertex &v0 = vertices.at( iv0 ) ;
647 const QgsMeshVertex &v1 = vertices.at( iv1 ) ;
648 const QgsMeshVertex &v2 = vertices.at( iv2 ) ;
649
650 if ( v0.isEmpty() )
652
653 if ( v1.isEmpty() )
655
656 if ( v2.isEmpty() )
658
659 double crossProd = crossProduct( v1, v0, v2 ); //if cross product>0, we have two edges clockwise
660 if ( direction != 0 && crossProd * direction < 0 ) // We have a convex face or a (partially) flat face
661 {
662 clockwise = direction > 0;
664 }
665 else if ( crossProd == 0 )
666 {
667 clockwise = direction > 0;
669 }
670 else if ( direction == 0 )
671 direction = crossProd / std::fabs( crossProd );
672 }
673
674 clockwise = direction > 0;
675
677}
678
680{
681 // First check the topology of the face, then put it counter clockwise if needed
682 // If the indexes are not well ordered (edges intersect), invalid face
683 int faceSize = face.count();
684 if ( faceSize < 3 )
686
687 QVector<QgsMeshVertex> vertices( face.size() );
688
689 for ( int i = 0; i < faceSize; ++i )
690 {
691 int iv = face[i];
692 if ( iv < 0 || iv >= mesh->vertexCount() )
694
695 vertices[i] = mesh->vertices.at( face[i] );
696 }
697
698 bool clockwise = false;
700 if ( error != QgsMeshEditingError() )
701 return error;
702
703 if ( clockwise )// clockwise --> reverse the order of the index;
704 {
705 for ( int i = 0; i < faceSize / 2; ++i )
706 {
707 int temp = face[i];
708 face[i] = face.at( faceSize - i - 1 );
709 face[faceSize - i - 1] = temp;
710 }
711 }
712
714}
715
717{
718 QVector<int> oldToNewIndex( mMesh->vertices.count(), -1 );
719 int verticesTotalCount = mMesh->vertexCount();
720 int oldIndex = 0;
721 int newIndex = 0;
722 while ( oldIndex < verticesTotalCount )
723 {
724 if ( mMesh->vertex( oldIndex ).isEmpty() )
725 {
726 oldIndex++;
727 }
728 else
729 {
730 oldToNewIndex[oldIndex] = newIndex;
731 if ( oldIndex != newIndex )
732 mMesh->vertices[newIndex] = mMesh->vertices[oldIndex];
733 oldToNewIndex[oldIndex] = newIndex;
734 oldIndex++;
735 newIndex++;
736 }
737 }
738
739 mMesh->vertices.resize( newIndex );
740
741 oldIndex = 0;
742 newIndex = 0;
743 int facesTotalCount = mMesh->faceCount();
744 while ( oldIndex < facesTotalCount )
745 {
746 if ( mMesh->face( oldIndex ).isEmpty() )
747 oldIndex++;
748 else
749 {
750 if ( oldIndex != newIndex )
751 mMesh->faces[newIndex] = mMesh->faces[oldIndex];
752 QgsMeshFace &face = mMesh->faces[newIndex];
753 for ( int i = 0; i < face.count(); ++i )
754 face[i] = oldToNewIndex[face.at( i )];
755 newIndex++;
756 oldIndex++;
757 }
758 }
759
760 mMesh->faces.resize( newIndex );
761
762 mVertexToFace.clear();
763 mFacesNeighborhood.clear();
764}
765
767{
768 QVector<int> oldToNewVerticesIndexes;
769 if ( !renumberVertices( oldToNewVerticesIndexes ) )
770 return false;
771
772
773 QVector<int> oldToNewFacesIndexes;
774
775 if ( !renumberFaces( oldToNewFacesIndexes ) )
776 return false;
777
778 // If we are here, we can apply the renumbering
779
780 QVector<QgsMeshVertex> tempVertices( mMesh->vertices.count() );
781 for ( int i = 0; i < oldToNewVerticesIndexes.count(); ++i )
782 {
783 tempVertices[oldToNewVerticesIndexes.at( i )] = mMesh->vertex( i );
784 }
785 mMesh->vertices = tempVertices;
786
787 QVector<QgsMeshFace> tempFaces( mMesh->faces.count() );
788 for ( int i = 0; i < oldToNewFacesIndexes.count(); ++i )
789 {
790 tempFaces[oldToNewFacesIndexes.at( i )] = mMesh->face( i );
791
792 QgsMeshFace &face = tempFaces[oldToNewFacesIndexes.at( i )];
793
794 for ( int fi = 0; fi < face.count(); ++fi )
795 {
796 face[fi] = oldToNewVerticesIndexes.at( face.at( fi ) );
797 }
798 }
799
800 mMesh->faces = tempFaces;
801
802 return true;
803
804}
805
806bool QgsTopologicalMesh::renumberVertices( QVector<int> &oldToNewIndex ) const
807{
808 std::vector<QgsMeshVertexCirculator> circulators;
809 circulators.reserve( mMesh->vertices.count() );
810 int minDegree = std::numeric_limits<int>::max();
811 int minDegreeVertex = -1;
812
813 QQueue<int> queue;
814 QSet<int> nonThreadedVertex;
815 oldToNewIndex = QVector<int> ( mMesh->vertexCount(), -1 );
816 for ( int i = 0; i < mMesh->vertexCount(); ++i )
817 {
818 circulators.emplace_back( *this, i );
819 const QgsMeshVertexCirculator &circulator = circulators.back();
820 if ( circulators.back().degree() < minDegree )
821 {
822 minDegreeVertex = circulators.size() - 1;
823 minDegree = circulator.degree();
824 }
825 nonThreadedVertex.insert( i );
826 }
827
828 auto sortedNeighbor = [circulators]( QList<int> &neighbors, int index )
829 {
830 const QgsMeshVertexCirculator &circ = circulators.at( index );
831
832 if ( !circ.isValid() )
833 return;
834
836 neighbors.append( circ.oppositeVertexCounterClockwise() );
837
838 int firstFace = circ.currentFaceIndex();
839 do
840 {
841 int neighborIndex = circ.oppositeVertexClockwise();
842 int degree = circulators.at( neighborIndex ).degree();
843 QList<int>::Iterator it = neighbors.begin();
844 while ( it != neighbors.end() )
845 {
846 if ( degree <= circulators.at( *it ).degree() )
847 {
848 neighbors.insert( it, neighborIndex );
849 break;
850 }
851 it++;
852 }
853 if ( it == neighbors.end() )
854 neighbors.append( neighborIndex );
855 }
856 while ( circ.turnClockwise() != firstFace && circ.currentFaceIndex() != -1 );
857 };
858
859 int newIndex = 0;
860 int currentVertex = minDegreeVertex;
861 nonThreadedVertex.remove( minDegreeVertex );
862
863 while ( newIndex < mMesh->vertexCount() )
864 {
865 if ( oldToNewIndex[currentVertex] == -1 )
866 oldToNewIndex[currentVertex] = newIndex++;
867
868 if ( circulators.at( currentVertex ).isValid() )
869 {
870 QList<int> neighbors;
871 sortedNeighbor( neighbors, currentVertex );
872
873 for ( const int i : std::as_const( neighbors ) )
874 if ( oldToNewIndex.at( i ) == -1 && nonThreadedVertex.contains( i ) )
875 {
876 queue.enqueue( i );
877 nonThreadedVertex.remove( i );
878 }
879 }
880
881 if ( queue.isEmpty() )
882 {
883 if ( nonThreadedVertex.isEmpty() && newIndex < mMesh->vertexCount() )
884 return false;
885
886 const QList<int> remainingVertex( nonThreadedVertex.constBegin(), nonThreadedVertex.constEnd() );
887 int minRemainingDegree = std::numeric_limits<int>::max();
888 int minRemainingVertex = -1;
889 for ( const int i : remainingVertex )
890 {
891 int degree = circulators.at( i ).degree();
892 if ( degree < minRemainingDegree )
893 {
894 minRemainingDegree = degree;
895 minRemainingVertex = i;
896 }
897 }
898 currentVertex = minRemainingVertex;
899 nonThreadedVertex.remove( currentVertex );
900 }
901 else
902 {
903 currentVertex = queue.dequeue();
904 }
905 }
906
907 return true;
908}
909
910bool QgsTopologicalMesh::renumberFaces( QVector<int> &oldToNewIndex ) const
911{
912 QQueue<int> queue;
913 QSet<int> nonThreadedFaces;
914
915 oldToNewIndex = QVector<int>( mMesh->faceCount(), -1 );
916
917 QVector<int> faceDegrees( mMesh->faceCount(), 0 );
918
919 int minDegree = std::numeric_limits<int>::max();
920 int minDegreeFace = -1;
921 for ( int faceIndex = 0; faceIndex < mMesh->faceCount(); ++faceIndex )
922 {
923 const FaceNeighbors &neighbors = mFacesNeighborhood.at( faceIndex );
924
925 int degree = 0;
926 for ( int n = 0; n < neighbors.size(); ++n )
927 {
928 if ( neighbors.at( n ) != -1 )
929 degree++;
930 }
931
932 if ( degree < minDegree )
933 {
934 minDegree = degree;
935 minDegreeFace = faceIndex;
936 }
937
938 faceDegrees[faceIndex] = degree;
939 nonThreadedFaces.insert( faceIndex );
940 }
941
942 int newIndex = 0;
943 int currentFace = minDegreeFace;
944 nonThreadedFaces.remove( minDegreeFace );
945
946 auto sortedNeighbor = [this, faceDegrees]( QList<int> &neighbors, int index )
947 {
948 const FaceNeighbors &neighborhood = mFacesNeighborhood.at( index );
949
950 for ( int i = 0; i < neighborhood.count(); ++i )
951 {
952 int neighborIndex = neighborhood.at( i );
953 if ( neighborIndex == -1 )
954 continue;
955
956 int degree = faceDegrees.at( neighborIndex );
957 if ( neighbors.isEmpty() )
958 neighbors.append( neighborIndex );
959 else
960 {
961 QList<int>::Iterator it = neighbors.begin();
962 while ( it != neighbors.end() )
963 {
964 if ( degree <= faceDegrees.at( *it ) )
965 {
966 neighbors.insert( it, neighborIndex );
967 break;
968 }
969 it++;
970 }
971 if ( it == neighbors.end() )
972 neighbors.append( neighborIndex );
973 }
974 }
975 };
976
977 while ( newIndex < mMesh->faceCount() )
978 {
979 if ( oldToNewIndex[currentFace] == -1 )
980 oldToNewIndex[currentFace] = newIndex++;
981
982 QList<int> neighbors;
983 sortedNeighbor( neighbors, currentFace );
984
985 for ( const int i : std::as_const( neighbors ) )
986 if ( oldToNewIndex.at( i ) == -1 && nonThreadedFaces.contains( i ) )
987 {
988 queue.enqueue( i );
989 nonThreadedFaces.remove( i );
990 }
991
992 if ( queue.isEmpty() )
993 {
994 if ( nonThreadedFaces.isEmpty() && newIndex < mMesh->faceCount() )
995 return false;
996
997 const QList<int> remainingFace( nonThreadedFaces.constBegin(), nonThreadedFaces.constEnd() );
998 int minRemainingDegree = std::numeric_limits<int>::max();
999 int minRemainingFace = -1;
1000 for ( const int i : remainingFace )
1001 {
1002 int degree = faceDegrees.at( i );
1003 if ( degree < minRemainingDegree )
1004 {
1005 minRemainingDegree = degree;
1006 minRemainingFace = i;
1007 }
1008 }
1009 currentFace = minRemainingFace;
1010 nonThreadedFaces.remove( currentFace );
1011 }
1012 else
1013 {
1014 currentFace = queue.dequeue();
1015 }
1016
1017 }
1018
1019 return true;
1020}
1021
1023{
1024 return mFacesToAdd;
1025}
1026
1028{
1029 return mFacesToRemove;
1030}
1031
1036
1038{
1039 return mVerticesToAdd;
1040}
1041
1046
1048{
1049 return mNewZValues;
1050}
1051
1053{
1054 return mNewXYValues;
1055}
1056
1058{
1059 return mOldXYValues;
1060}
1061
1066
1068{
1069 return ( mFaceIndexesToRemove.isEmpty() &&
1070 mFacesToAdd.isEmpty() &&
1071 mFacesNeighborhoodToAdd.isEmpty() &&
1072 mFacesToRemove.isEmpty() &&
1073 mFacesNeighborhoodToRemove.isEmpty() &&
1074 mNeighborhoodChanges.isEmpty() &&
1075 mVerticesToAdd.isEmpty() &&
1076 mVertexToFaceToAdd.isEmpty() &&
1077 mVerticesToRemoveIndexes.isEmpty() &&
1078 mRemovedVertices.isEmpty() &&
1079 mVerticesToFaceRemoved.isEmpty() &&
1080 mVerticesToFaceChanges.isEmpty() &&
1082 mNewZValues.isEmpty() &&
1083 mOldZValues.isEmpty() &&
1084 mNewXYValues.isEmpty() &&
1085 mOldXYValues.isEmpty() &&
1087}
1088
1093
1094int QgsTopologicalMesh::Changes::addedFaceIndexInMesh( int internalIndex ) const
1095{
1096 if ( internalIndex == -1 )
1097 return -1;
1098
1099 return internalIndex + mAddedFacesFirstIndex;
1100}
1101
1102int QgsTopologicalMesh::Changes::removedFaceIndexInMesh( int internalIndex ) const
1103{
1104 if ( internalIndex == -1 )
1105 return -1;
1106
1107 return mFaceIndexesToRemove.at( internalIndex );
1108}
1109
1111{
1113 mFaceIndexesToRemove.clear();
1114 mFacesToAdd.clear();
1116 mFacesToRemove.clear();
1118 mNeighborhoodChanges.clear();
1119
1120 mVerticesToAdd.clear();
1121 mVertexToFaceToAdd.clear();
1123 mRemovedVertices.clear();
1124 mVerticesToFaceRemoved.clear();
1125 mVerticesToFaceChanges.clear();
1126
1128 mNewZValues.clear();
1129 mOldZValues.clear();
1130 mNewXYValues.clear();
1131 mOldXYValues.clear();
1133}
1134
1136{
1137 Changes changes;
1138 changes.mVerticesToAdd.append( vertex );
1139 changes.mVertexToFaceToAdd.append( -1 );
1140
1141 mMesh->vertices.append( vertex );
1142 mVertexToFace.append( -1 );
1143 referenceAsFreeVertex( mMesh->vertices.count() - 1 );
1144
1145 return changes;
1146}
1147
1148// Returns the orientation of the polygon formed by mesh vertices, <0 counter clockwise; >0 clockwise
1149static double vertexPolygonOrientation( const QgsMesh &mesh, const QList<int> &vertexIndexes )
1150{
1151 if ( vertexIndexes.count() < 3 )
1152 return 0.0;
1153
1154 int hullDomainVertexPos = -1;
1155 double xMin = std::numeric_limits<double>::max();
1156 double yMin = std::numeric_limits<double>::max();
1157 for ( int i = 0; i < vertexIndexes.count(); ++i )
1158 {
1159 const QgsMeshVertex &vertex = mesh.vertices.at( vertexIndexes.at( i ) );
1160 if ( xMin >= vertex.x() && yMin > vertex.y() )
1161 {
1162 hullDomainVertexPos = i;
1163 xMin = vertex.x();
1164 yMin = vertex.y();
1165 }
1166 }
1167
1168 if ( hullDomainVertexPos >= 0 )
1169 {
1170 int iv1 = vertexIndexes.at( ( hullDomainVertexPos - 1 + vertexIndexes.count() ) % vertexIndexes.count() );
1171 int iv2 = vertexIndexes.at( ( hullDomainVertexPos + 1 ) % vertexIndexes.count() );
1172 int ivc = vertexIndexes.at( ( hullDomainVertexPos ) );
1173 double cp = crossProduct( ivc, iv1, iv2, mesh );
1174 return cp;
1175 }
1176
1177 return 0.0;
1178}
1179
1181{
1182 if ( vertexIndex >= mVertexToFace.count() )
1183 return Changes();
1184
1185 if ( mVertexToFace.at( vertexIndex ) == -1 ) //it is a free vertex
1186 {
1187 Changes changes;
1188 changes.mRemovedVertices.append( mMesh->vertices.at( vertexIndex ) );
1189 changes.mVerticesToRemoveIndexes.append( vertexIndex );
1190 changes.mVerticesToFaceRemoved.append( -1 );
1191 dereferenceAsFreeVertex( vertexIndex );
1192 mMesh->vertices[vertexIndex] = QgsMeshVertex();
1193 return changes;
1194 }
1195
1196 //search concerned faces
1197 QgsMeshVertexCirculator circulator = vertexCirculator( vertexIndex );
1198 circulator.goBoundaryClockwise();
1199 QList<int> boundariesVertexIndex;
1200 QList<int> associateFaceToBoundaries;
1201 QList<int> removedFacesIndexes;
1202 QSet<int> boundaryInGlobalMesh;
1203
1204 do
1205 {
1206 removedFacesIndexes.append( circulator.currentFaceIndex() );
1207 boundariesVertexIndex.append( circulator.oppositeVertexClockwise() );
1208 Q_ASSERT( !mMesh->vertices.at( boundariesVertexIndex.last() ).isEmpty() );
1209 const QgsMeshFace &currentFace = circulator.currentFace();
1210 associateFaceToBoundaries.append( mFacesNeighborhood.at( circulator.currentFaceIndex() ).at(
1211 vertexPositionInFace( boundariesVertexIndex.last(), currentFace ) ) );
1212
1213 if ( currentFace.count() > 3 ) // quad or more, need other vertices
1214 {
1215 int posInface = vertexPositionInFace( vertexIndex, currentFace );
1216 for ( int i = 2; i < currentFace.count() - 1; ++i )
1217 {
1218 boundariesVertexIndex.append( currentFace.at( ( posInface + i ) % currentFace.count() ) );
1219 Q_ASSERT( !mMesh->vertices.at( boundariesVertexIndex.last() ).isEmpty() );
1220 associateFaceToBoundaries.append( mFacesNeighborhood.at( circulator.currentFaceIndex() ).at(
1221 vertexPositionInFace( boundariesVertexIndex.last(), currentFace ) ) );
1222 }
1223 }
1224 }
1225 while ( circulator.turnCounterClockwise() != -1 && circulator.currentFaceIndex() != removedFacesIndexes.first() );
1226
1227 bool boundaryFill = false;
1228 if ( circulator.currentFaceIndex() == -1 ) //we are on boundary of the mesh
1229 {
1230 boundaryFill = true;
1231 //we need to add last vertex/boundary faces that was not added because we are on mesh boundary
1232 circulator.goBoundaryCounterClockwise();
1233 int lastVertexIndex = circulator.oppositeVertexCounterClockwise();
1234 boundariesVertexIndex.append( lastVertexIndex );
1235
1236 // but we can be on the case where the last vertex share an edge with the first point,
1237 // in the case, the associate face on boundarie is not -1, but the face on the other side of the edge
1238 QgsMeshVertexCirculator boundaryCirculator = vertexCirculator( lastVertexIndex );
1239 boundaryCirculator.goBoundaryCounterClockwise();
1240 if ( boundaryCirculator.oppositeVertexCounterClockwise() == boundariesVertexIndex.first() )
1241 {
1242 associateFaceToBoundaries.append( boundaryCirculator.currentFaceIndex() );
1243 boundaryFill = false; //we are not a boundary fill anymore
1244 }
1245 else
1246 associateFaceToBoundaries.append( -1 );
1247
1248 for ( const int index : std::as_const( boundariesVertexIndex ) )
1249 {
1250 if ( isVertexOnBoundary( index ) )
1251 boundaryInGlobalMesh.insert( index );
1252 }
1253 }
1254
1255 int currentVertexToFace = mVertexToFace.at( vertexIndex );
1256 // here, we use the method removeFaces that effectivly removes and then constructs changes
1257 Changes changes = removeFaces( removedFacesIndexes );
1258
1259 QList<QList<int>> holes;
1260 QList<QList<int>> associateMeshFacesToHoles;
1261
1262 bool cancelOperation = false;
1263
1264 if ( boundaryFill )
1265 {
1266 // The hole is not a closed polygon, we need to close it, but the closing segment can intersect another segments/vertices.
1267 // In this case we consider as many polygons as necessary.
1268
1269 int startPos = 0;
1270 int finalPos = boundariesVertexIndex.count() - 1;
1271 QList<int> uncoveredVertex;
1272
1273 QList<int> partToCheck = boundariesVertexIndex.mid( startPos, finalPos - startPos + 1 );
1274 QList<int> associateFacePart = associateFaceToBoundaries.mid( startPos, finalPos - startPos + 1 );
1275 while ( startPos < finalPos && !partToCheck.isEmpty() )
1276 {
1277 // check if we intersect an edge between first and second
1278 int secondPos = partToCheck.count() - 1;
1279 const QgsPoint &closingSegmentExtremety1 = mMesh->vertex( partToCheck.at( 0 ) );
1280 const QgsPoint &closingSegmentExtremety2 = mMesh->vertex( partToCheck.last() );
1281 bool isEdgeIntersect = false;
1282 for ( int i = 1; i < secondPos - 1; ++i )
1283 {
1284 const QgsPoint &p1 = mMesh->vertex( partToCheck.at( i ) );
1285 const QgsPoint &p2 = mMesh->vertex( partToCheck.at( i + 1 ) );
1286 bool isLineIntersection;
1287 QgsPoint intersectPoint;
1288 isEdgeIntersect = QgsGeometryUtils::segmentIntersection( closingSegmentExtremety1, closingSegmentExtremety2, p1, p2, intersectPoint, isLineIntersection, 1e-8, true );
1289 if ( isEdgeIntersect )
1290 break;
1291 }
1292
1293 int index = partToCheck.at( 0 );
1294 if ( boundaryInGlobalMesh.contains( index ) && index != boundariesVertexIndex.at( 0 ) )
1295 {
1296 cancelOperation = true;
1297 break;
1298 }
1299
1300 // if uncovered vertex is a boundary vertex in the global mesh (except first that is always a boundary in the global mesh)
1301 // This operation will leads to a unique shared vertex that is not allowed, you have to cancel the operation
1302 if ( isEdgeIntersect || vertexPolygonOrientation( *mMesh, partToCheck ) >= 0 )
1303 {
1304 partToCheck.removeLast();
1305 associateFacePart.removeAt( associateFacePart.count() - 2 );
1306 if ( partToCheck.count() == 1 )
1307 {
1308 uncoveredVertex.append( index );
1309 startPos = startPos + 1;
1310 partToCheck = boundariesVertexIndex.mid( startPos, finalPos - startPos + 1 );
1311 associateFacePart = associateFaceToBoundaries.mid( startPos, finalPos - startPos + 1 );
1312 }
1313 }
1314 else
1315 {
1316 // store the well defined hole
1317 holes.append( partToCheck );
1318 associateMeshFacesToHoles.append( associateFacePart );
1319
1320 startPos = startPos + partToCheck.count() - 1;
1321 uncoveredVertex.append( partToCheck.at( 0 ) );
1322 partToCheck = boundariesVertexIndex.mid( startPos, finalPos - startPos + 1 );
1323 associateFacePart = associateFaceToBoundaries.mid( startPos, finalPos - startPos + 1 );
1324 }
1325 }
1326 }
1327 else
1328 {
1329 holes.append( boundariesVertexIndex );
1330 associateMeshFacesToHoles.append( associateFaceToBoundaries );
1331 }
1332
1333 if ( cancelOperation )
1334 {
1335 reverseChanges( changes );
1336 return Changes();
1337 }
1338
1339 Q_ASSERT( holes.count() == associateMeshFacesToHoles.count() );
1340
1341 changes.mRemovedVertices.append( mMesh->vertices.at( vertexIndex ) );
1342 changes.mVerticesToRemoveIndexes.append( vertexIndex );
1343 changes.mVerticesToFaceRemoved.append( currentVertexToFace );
1344 // these changes contain information that will lead to reference the removed vertex as free vertex when reverse/reapply
1345 dereferenceAsFreeVertex( vertexIndex );
1346 mMesh->vertices[vertexIndex] = QgsMeshVertex();
1347 mVertexToFace[vertexIndex] = -1;
1348
1349 int oldFacesCount = mMesh->faceCount();
1350 for ( int h = 0; h < holes.count(); ++h )
1351 {
1352 const QList<int> &holeVertices = holes.at( h );
1353 const QList<int> &associateMeshFacesToHole = associateMeshFacesToHoles.at( h );
1354 QHash<p2t::Point *, int> mapPoly2TriPointToVertex;
1355 std::vector<p2t::Point *> holeToFill( holeVertices.count() );
1356 try
1357 {
1358 for ( int i = 0; i < holeVertices.count(); ++i )
1359 {
1360 const QgsMeshVertex &vertex = mMesh->vertex( holeVertices.at( i ) );
1361 holeToFill[i] = new p2t::Point( vertex.x(), vertex.y() );
1362 mapPoly2TriPointToVertex.insert( holeToFill[i], holeVertices.at( i ) );
1363 }
1364
1365 auto cdt = std::make_unique<p2t::CDT>( holeToFill );
1366
1367 cdt->Triangulate();
1368 std::vector<p2t::Triangle *> triangles = cdt->GetTriangles();
1369 QVector<QgsMeshFace> newFaces( triangles.size() );
1370 for ( size_t i = 0; i < triangles.size(); ++i )
1371 {
1372 QgsMeshFace &face = newFaces[i];
1373 face.resize( 3 );
1374 for ( int j = 0; j < 3; j++ )
1375 {
1376 int vertInd = mapPoly2TriPointToVertex.value( triangles.at( i )->GetPoint( j ), -1 );
1377 if ( vertInd == -1 )
1378 throw std::exception();
1379 Q_ASSERT( !mMesh->vertices.at( vertInd ).isEmpty() );
1380 face[j] = vertInd;
1381 }
1382 }
1383
1384 QgsMeshEditingError error;
1385 QgsTopologicalMesh::TopologicalFaces topologicalFaces = createNewTopologicalFaces( newFaces, false, error );
1387 throw std::exception();
1388 int newFaceIndexStartIndex = mMesh->faceCount();
1389 QgsTopologicalMesh::Changes addChanges;
1390 addChanges.mAddedFacesFirstIndex = newFaceIndexStartIndex;
1391 addChanges.mFacesToAdd = topologicalFaces.meshFaces();
1392 addChanges.mFacesNeighborhoodToAdd = topologicalFaces.mFacesNeighborhood;
1393
1394 // vertices to face changes
1395 const QList<int> &verticesToFaceToChange = topologicalFaces.mVerticesToFace.keys();
1396 for ( const int vtc : verticesToFaceToChange )
1397 if ( mVertexToFace.at( vtc ) == -1 )
1398 addChanges.mVerticesToFaceChanges.append( {vtc,
1399 mVertexToFace.at( vtc ),
1400 addChanges.addedFaceIndexInMesh( topologicalFaces.mVerticesToFace.values( vtc ).first() )
1401 } );
1402
1403
1404 // reindex neighborhood for new faces
1405 for ( int i = 0; i < topologicalFaces.mFaces.count(); ++i )
1406 {
1407 FaceNeighbors &faceNeighbors = addChanges.mFacesNeighborhoodToAdd[i];
1408 faceNeighbors = topologicalFaces.mFacesNeighborhood.at( i );
1409 for ( int n = 0; n < faceNeighbors.count(); ++n )
1410 {
1411 if ( faceNeighbors.at( n ) != -1 )
1412 faceNeighbors[n] += newFaceIndexStartIndex; //reindex internal neighborhood
1413 }
1414 }
1415
1416 // link neighborhood for boundaries of each side
1417 for ( int i = 0 ; i < holeVertices.count(); ++i )
1418 {
1419 int vertexHoleIndex = holeVertices.at( i );
1420 int meshFaceBoundaryIndex = associateMeshFacesToHole.at( i );
1421
1422 const QgsMeshVertexCirculator circulator = QgsMeshVertexCirculator( topologicalFaces, vertexHoleIndex );
1423 circulator.goBoundaryClockwise();
1424 int newFaceBoundaryLocalIndex = circulator.currentFaceIndex();
1425 int newFaceBoundaryIndexInMesh = circulator.currentFaceIndex() + newFaceIndexStartIndex;
1426 const QgsMeshFace &newFace = circulator.currentFace();
1427 int positionInNewFaces = vertexPositionInFace( vertexHoleIndex, newFace );
1428
1429 if ( meshFaceBoundaryIndex != -1 )
1430 {
1431 const QgsMeshFace meshFace = mMesh->face( meshFaceBoundaryIndex );
1432 int positionInMeshFaceBoundary = vertexPositionInFace( *mMesh, vertexHoleIndex, meshFaceBoundaryIndex );
1433 positionInMeshFaceBoundary = ( positionInMeshFaceBoundary - 1 + meshFace.count() ) % meshFace.count(); //take the position just before
1434
1435 addChanges.mNeighborhoodChanges.append( {meshFaceBoundaryIndex, positionInMeshFaceBoundary, -1, newFaceBoundaryIndexInMesh} );
1436 }
1437
1438 addChanges.mFacesNeighborhoodToAdd[newFaceBoundaryLocalIndex][positionInNewFaces] = meshFaceBoundaryIndex;
1439 }
1440
1441 applyChanges( addChanges );
1442
1443 changes.mFacesToAdd.append( addChanges.mFacesToAdd );
1444 changes.mFacesNeighborhoodToAdd.append( addChanges.mFacesNeighborhoodToAdd );
1445 //for each neighborhood change, check if a corresponding change already exist and merge them, if not just append
1446 for ( const std::array<int, 4> &neighborChangeToAdd : std::as_const( addChanges.mNeighborhoodChanges ) )
1447 {
1448 bool merged = false;
1449 for ( std::array<int, 4> &existingNeighborChange : changes.mNeighborhoodChanges )
1450 {
1451 if ( existingNeighborChange.at( 0 ) == neighborChangeToAdd.at( 0 ) &&
1452 existingNeighborChange.at( 1 ) == neighborChangeToAdd.at( 1 ) )
1453 {
1454 merged = true;
1455 Q_ASSERT( existingNeighborChange.at( 3 ) == neighborChangeToAdd.at( 2 ) );
1456 existingNeighborChange[3] = neighborChangeToAdd.at( 3 );
1457 }
1458 }
1459 if ( !merged )
1460 changes.mNeighborhoodChanges.append( neighborChangeToAdd );
1461 }
1462 //for each vertex to face change, check if a corresponding change already exist and merge them, if not just append
1463 for ( const std::array<int, 3> &verticesToFaceToAdd : std::as_const( addChanges.mVerticesToFaceChanges ) )
1464 {
1465 bool merged = false;
1466 for ( std::array<int, 3> &existingVerticesToFace : changes.mVerticesToFaceChanges )
1467 {
1468 if ( existingVerticesToFace.at( 0 ) == verticesToFaceToAdd.at( 0 ) )
1469 {
1470 merged = true;
1471 Q_ASSERT( existingVerticesToFace.at( 2 ) == verticesToFaceToAdd.at( 1 ) );
1472 existingVerticesToFace[2] = verticesToFaceToAdd.at( 2 );
1473 }
1474 }
1475 if ( !merged )
1476 changes.mVerticesToFaceChanges.append( verticesToFaceToAdd );
1477 }
1478
1479 qDeleteAll( holeToFill );
1480 }
1481 catch ( ... )
1482 {
1483 qDeleteAll( holeToFill );
1484 QgsMessageLog::logMessage( QObject::tr( "Triangulation failed. Skipping hole…" ), QObject::tr( "Mesh Editing" ) );
1485 }
1486 }
1487 changes.mAddedFacesFirstIndex = oldFacesCount;
1488
1489
1490
1491 changes.mAddedFacesFirstIndex = oldFacesCount;
1492
1493 return changes;
1494}
1495
1497{
1498 QSet<int> facesIndex;
1499 //Search for associated faces
1500 for ( int vertexIndex : vertices )
1501 {
1502 const QList< int > faces = facesAroundVertex( vertexIndex );
1503 facesIndex.unite( QSet< int >( faces.begin(), faces.end() ) );
1504 }
1505
1506 // remove the faces
1507 Changes changes = removeFaces( facesIndex.values() );
1508
1509 // removes the vertices
1510 for ( int vertexIndex : vertices )
1511 {
1512 int currentVertexToFace = mVertexToFace.at( vertexIndex );
1513 // here, we use the method removeFaces that effectivly removes and then constructs changes
1514 changes.mRemovedVertices.append( mMesh->vertices.at( vertexIndex ) );
1515 changes.mVerticesToRemoveIndexes.append( vertexIndex );
1516 changes.mVerticesToFaceRemoved.append( currentVertexToFace );
1517 // these changes contain information that will lead to reference the removed vertex as free vertex when reverse/reapply
1518 dereferenceAsFreeVertex( vertexIndex );
1519 mMesh->vertices[vertexIndex] = QgsMeshVertex();
1520 mVertexToFace[vertexIndex] = -1;
1521 }
1522
1523 return changes;
1524}
1525
1527{
1528 QList<int> boundariesToCheckClockwiseInNewFaces = topologicFaces.mBoundaries;
1529 QList<std::array<int, 2>> boundariesToCheckCounterClockwiseInNewFaces; //couple boundary / associate face in topologicFaces.mVerticesToFace
1530 QList<int> uniqueSharedVertexBoundary;
1531
1532
1533 // Go through the boundary and search for opposite boundary vertex clockwise in new faces and compare
1534 // with boundary opposite vertices on the mesh
1535 // If, in the mesh, the opposite vertex counter clockwise is not the same , another check will be done
1536 // later with counter clockwise in new faces
1537 // If, in the mesh, the opposite vertex clockwise is the same, this is an error
1538 while ( !boundariesToCheckClockwiseInNewFaces.isEmpty() )
1539 {
1540 int boundary = boundariesToCheckClockwiseInNewFaces.takeLast();
1541
1542 const QList<int> &linkedFaces = topologicFaces.mVerticesToFace.values( boundary );
1543
1544 for ( int const linkedFace : linkedFaces )
1545 {
1546
1547 //if the boundary is a free vertex in the destination mesh, no need to check
1548 if ( mVertexToFace.at( boundary ) == -1 )
1549 continue;
1550
1551 QgsMeshVertexCirculator newFacescirculator( topologicFaces, linkedFace, boundary );
1552 QgsMeshVertexCirculator meshCirculator = vertexCirculator( boundary );
1553
1554 if ( !newFacescirculator.isValid() )
1556
1557 //Get the opposite vertex on the clockwise side with new faces block
1558 if ( !newFacescirculator.goBoundaryClockwise() )
1560
1561 int oppositeVertexInNewFaces = newFacescirculator.oppositeVertexClockwise();
1562
1563 if ( !meshCirculator.goBoundaryCounterClockwise() )
1565
1566 int oppositeVertexCCWInMesh = meshCirculator.oppositeVertexCounterClockwise();
1567
1568 if ( oppositeVertexCCWInMesh == oppositeVertexInNewFaces ) //this boundary is OK, continue wit next one
1569 continue;
1570 else
1571 {
1572 //to avoid manifold face that could pass through the check, compare not only the boundary edges but also with the opposite internal edge in new face
1573 const QgsMeshFace &newFaceOnBoundary = newFacescirculator.currentFace();
1574 int faceSize = newFaceOnBoundary.size();
1575 int posInNewFace = vertexPositionInFace( boundary, newFaceOnBoundary );
1576 int previousVertexIndex = ( posInNewFace + faceSize - 1 ) % faceSize;
1577 if ( newFaceOnBoundary.at( previousVertexIndex ) == oppositeVertexCCWInMesh )
1579 }
1580
1581 meshCirculator.goBoundaryClockwise();
1582
1583 int oppositeVertexCWInMesh = meshCirculator.oppositeVertexClockwise();
1584
1585 if ( oppositeVertexCWInMesh == oppositeVertexInNewFaces )
1587
1588 //if we are here we need more checks
1589 boundariesToCheckCounterClockwiseInNewFaces.append( {boundary, linkedFace} );
1590 }
1591 }
1592
1593 // Check now with opposite boundary vertex counter clockwise in new faces
1594 while ( !boundariesToCheckCounterClockwiseInNewFaces.isEmpty() )
1595 {
1596 std::array<int, 2> boundaryLinkedface = boundariesToCheckCounterClockwiseInNewFaces.takeLast();
1597 int boundary = boundaryLinkedface.at( 0 );
1598 int linkedFace = boundaryLinkedface.at( 1 );
1599
1600 QgsMeshVertexCirculator newFacescirculator( topologicFaces, linkedFace, boundary );
1601 QgsMeshVertexCirculator meshCirculator = vertexCirculator( boundary );
1602
1603 if ( !newFacescirculator.goBoundaryCounterClockwise() )
1605
1606 int oppositeVertexInNewFaces = newFacescirculator.oppositeVertexCounterClockwise();
1607
1608 if ( !meshCirculator.goBoundaryClockwise() )
1610
1611 int oppositeVertexCWInMesh = meshCirculator.oppositeVertexClockwise();
1612
1613 if ( oppositeVertexCWInMesh == oppositeVertexInNewFaces ) //this boundary is OK, continue with next one
1614 continue;
1615
1616 meshCirculator.goBoundaryCounterClockwise();
1617
1618 int oppositeVertexCCWInMesh = meshCirculator.oppositeVertexCounterClockwise();
1619
1620 if ( oppositeVertexCCWInMesh == oppositeVertexInNewFaces )
1622
1623 uniqueSharedVertexBoundary.append( boundary );
1624 }
1625
1626 if ( !uniqueSharedVertexBoundary.isEmpty() )
1627 return QgsMeshEditingError( Qgis::MeshEditingErrorType::UniqueSharedVertex, uniqueSharedVertexBoundary.first() );
1628
1629 // Check if internal vertices of new faces block are free in the mesh
1630 QSet<int> boundaryVertices( topologicFaces.mBoundaries.constBegin(), topologicFaces.mBoundaries.constEnd() );
1631 for ( const QgsMeshFace &newFace : std::as_const( topologicFaces.mFaces ) )
1632 {
1633 for ( const int vertexIndex : newFace )
1634 {
1635 if ( boundaryVertices.contains( vertexIndex ) )
1636 continue;
1637 if ( mVertexToFace.at( vertexIndex ) != -1 )
1639 }
1640 }
1641
1642 return QgsMeshEditingError();
1643}
1644
1646{
1647 mFaces.clear();
1648 mFacesNeighborhood.clear();
1649 mVerticesToFace.clear();
1650 mBoundaries.clear();
1651}
1652
1653QVector<QgsTopologicalMesh::FaceNeighbors> QgsTopologicalMesh::TopologicalFaces::facesNeighborhood() const
1654{
1655 return mFacesNeighborhood;
1656}
1657
1659{
1660 if ( mVerticesToFace.contains( vertexIndex ) )
1661 return mVerticesToFace.values( vertexIndex ).at( 0 );
1662
1663 return -1;
1664}
1665
1667{
1668 QgsTopologicalMesh topologicMesh;
1669 topologicMesh.mMesh = mesh;
1670 topologicMesh.mVertexToFace = QVector<int>( mesh->vertexCount(), -1 );
1671 topologicMesh.mMaximumVerticesPerFace = maxVerticesPerFace;
1673
1674 for ( int i = 0; i < mesh->faceCount(); ++i )
1675 {
1676 if ( mesh->face( i ).isEmpty() )
1677 continue;
1678 if ( maxVerticesPerFace != 0 && mesh->face( i ).count() > maxVerticesPerFace )
1679 {
1681 break;
1682 }
1683
1684 error = counterClockwiseFaces( mesh->faces[i], mesh );
1686 {
1688 error.elementIndex = i;
1689 break;
1690 }
1691 }
1692
1694 {
1695 TopologicalFaces subMesh = createTopologicalFaces( mesh->faces, &topologicMesh.mVertexToFace, error, false );
1696 topologicMesh.mFacesNeighborhood = subMesh.mFacesNeighborhood;
1697
1698 for ( int i = 0; i < topologicMesh.mMesh->vertexCount(); ++i )
1699 {
1700 if ( topologicMesh.mVertexToFace.at( i ) == -1 )
1701 topologicMesh.mFreeVertices.insert( i );
1702 }
1703 }
1704
1705 return topologicMesh;
1706}
1707
1708QgsTopologicalMesh::TopologicalFaces QgsTopologicalMesh::createNewTopologicalFaces( const QVector<QgsMeshFace> &faces, bool uniqueSharedVertexAllowed, QgsMeshEditingError &error )
1709{
1710 return createTopologicalFaces( faces, nullptr, error, uniqueSharedVertexAllowed );
1711}
1712
1713
1714QgsTopologicalMesh::TopologicalFaces QgsTopologicalMesh::createTopologicalFaces(
1715 const QVector<QgsMeshFace> &faces,
1716 QVector<int> *globalVertexToFace,
1717 QgsMeshEditingError &error,
1718 bool allowUniqueSharedVertex )
1719{
1720 int facesCount = faces.count();
1721 QVector<FaceNeighbors> faceTopologies;
1722 QMultiHash<int, int> verticesToFace;
1723
1724 error = QgsMeshEditingError();
1725 TopologicalFaces ret;
1726
1727 // Contains for each vertex a map (opposite vertex # edge) --> face index
1728 // when turning counter clockwise, if v1 first vertex and v2 second one, [v1][v2]--> neighbor face
1729 QMap<int, QMap<int, int>> verticesToNeighbor;
1730
1731 for ( int faceIndex = 0; faceIndex < facesCount; ++faceIndex )
1732 {
1733 const QgsMeshFace &face = faces.at( faceIndex );
1734 int faceSize = face.count();
1735 //Fill vertices to neighbor faces
1736 for ( int i = 0; i < faceSize; ++i )
1737 {
1738 int v1 = face[i % faceSize];
1739 int v2 = face[( i + 1 ) % faceSize];
1740 if ( verticesToNeighbor[v2].contains( v1 ) )
1741 {
1743 return ret;
1744 }
1745 else
1746 verticesToNeighbor[v2].insert( v1, faceIndex );
1747 }
1748 }
1749
1750 faceTopologies = QVector<FaceNeighbors>( faces.count() );
1751
1752 QSet<int> boundaryVertices;
1753
1754 for ( int faceIndex = 0; faceIndex < facesCount; ++faceIndex )
1755 {
1756 const QgsMeshFace &face = faces.at( faceIndex );
1757 int faceSize = face.size();
1758 FaceNeighbors &faceTopology = faceTopologies[faceIndex];
1759 faceTopology.resize( faceSize );
1760
1761 for ( int i = 0; i < faceSize; ++i )
1762 {
1763 int v1 = face.at( i );
1764 int v2 = face.at( ( i + 1 ) % faceSize );
1765
1766 if ( globalVertexToFace )
1767 {
1768 if ( ( *globalVertexToFace )[v1] == -1 )
1769 ( *globalVertexToFace )[v1] = faceIndex ;
1770 }
1771 else
1772 {
1773 if ( allowUniqueSharedVertex || !verticesToFace.contains( v1 ) )
1774 verticesToFace.insert( v1, faceIndex ) ;
1775 }
1776
1777 QMap<int, int> &edges = verticesToNeighbor[v1];
1778 if ( edges.contains( v2 ) )
1779 faceTopology[i] = edges.value( v2 );
1780 else
1781 {
1782 faceTopology[i] = -1;
1783
1784 if ( !allowUniqueSharedVertex )
1785 {
1786 if ( boundaryVertices.contains( v1 ) )
1787 {
1788 error = QgsMeshEditingError( Qgis::MeshEditingErrorType::UniqueSharedVertex, v1 ); // if a vertices is more than one time in the boundary, that means faces share only one vertices
1789 return ret;
1790 }
1791 }
1792 boundaryVertices.insert( v1 );
1793 }
1794 }
1795 }
1796
1797 ret.mFaces = faces;
1798 ret.mFacesNeighborhood = faceTopologies;
1799 ret.mBoundaries = boundaryVertices.values();
1800 ret.mVerticesToFace = verticesToFace;
1801 return ret;
1802}
1803
1804QVector<int> QgsTopologicalMesh::neighborsOfFace( int faceIndex ) const
1805{
1806 return mFacesNeighborhood.at( faceIndex );
1807}
1808
1809QList<int> QgsTopologicalMesh::facesAroundVertex( int vertexIndex ) const
1810{
1811 QgsMeshVertexCirculator circ = vertexCirculator( vertexIndex );
1812
1813 return circ.facesAround();
1814}
1815
1817{
1818 QSet<int> removedFaces( facesIndexes.begin(), facesIndexes.end() );
1819 QSet<int> concernedFaces = concernedFacesBy( facesIndexes );
1820
1821 for ( const int f : std::as_const( removedFaces ) )
1822 concernedFaces.remove( f );
1823
1824 QVector<QgsMeshFace> remainingFaces;
1825 remainingFaces.reserve( concernedFaces.count() );
1826 for ( const int f : std::as_const( concernedFaces ) )
1827 remainingFaces.append( mMesh->face( f ) );
1828
1829 QgsMeshEditingError error;
1830 createTopologicalFaces( remainingFaces, nullptr, error, false );
1831
1832 return error;
1833}
1834
1835QgsTopologicalMesh::Changes QgsTopologicalMesh::removeFaces( const QList<int> &facesIndexesToRemove )
1836{
1837 Changes changes;
1838 changes.mFaceIndexesToRemove = facesIndexesToRemove;
1839 changes.mFacesToRemove.resize( facesIndexesToRemove.count() );
1840 changes.mFacesNeighborhoodToRemove.resize( facesIndexesToRemove.count() );
1841
1842 QSet<int> indexSet( facesIndexesToRemove.begin(), facesIndexesToRemove.end() );
1843 QSet<int> threatedVertex;
1844
1845 for ( int i = 0; i < facesIndexesToRemove.count(); ++i )
1846 {
1847 const int faceIndex = facesIndexesToRemove.at( i );
1848 const QgsMeshFace &face = mMesh->face( faceIndex );
1849 changes.mFacesToRemove[i] = face;
1850 const FaceNeighbors &neighborhood = mFacesNeighborhood.at( faceIndex );
1851 changes.mFacesNeighborhoodToRemove[i] = neighborhood;
1852 for ( int j = 0; j < face.count(); ++j )
1853 {
1854 //change the neighborhood for each neighbor face
1855 int neighborIndex = neighborhood.at( j );
1856 if ( neighborIndex != -1 && !indexSet.contains( neighborIndex ) )
1857 {
1858 int positionInNeighbor = mFacesNeighborhood.at( neighborIndex ).indexOf( faceIndex );
1859 changes.mNeighborhoodChanges.append( {neighborIndex, positionInNeighbor, faceIndex, -1} );
1860 }
1861
1862 //change vertexToFace
1863 int vertexIndex = face.at( j );
1864 if ( !threatedVertex.contains( vertexIndex ) && indexSet.contains( mVertexToFace.at( vertexIndex ) ) )
1865 {
1866 int oldValue = mVertexToFace.at( vertexIndex );
1867 //look for another face linked to this vertex
1868 int refValue = -1;
1869 if ( neighborIndex != -1 && !indexSet.contains( neighborIndex ) ) //if exist, simpler to take it
1870 refValue = neighborIndex;
1871 else
1872 {
1873 QList<int> aroundFaces = facesAroundVertex( vertexIndex );
1874 aroundFaces.removeOne( faceIndex );
1875 if ( !aroundFaces.isEmpty() )
1876 {
1877 while ( !aroundFaces.isEmpty() && refValue == -1 )
1878 {
1879 if ( !indexSet.contains( aroundFaces.first() ) )
1880 refValue = aroundFaces.first();
1881 else
1882 aroundFaces.removeFirst();
1883 }
1884 }
1885 }
1886 changes.mVerticesToFaceChanges.append( {vertexIndex, oldValue, refValue} );
1887 threatedVertex.insert( vertexIndex );
1888 }
1889 }
1890 }
1891
1892 applyChanges( changes );
1893
1894 return changes;
1895}
1896
1897bool QgsTopologicalMesh::eitherSideFacesAndVertices( int vertexIndex1,
1898 int vertexIndex2,
1899 int &face1,
1900 int &face2,
1901 int &neighborVertex1InFace1,
1902 int &neighborVertex1InFace2,
1903 int &neighborVertex2inFace1,
1904 int &neighborVertex2inFace2 ) const
1905{
1906 QgsMeshVertexCirculator circulator1 = vertexCirculator( vertexIndex1 );
1907 QgsMeshVertexCirculator circulator2 = vertexCirculator( vertexIndex2 );
1908
1909 circulator1.goBoundaryClockwise();
1910 int firstFace1 = circulator1.currentFaceIndex();
1911 circulator2.goBoundaryClockwise();
1912 int firstFace2 = circulator2.currentFaceIndex();
1913
1914 if ( circulator1.oppositeVertexCounterClockwise() != vertexIndex2 )
1915 while ( circulator1.turnCounterClockwise() != -1 &&
1916 circulator1.currentFaceIndex() != firstFace1 &&
1917 circulator1.oppositeVertexCounterClockwise() != vertexIndex2 ) {}
1918
1919 if ( circulator2.oppositeVertexCounterClockwise() != vertexIndex1 )
1920 while ( circulator2.turnCounterClockwise() != -1 &&
1921 circulator2.currentFaceIndex() != firstFace2 &&
1922 circulator2.oppositeVertexCounterClockwise() != vertexIndex1 ) {}
1923
1924 if ( circulator1.oppositeVertexCounterClockwise() != vertexIndex2
1925 || circulator2.oppositeVertexCounterClockwise() != vertexIndex1 )
1926 return false;
1927
1928 face1 = circulator1.currentFaceIndex();
1929 face2 = circulator2.currentFaceIndex();
1930
1931 neighborVertex1InFace1 = circulator1.oppositeVertexClockwise();
1932 circulator1.turnCounterClockwise();
1933 neighborVertex1InFace2 = circulator1.oppositeVertexCounterClockwise();
1934
1935 neighborVertex2inFace2 = circulator2.oppositeVertexClockwise();
1936 circulator2.turnCounterClockwise();
1937 neighborVertex2inFace1 = circulator2.oppositeVertexCounterClockwise();
1938
1939 return true;
1940}
1941
1942bool QgsTopologicalMesh::edgeCanBeFlipped( int vertexIndex1, int vertexIndex2 ) const
1943{
1944 int faceIndex1;
1945 int faceIndex2;
1946 int oppositeVertexFace1;
1947 int oppositeVertexFace2;
1948 int supposedOppositeVertexFace1;
1949 int supposedoppositeVertexFace2;
1950
1951 bool result = eitherSideFacesAndVertices(
1952 vertexIndex1,
1953 vertexIndex2,
1954 faceIndex1,
1955 faceIndex2,
1956 oppositeVertexFace1,
1957 supposedoppositeVertexFace2,
1958 supposedOppositeVertexFace1,
1959 oppositeVertexFace2 );
1960
1961 if ( !result ||
1962 faceIndex1 < 0 ||
1963 faceIndex2 < 0 ||
1964 oppositeVertexFace1 < 0 ||
1965 oppositeVertexFace2 < 0 ||
1966 supposedOppositeVertexFace1 != oppositeVertexFace1 ||
1967 supposedoppositeVertexFace2 != oppositeVertexFace2 )
1968 return false;
1969
1970 const QgsMeshFace &face1 = mMesh->face( faceIndex1 );
1971 const QgsMeshFace &face2 = mMesh->face( faceIndex2 );
1972
1973
1974 if ( face1.count() != 3 || face2.count() != 3 )
1975 return false;
1976
1977 double crossProduct1 = crossProduct( vertexIndex1, oppositeVertexFace1, oppositeVertexFace2, *mMesh );
1978 double crossProduct2 = crossProduct( vertexIndex2, oppositeVertexFace1, oppositeVertexFace2, *mMesh );
1979
1980 return crossProduct1 * crossProduct2 < 0;
1981}
1982
1984{
1985 int faceIndex1;
1986 int faceIndex2;
1987 int oppositeVertexFace1;
1988 int oppositeVertexFace2;
1989 int supposedOppositeVertexFace1;
1990 int supposedoppositeVertexFace2;
1991
1992 bool result = eitherSideFacesAndVertices(
1993 vertexIndex1,
1994 vertexIndex2,
1995 faceIndex1,
1996 faceIndex2,
1997 oppositeVertexFace1,
1998 supposedoppositeVertexFace2,
1999 supposedOppositeVertexFace1,
2000 oppositeVertexFace2 );
2001
2002 if ( !result ||
2003 faceIndex1 < 0 ||
2004 faceIndex2 < 0 ||
2005 oppositeVertexFace1 < 0 ||
2006 oppositeVertexFace2 < 0 ||
2007 supposedOppositeVertexFace1 != oppositeVertexFace1 ||
2008 supposedoppositeVertexFace2 != oppositeVertexFace2 )
2009 return Changes();
2010
2011
2012 Changes changes;
2013
2014 const QgsMeshFace &face1 = mMesh->face( faceIndex1 );
2015 const QgsMeshFace &face2 = mMesh->face( faceIndex2 );
2016
2017 Q_ASSERT( face1.count() == 3 );
2018 Q_ASSERT( face2.count() == 3 );
2019
2020 int pos1 = vertexPositionInFace( vertexIndex1, face1 );
2021 int pos2 = vertexPositionInFace( vertexIndex2, face2 );
2022
2023 int neighborFace1 = mFacesNeighborhood.at( faceIndex1 ).at( pos1 );
2024 int posInNeighbor1 = QgsTopologicalMesh::vertexPositionInFace( *mMesh, oppositeVertexFace1, neighborFace1 );
2025 int neighborFace2 = mFacesNeighborhood.at( faceIndex1 ).at( ( pos1 + 1 ) % 3 );
2026 int posInNeighbor2 = QgsTopologicalMesh::vertexPositionInFace( *mMesh, vertexIndex2, neighborFace2 );
2027 int neighborFace3 = mFacesNeighborhood.at( faceIndex2 ).at( pos2 );
2028 int posInNeighbor3 = QgsTopologicalMesh::vertexPositionInFace( *mMesh, oppositeVertexFace2, neighborFace3 );
2029 int neighborFace4 = mFacesNeighborhood.at( faceIndex2 ).at( ( pos2 + 1 ) % 3 );
2030 int posInNeighbor4 = QgsTopologicalMesh::vertexPositionInFace( *mMesh, vertexIndex1, neighborFace4 );
2031
2032 changes.mFaceIndexesToRemove.append( faceIndex1 );
2033 changes.mFaceIndexesToRemove.append( faceIndex2 );
2034 changes.mFacesToRemove.append( face1 );
2035 changes.mFacesToRemove.append( face2 );
2036 changes.mFacesNeighborhoodToRemove.append( mFacesNeighborhood.at( faceIndex1 ) );
2037 changes.mFacesNeighborhoodToRemove.append( mFacesNeighborhood.at( faceIndex2 ) );
2038 int startIndex = mMesh->faceCount();
2039 changes.mAddedFacesFirstIndex = startIndex;
2040 changes.mFacesToAdd.append( {oppositeVertexFace1, oppositeVertexFace2, vertexIndex1} );
2041 changes.mFacesToAdd.append( {oppositeVertexFace2, oppositeVertexFace1, vertexIndex2} );
2042 changes.mFacesNeighborhoodToAdd.append( {startIndex + 1,
2043 mFacesNeighborhood.at( faceIndex2 ).at( ( pos2 + 1 ) % 3 ),
2044 mFacesNeighborhood.at( faceIndex1 ).at( pos1 )
2045 } );
2046 changes.mFacesNeighborhoodToAdd.append( {startIndex,
2047 mFacesNeighborhood.at( faceIndex1 ).at( ( pos1 + 1 ) % 3 ),
2048 mFacesNeighborhood.at( faceIndex2 ).at( pos2 )
2049 } );
2050
2051 if ( neighborFace1 >= 0 )
2052 changes.mNeighborhoodChanges.append( {neighborFace1, posInNeighbor1, faceIndex1, startIndex} );
2053 if ( neighborFace2 >= 0 )
2054 changes.mNeighborhoodChanges.append( {neighborFace2, posInNeighbor2, faceIndex1, startIndex + 1} );
2055 if ( neighborFace3 >= 0 )
2056 changes.mNeighborhoodChanges.append( {neighborFace3, posInNeighbor3, faceIndex2, startIndex + 1} );
2057 if ( neighborFace4 >= 0 )
2058 changes.mNeighborhoodChanges.append( {neighborFace4, posInNeighbor4, faceIndex2, startIndex} );
2059
2060
2061 if ( mVertexToFace.at( vertexIndex1 ) == faceIndex1 || mVertexToFace.at( vertexIndex1 ) == faceIndex2 )
2062 changes.mVerticesToFaceChanges.append( {vertexIndex1, mVertexToFace.at( vertexIndex1 ), startIndex} );
2063 if ( mVertexToFace.at( vertexIndex2 ) == faceIndex1 || mVertexToFace.at( vertexIndex2 ) == faceIndex2 )
2064 changes.mVerticesToFaceChanges.append( {vertexIndex2, mVertexToFace.at( vertexIndex2 ), startIndex + 1} );
2065
2066 if ( mVertexToFace.at( oppositeVertexFace1 ) == faceIndex1 )
2067 changes.mVerticesToFaceChanges.append( {oppositeVertexFace1, faceIndex1, startIndex} );
2068
2069 if ( mVertexToFace.at( oppositeVertexFace2 ) == faceIndex2 )
2070 changes.mVerticesToFaceChanges.append( {oppositeVertexFace2, faceIndex2, startIndex + 1} );
2071
2072 applyChanges( changes );
2073
2074 return changes;
2075}
2076
2077bool QgsTopologicalMesh::canBeMerged( int vertexIndex1, int vertexIndex2 ) const
2078{
2079 int faceIndex1;
2080 int faceIndex2;
2081 int neighborVertex1InFace1;
2082 int neighborVertex1InFace2;
2083 int neighborVertex2inFace1;
2084 int neighborVertex2inFace2;
2085
2086 bool result = eitherSideFacesAndVertices(
2087 vertexIndex1,
2088 vertexIndex2,
2089 faceIndex1,
2090 faceIndex2,
2091 neighborVertex1InFace1,
2092 neighborVertex1InFace2,
2093 neighborVertex2inFace1,
2094 neighborVertex2inFace2 );
2095
2096 if ( !result ||
2097 faceIndex1 < 0 ||
2098 faceIndex2 < 0 )
2099 return false;
2100
2101 const QgsMeshFace &face1 = mMesh->face( faceIndex1 );
2102 const QgsMeshFace &face2 = mMesh->face( faceIndex2 );
2103
2104 if ( face1.count() + face2.count() - 2 > mMaximumVerticesPerFace )
2105 return false;
2106
2107 QgsMeshVertex v1 = mMesh->vertices.at( vertexIndex1 );
2108 QgsMeshVertex v2 = mMesh->vertices.at( vertexIndex2 );
2109 QgsMeshVertex nv11 = mMesh->vertices.at( neighborVertex1InFace1 );
2110 QgsMeshVertex nv12 = mMesh->vertices.at( neighborVertex1InFace2 );
2111 QgsMeshVertex nv21 = mMesh->vertices.at( neighborVertex2inFace1 );
2112 QgsMeshVertex nv22 = mMesh->vertices.at( neighborVertex2inFace2 );
2113
2114 double crossProduct1 = crossProduct( vertexIndex1, neighborVertex1InFace1, neighborVertex1InFace2, *mMesh );
2115 double crossProduct2 = crossProduct( vertexIndex2, neighborVertex2inFace1, neighborVertex2inFace2, *mMesh );
2116
2117 return crossProduct1 * crossProduct2 < 0;
2118}
2119
2120QgsTopologicalMesh::Changes QgsTopologicalMesh::merge( int vertexIndex1, int vertexIndex2 )
2121{
2122 int faceIndex1;
2123 int faceIndex2;
2124 int neighborVertex1InFace1;
2125 int neighborVertex1InFace2;
2126 int neighborVertex2inFace1;
2127 int neighborVertex2inFace2;
2128
2129 bool result = eitherSideFacesAndVertices(
2130 vertexIndex1,
2131 vertexIndex2,
2132 faceIndex1,
2133 faceIndex2,
2134 neighborVertex1InFace1,
2135 neighborVertex1InFace2,
2136 neighborVertex2inFace1,
2137 neighborVertex2inFace2 );
2138
2139 if ( !result ||
2140 faceIndex1 < 0 ||
2141 faceIndex2 < 0 )
2142 return Changes();
2143
2144 Changes changes;
2145
2146 const QgsMeshFace &face1 = mMesh->face( faceIndex1 );
2147 const QgsMeshFace &face2 = mMesh->face( faceIndex2 );
2148 int faceSize1 = face1.count();
2149 int faceSize2 = face2.count();
2150
2151 int pos1 = QgsTopologicalMesh::vertexPositionInFace( vertexIndex1, face1 );
2152 int pos2 = QgsTopologicalMesh::vertexPositionInFace( vertexIndex2, face2 );
2153
2154 changes.mFaceIndexesToRemove.append( faceIndex1 );
2155 changes.mFaceIndexesToRemove.append( faceIndex2 );
2156 changes.mFacesToRemove.append( face1 );
2157 changes.mFacesToRemove.append( face2 );
2158 changes.mFacesNeighborhoodToRemove.append( mFacesNeighborhood.at( faceIndex1 ) );
2159 changes.mFacesNeighborhoodToRemove.append( mFacesNeighborhood.at( faceIndex2 ) );
2160 int startIndex = mMesh->faceCount();
2161 changes.mAddedFacesFirstIndex = startIndex;
2162
2163 QgsMeshFace newface;
2164 FaceNeighbors newNeighborhood;
2165 for ( int i = 0; i < faceSize1 - 1; ++i )
2166 {
2167 int currentPos = ( pos1 + i ) % faceSize1;
2168 newface.append( face1.at( currentPos ) ); //add vertex of face1
2169
2170 int currentNeighbor = mFacesNeighborhood.at( faceIndex1 ).at( currentPos );
2171 newNeighborhood.append( currentNeighbor );
2172
2173 if ( currentNeighbor != -1 )
2174 {
2175 int currentPosInNeighbor = QgsTopologicalMesh::vertexPositionInFace( *mMesh, face1.at( ( currentPos + 1 ) % faceSize1 ), currentNeighbor );
2176 changes.mNeighborhoodChanges.append( {currentNeighbor, currentPosInNeighbor, faceIndex1, startIndex} );
2177 }
2178 }
2179 for ( int i = 0; i < faceSize2 - 1; ++i )
2180 {
2181 int currentPos = ( pos2 + i ) % faceSize2;
2182 newface.append( face2.at( currentPos ) ); //add vertex of face2
2183
2184 int currentNeighbor = mFacesNeighborhood.at( faceIndex2 ).at( currentPos );
2185 newNeighborhood.append( currentNeighbor );
2186
2187 if ( currentNeighbor != -1 )
2188 {
2189 int currentPosInNeighbor = QgsTopologicalMesh::vertexPositionInFace( *mMesh, face2.at( ( currentPos + 1 ) % faceSize2 ), currentNeighbor );
2190 changes.mNeighborhoodChanges.append( {currentNeighbor, currentPosInNeighbor, faceIndex2, startIndex} );
2191 }
2192 }
2193
2194 for ( int i = 0; i < faceSize1; ++i )
2195 if ( mVertexToFace.at( face1.at( i ) ) == faceIndex1 )
2196 changes.mVerticesToFaceChanges.append( {face1.at( i ), faceIndex1, startIndex} );
2197
2198 for ( int i = 0; i < faceSize2; ++i )
2199 if ( mVertexToFace.at( face2.at( i ) ) == faceIndex2 )
2200 changes.mVerticesToFaceChanges.append( {face2.at( i ), faceIndex2, startIndex} );
2201
2202 changes.mFacesToAdd.append( newface );
2203 changes.mFacesNeighborhoodToAdd.append( newNeighborhood );
2204
2205 applyChanges( changes );
2206
2207 return changes;
2208}
2209
2210bool QgsTopologicalMesh::canBeSplit( int faceIndex ) const
2211{
2212 const QgsMeshFace face = mMesh->face( faceIndex );
2213
2214 return face.count() == 4;
2215}
2216
2218{
2219 //search for the spliited angle (greater angle)
2220 const QgsMeshFace &face = mMesh->face( faceIndex );
2221 int faceSize = face.count();
2222
2223 Q_ASSERT( faceSize == 4 );
2224
2225 double maxAngle = 0;
2226 int splitVertexPos = -1;
2227 for ( int i = 0; i < faceSize; ++i )
2228 {
2229 QgsVector vect1( mMesh->vertex( face.at( i ) ) - mMesh->vertex( face.at( ( i + 1 ) % faceSize ) ) );
2230 QgsVector vect2( mMesh->vertex( face.at( ( i + 2 ) % faceSize ) ) - mMesh->vertex( face.at( ( i + 1 ) % faceSize ) ) );
2231
2232 double angle = std::abs( vect1.angle( vect2 ) );
2233 angle = std::min( angle, 2.0 * M_PI - angle );
2234 if ( angle > maxAngle )
2235 {
2236 maxAngle = angle;
2237 splitVertexPos = ( i + 1 ) % faceSize;
2238 }
2239 }
2240
2241 Changes changes;
2242 if ( splitVertexPos == -1 )
2243 return changes;
2244
2245 const QgsMeshFace newFace1 = {face.at( splitVertexPos ),
2246 face.at( ( splitVertexPos + 1 ) % faceSize ),
2247 face.at( ( splitVertexPos + 2 ) % faceSize )
2248 };
2249
2250 const QgsMeshFace newFace2 = {face.at( splitVertexPos ),
2251 face.at( ( splitVertexPos + 2 ) % faceSize ),
2252 face.at( ( splitVertexPos + 3 ) % faceSize )
2253 };
2254
2255 QVector<int> neighborIndex( faceSize );
2256 QVector<int> posInNeighbor( faceSize );
2257
2258 for ( int i = 0; i < faceSize; ++i )
2259 {
2260 neighborIndex[i] = mFacesNeighborhood.at( faceIndex ).at( ( splitVertexPos + i ) % faceSize );
2261 posInNeighbor[i] = QgsTopologicalMesh::vertexPositionInFace( *mMesh, face.at( ( splitVertexPos + i + 1 ) % faceSize ), neighborIndex[i] );
2262 }
2263
2264 changes.mFaceIndexesToRemove.append( faceIndex );
2265 changes.mFacesToRemove.append( face );
2266 changes.mFacesNeighborhoodToRemove.append( mFacesNeighborhood.at( faceIndex ) );
2267 int startIndex = mMesh->faceCount();
2268 changes.mAddedFacesFirstIndex = startIndex;
2269 changes.mFacesToAdd.append( newFace1 );
2270 changes.mFacesToAdd.append( newFace2 );
2271
2272 changes.mFacesNeighborhoodToAdd.append( {mFacesNeighborhood.at( faceIndex ).at( splitVertexPos ),
2273 mFacesNeighborhood.at( faceIndex ).at( ( splitVertexPos + 1 ) % faceSize ),
2274 startIndex + 1
2275 } );
2276 changes.mFacesNeighborhoodToAdd.append( {startIndex,
2277 mFacesNeighborhood.at( faceIndex ).at( ( splitVertexPos + 2 ) % faceSize ),
2278 mFacesNeighborhood.at( faceIndex ).at( ( splitVertexPos + 3 ) % faceSize )
2279 } );
2280
2281 for ( int i = 0; i < faceSize; ++i )
2282 {
2283 if ( neighborIndex[i] >= 0 )
2284 changes.mNeighborhoodChanges.append( {neighborIndex[i], posInNeighbor[i], faceIndex, startIndex + int( i / 2 )} );
2285
2286 int vertexIndex = face.at( ( splitVertexPos + i ) % faceSize );
2287 if ( mVertexToFace.at( vertexIndex ) == faceIndex )
2288 changes.mVerticesToFaceChanges.append( {vertexIndex, faceIndex, startIndex + int( i / 2 )} );
2289 }
2290
2291 applyChanges( changes );
2292
2293 return changes;
2294}
2295
2296
2298{
2299 Changes changes;
2300 changes.mVerticesToAdd.append( vertex );
2301 changes.mVertexToFaceToAdd.append( -1 );
2302
2303 mMesh->vertices.append( vertex );
2304 mVertexToFace.append( -1 );
2305 changes.mAddedFacesFirstIndex = mMesh->faceCount();
2306
2307 const QgsMeshFace includingFace = mMesh->face( includingFaceIndex );
2308 const FaceNeighbors includingFaceNeighborhood = mFacesNeighborhood.at( includingFaceIndex );
2309 int includingFaceSize = includingFace.count();
2310
2311 for ( int i = 0; i < includingFaceSize; ++i )
2312 {
2313 // add a new face
2314 QgsMeshFace face( 3 );
2315 face[0] = mMesh->vertexCount() - 1;
2316 face[1] = includingFace.at( i );
2317 face[2] = includingFace.at( ( i + 1 ) % includingFaceSize );
2318 mMesh->faces.append( face );
2319 changes.mFacesToAdd.append( face );
2320
2321 int currentVertexIndex = includingFace.at( i );
2322 if ( mVertexToFace.at( currentVertexIndex ) == includingFaceIndex )
2323 {
2324 int newFaceIndex = mMesh->faceCount() - 1;
2325 mVertexToFace[currentVertexIndex] = newFaceIndex;
2326 changes.mVerticesToFaceChanges.append( {currentVertexIndex, includingFaceIndex, newFaceIndex} );
2327 }
2328
2329 int includingFaceNeighbor = includingFaceNeighborhood.at( i );
2330 FaceNeighbors neighbors(
2331 {
2332 changes.mAddedFacesFirstIndex + ( i + includingFaceSize - 1 ) % includingFaceSize,
2333 includingFaceNeighbor,
2334 changes.mAddedFacesFirstIndex + ( i + includingFaceSize + 1 ) % includingFaceSize
2335 } );
2336 mFacesNeighborhood.append( neighbors );
2337 changes.mFacesNeighborhoodToAdd.append( neighbors );
2338
2339 if ( includingFaceNeighbor != -1 )
2340 {
2341 int indexInNeighbor = QgsTopologicalMesh::vertexPositionInFace( *mMesh, includingFace.at( ( i + 1 ) % includingFaceSize ), includingFaceNeighbor );
2342 int oldValue = mFacesNeighborhood[includingFaceNeighbor][indexInNeighbor];
2343 mFacesNeighborhood[includingFaceNeighbor][indexInNeighbor] = changes.mAddedFacesFirstIndex + i;
2344 changes.mNeighborhoodChanges.append( {includingFaceNeighbor, indexInNeighbor, oldValue, changes.mAddedFacesFirstIndex + i} );
2345 }
2346 }
2347
2348 changes.mFacesToRemove.append( includingFace );
2349 changes.mFaceIndexesToRemove.append( includingFaceIndex );
2350 changes.mFacesNeighborhoodToRemove.append( includingFaceNeighborhood );
2351
2352 mFacesNeighborhood[includingFaceIndex] = FaceNeighbors();
2353 mMesh->faces[includingFaceIndex] = QgsMeshFace();
2354 mVertexToFace[mVertexToFace.count() - 1] = mMesh->faceCount() - 1;
2355 changes.mVertexToFaceToAdd[changes.mVertexToFaceToAdd.count() - 1] = mMesh->faceCount() - 1 ;
2356
2357 return changes;
2358}
2359
2361{
2362 const QgsMeshFace face1 = mMesh->face( faceIndex );
2363
2364 Changes changes;
2365 changes.mVerticesToAdd.append( vertexToInsert );
2366 changes.mAddedFacesFirstIndex = mMesh->faceCount();
2367
2368 // triangulate the first face
2369 int newVertexPositionInFace1 = position + 1;
2370
2371 auto triangulate = [this, &changes]( int removedFaceIndex, const QgsMeshVertex & newVertex, int newVertexPosition, QVector<int> &edgeFacesIndexes )->bool
2372 {
2373 const QgsMeshFace &initialFace = mMesh->face( removedFaceIndex );
2374 changes.mFacesToRemove.append( initialFace );
2375 changes.mFaceIndexesToRemove.append( removedFaceIndex );
2376 changes.mFacesNeighborhoodToRemove.append( mFacesNeighborhood.at( removedFaceIndex ) );
2377 const int addedVertexIndex = mMesh->vertexCount();
2378
2379 int faceStartGlobalIndex = mMesh->faceCount() + changes.mFacesToAdd.count();
2380 int localStartIndex = changes.mFacesToAdd.count();
2381
2382 QVector<int> newBoundary = initialFace;
2383 newBoundary.insert( newVertexPosition, addedVertexIndex );
2384
2385 try
2386 {
2387 QHash<p2t::Point *, int> mapPoly2TriPointToVertex;
2388 std::vector<p2t::Point *> faceToFill( newBoundary.count() );
2389 for ( int i = 0; i < newBoundary.count(); ++i )
2390 {
2391 QgsMeshVertex vert;
2392
2393 if ( newBoundary.at( i ) == addedVertexIndex )
2394 vert = newVertex;
2395 else
2396 vert = mMesh->vertex( newBoundary.at( i ) );
2397
2398 faceToFill[i] = new p2t::Point( vert.x(), vert.y() );
2399 mapPoly2TriPointToVertex.insert( faceToFill[i], newBoundary.at( i ) );
2400 }
2401
2402 auto cdt = std::make_unique<p2t::CDT>( faceToFill );
2403 cdt->Triangulate();
2404 std::vector<p2t::Triangle *> triangles = cdt->GetTriangles();
2405 QVector<QgsMeshFace> newFaces( triangles.size() );
2406 for ( size_t i = 0; i < triangles.size(); ++i )
2407 {
2408 QgsMeshFace &face = newFaces[i];
2409 face.resize( 3 );
2410 for ( int j = 0; j < 3; j++ )
2411 {
2412 int vertInd = mapPoly2TriPointToVertex.value( triangles.at( i )->GetPoint( j ), -1 );
2413 if ( vertInd == -1 )
2414 throw std::exception();
2415 face[j] = vertInd;
2416 }
2417 }
2418
2419 QgsMeshEditingError error;
2420 QgsTopologicalMesh::TopologicalFaces topologicalFaces = createNewTopologicalFaces( newFaces, false, error );
2422 throw std::exception();
2423
2424 changes.mFacesToAdd.append( topologicalFaces.meshFaces() );
2425 changes.mFacesNeighborhoodToAdd.append( topologicalFaces.mFacesNeighborhood );
2426
2427 // vertices to face changes
2428 const QList<int> &verticesToFaceToChange = topologicalFaces.mVerticesToFace.keys();
2429 for ( const int vtc : verticesToFaceToChange )
2430 if ( vtc != addedVertexIndex && mVertexToFace.at( vtc ) == removedFaceIndex )
2431 changes.mVerticesToFaceChanges.append(
2432 {
2433 vtc,
2434 removedFaceIndex,
2435 topologicalFaces.vertexToFace( vtc ) + faceStartGlobalIndex
2436 } );
2437
2438 // reindex neighborhood for new faces
2439 for ( int i = 0; i < topologicalFaces.mFaces.count(); ++i )
2440 {
2441 FaceNeighbors &faceNeighbors = changes.mFacesNeighborhoodToAdd[i + localStartIndex];
2442 for ( int n = 0; n < faceNeighbors.count(); ++n )
2443 {
2444 if ( faceNeighbors.at( n ) != -1 )
2445 faceNeighbors[n] += faceStartGlobalIndex; //reindex internal neighborhood
2446 }
2447 }
2448
2449 edgeFacesIndexes.resize( 2 );
2450 // link neighborhood for boundaries of each side
2451 for ( int i = 0 ; i < newBoundary.count(); ++i )
2452 {
2453 int vertexIndex = newBoundary.at( i );
2454 QgsMeshVertexCirculator circulator = QgsMeshVertexCirculator( topologicalFaces, vertexIndex );
2455 circulator.goBoundaryClockwise();
2456 int newFaceBoundaryLocalIndex = localStartIndex + circulator.currentFaceIndex();
2457
2458 int newFaceBoundaryIndexInMesh = faceStartGlobalIndex;
2459
2460 int meshFaceBoundaryIndex;
2461 if ( i == newVertexPosition )
2462 {
2463 meshFaceBoundaryIndex = -1; //face that are on the opposite side of the edge, filled later
2464 edgeFacesIndexes[0] = newFaceBoundaryLocalIndex;
2465 }
2466 else if ( i == ( newVertexPosition + newBoundary.count() - 1 ) % newBoundary.count() )
2467 {
2468 meshFaceBoundaryIndex = -1; //face that are on the opposite side of the edge, filled later
2469 edgeFacesIndexes[1] = newFaceBoundaryLocalIndex;
2470 }
2471 else
2472 meshFaceBoundaryIndex = mFacesNeighborhood.at( removedFaceIndex ).at( QgsTopologicalMesh::vertexPositionInFace( vertexIndex, initialFace ) );
2473
2474 const QgsMeshFace &newFace = circulator.currentFace();
2475 int positionInNewFaces = QgsTopologicalMesh::vertexPositionInFace( vertexIndex, newFace );
2476
2477 if ( meshFaceBoundaryIndex != -1 )
2478 {
2479 const QgsMeshFace meshFace = mMesh->face( meshFaceBoundaryIndex );
2480 int positionInMeshFaceBoundary = QgsTopologicalMesh::vertexPositionInFace( *mMesh, vertexIndex, meshFaceBoundaryIndex );
2481 positionInMeshFaceBoundary = ( positionInMeshFaceBoundary - 1 + meshFace.count() ) % meshFace.count(); //take the position just before
2482
2483 changes.mNeighborhoodChanges.append( {meshFaceBoundaryIndex,
2484 positionInMeshFaceBoundary,
2485 removedFaceIndex,
2486 newFaceBoundaryIndexInMesh +
2487 circulator.currentFaceIndex()
2488 } );
2489 }
2490
2491 changes.mFacesNeighborhoodToAdd[newFaceBoundaryLocalIndex][positionInNewFaces] = meshFaceBoundaryIndex;
2492 }
2493
2494 qDeleteAll( faceToFill );
2495 }
2496 catch ( ... )
2497 {
2498 return false;
2499 }
2500
2501 return true;
2502 };
2503
2504 QVector<int> edgeFacesIndexes;
2505 if ( !triangulate( faceIndex, vertexToInsert, newVertexPositionInFace1, edgeFacesIndexes ) )
2506 return Changes();
2507
2508 changes.mVertexToFaceToAdd.append( edgeFacesIndexes.at( 0 ) + changes.mAddedFacesFirstIndex );
2509
2510 int addedVertexIndex = mMesh->vertexCount();
2511
2512 //if exist, triangulate the second face if exists
2513 int face2Index = mFacesNeighborhood.at( faceIndex ).at( position );
2514 if ( face2Index != -1 )
2515 {
2516 const QgsMeshFace &face2 = mMesh->face( face2Index );
2517 int vertexPositionInFace2 = vertexPositionInFace( face1.at( position ), face2 );
2518 QVector<int> edgeFacesIndexesFace2;
2519 if ( !triangulate( face2Index, vertexToInsert, vertexPositionInFace2, edgeFacesIndexesFace2 ) )
2520 return Changes();
2521
2522 //link neighborhood with other side
2523 const QgsMeshFace &firstFaceSide1 = changes.mFacesToAdd.at( edgeFacesIndexes.at( 0 ) );
2524 int pos1InFaceSide1 = vertexPositionInFace( addedVertexIndex, firstFaceSide1 );
2525
2526 const QgsMeshFace &secondFaceSide1 = changes.mFacesToAdd.at( edgeFacesIndexes.at( 1 ) );
2527 int pos2InFaceSide1 = vertexPositionInFace( addedVertexIndex, secondFaceSide1 );
2528 pos2InFaceSide1 = ( pos2InFaceSide1 + secondFaceSide1.size() - 1 ) % secondFaceSide1.size();
2529
2530 const QgsMeshFace &firstFaceSide2 = changes.mFacesToAdd.at( edgeFacesIndexesFace2.at( 0 ) );
2531 int pos1InFaceSide2 = vertexPositionInFace( addedVertexIndex, firstFaceSide2 );
2532
2533 const QgsMeshFace &secondFaceSide2 = changes.mFacesToAdd.at( edgeFacesIndexesFace2.at( 1 ) );
2534 int pos2InFaceSide2 = vertexPositionInFace( addedVertexIndex, secondFaceSide2 );
2535 pos2InFaceSide2 = ( pos2InFaceSide2 + secondFaceSide1.size() - 1 ) % secondFaceSide1.size();
2536
2537 changes.mFacesNeighborhoodToAdd[edgeFacesIndexes.at( 0 )][pos1InFaceSide1] = edgeFacesIndexesFace2.at( 1 ) + changes.mAddedFacesFirstIndex;
2538 changes.mFacesNeighborhoodToAdd[edgeFacesIndexes.at( 1 )][pos2InFaceSide1] = edgeFacesIndexesFace2.at( 0 ) + changes.mAddedFacesFirstIndex;
2539 changes.mFacesNeighborhoodToAdd[edgeFacesIndexesFace2.at( 0 )][pos1InFaceSide2] = edgeFacesIndexes.at( 1 ) + changes.mAddedFacesFirstIndex;
2540 changes.mFacesNeighborhoodToAdd[edgeFacesIndexesFace2.at( 1 )][pos2InFaceSide2] = edgeFacesIndexes.at( 0 ) + changes.mAddedFacesFirstIndex;
2541 }
2542
2543 applyChanges( changes );
2544 return changes;
2545}
2546
2547QgsTopologicalMesh::Changes QgsTopologicalMesh::changeZValue( const QList<int> &verticesIndexes, const QList<double> &newValues )
2548{
2549 Q_ASSERT( verticesIndexes.count() == newValues.count() );
2550 Changes changes;
2551 changes.mChangeCoordinateVerticesIndexes.reserve( verticesIndexes.count() );
2552 changes.mNewZValues.reserve( verticesIndexes.count() );
2553 changes.mOldZValues.reserve( verticesIndexes.count() );
2554 for ( int i = 0; i < verticesIndexes.count(); ++i )
2555 {
2556 changes.mChangeCoordinateVerticesIndexes.append( verticesIndexes.at( i ) );
2557 changes.mOldZValues.append( mMesh->vertices.at( verticesIndexes.at( i ) ).z() );
2558 changes.mNewZValues.append( newValues.at( i ) );
2559 }
2560
2561 applyChanges( changes );
2562
2563 return changes;
2564}
2565
2566QgsTopologicalMesh::Changes QgsTopologicalMesh::changeXYValue( const QList<int> &verticesIndexes, const QList<QgsPointXY> &newValues )
2567{
2568 Q_ASSERT( verticesIndexes.count() == newValues.count() );
2569 Changes changes;
2570 changes.mChangeCoordinateVerticesIndexes.reserve( verticesIndexes.count() );
2571 changes.mNewXYValues.reserve( verticesIndexes.count() );
2572 changes.mOldXYValues.reserve( verticesIndexes.count() );
2573 QSet<int> concernedFace;
2574 for ( int i = 0; i < verticesIndexes.count(); ++i )
2575 {
2576 changes.mChangeCoordinateVerticesIndexes.append( verticesIndexes.at( i ) );
2577 changes.mOldXYValues.append( mMesh->vertices.at( verticesIndexes.at( i ) ) );
2578 changes.mNewXYValues.append( newValues.at( i ) );
2579 const QList< int > faces = facesAroundVertex( verticesIndexes.at( i ) );
2580 concernedFace.unite( QSet< int>( faces.begin(), faces.end() ) );
2581 }
2582
2583 changes.mNativeFacesIndexesGeometryChanged = concernedFace.values();
2584
2585 applyChanges( changes );
2586
2587 return changes;
2588}
2589
2590bool QgsTopologicalMesh::delaunayConditionForEdge( int vertexIndex1, int vertexIndex2 )
2591{
2592 int faceIndex1;
2593 int faceIndex2;
2594 int oppositeVertexFace1;
2595 int oppositeVertexFace2;
2596 int supposedOppositeVertexFace1;
2597 int supposedoppositeVertexFace2;
2598
2599 bool result = eitherSideFacesAndVertices(
2600 vertexIndex1,
2601 vertexIndex2,
2602 faceIndex1,
2603 faceIndex2,
2604 oppositeVertexFace1,
2605 supposedoppositeVertexFace2,
2606 supposedOppositeVertexFace1,
2607 oppositeVertexFace2 );
2608
2609 if ( ! result )
2610 return false;
2611
2612 const QgsMeshFace face1 = mMesh->face( faceIndex1 );
2613 const QgsMeshFace face2 = mMesh->face( faceIndex2 );
2614
2615 QgsCircle circle = QgsCircle::from3Points( mMesh->vertex( face1.at( 0 ) ),
2616 mMesh->vertex( face1.at( 1 ) ),
2617 mMesh->vertex( face1.at( 2 ) ) );
2618 bool circle1ContainsPoint = circle.contains( mMesh->vertex( supposedoppositeVertexFace2 ) );
2619
2620 circle = QgsCircle::from3Points( mMesh->vertex( face2.at( 0 ) ),
2621 mMesh->vertex( face2.at( 1 ) ),
2622 mMesh->vertex( face2.at( 2 ) ) );
2623 bool circle2ContainsPoint = circle.contains( mMesh->vertex( supposedOppositeVertexFace1 ) );
2624
2625 return !( circle1ContainsPoint || circle2ContainsPoint );
2626}
@ InvalidFace
An error occurs due to an invalid face (for example, vertex indexes are unordered).
Definition qgis.h:1661
@ UniqueSharedVertex
A least two faces share only one vertices.
Definition qgis.h:1664
@ ManifoldFace
ManifoldFace.
Definition qgis.h:1666
@ InvalidVertex
An error occurs due to an invalid vertex (for example, vertex index is out of range the available ver...
Definition qgis.h:1665
@ FlatFace
A flat face is present.
Definition qgis.h:1663
Circle geometry type.
Definition qgscircle.h:44
bool contains(const QgsPoint &point, double epsilon=1E-8) const
Returns true if the circle contains the point.
static QgsCircle from3Points(const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double epsilon=1E-8)
Constructs a circle by 3 points on the circle.
Definition qgscircle.cpp:82
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.
Represents an error which occurred during mesh editing.
Qgis::MeshEditingErrorType errorType
Convenience class that turns around a vertex and provides information about faces and vertices.
bool isValid() const
Returns whether the vertex circulator is valid.
int turnClockwise() const
Turns counter clockwise around the vertex and returns the new current face, -1 if the circulator pass...
bool goBoundaryCounterClockwise() const
Sets the circulator on the boundary face turning counter clockwise, return false is there isn't bound...
int oppositeVertexCounterClockwise() const
Returns the opposite vertex of the current face and on the edge on the side turning counter clockwise...
int turnCounterClockwise() const
Turns counter clockwise around the vertex and returns the new current face, -1 if the circulator pass...
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.
QgsMeshVertexCirculator(const QgsTopologicalMesh &topologicalMesh, int vertexIndex)
Constructor with topologicalMesh and vertexIndex.
int oppositeVertexClockwise() const
Returns the opposite vertex of the current face and on the edge on the side turning clockwise.
int degree() const
Returns the degree of the vertex, that is the count of other vertices linked.
QList< int > facesAround() const
Returns all the faces indexes around the vertex.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
Adds a message to the log instance (and creates it if necessary).
Represents a 2D point.
Definition qgspointxy.h:60
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
double x
Definition qgspoint.h:52
bool isEmpty() const override
Returns true if the geometry is empty.
Definition qgspoint.cpp:739
double y
Definition qgspoint.h:53
Contains topological differences between two states of a topological mesh, only accessible from the Q...
void clearChanges()
Clears all changes.
QVector< FaceNeighbors > mFacesNeighborhoodToRemove
QVector< QgsMeshFace > removedFaces() const
Returns the faces that are removed with this changes.
QList< QgsMeshVertex > mRemovedVertices
QVector< QgsMeshVertex > addedVertices() const
Returns the added vertices with this changes.
QList< std::array< int, 4 > > mNeighborhoodChanges
bool isEmpty() const
Returns whether changes are empty, that there is nothing to change.
QList< int > changedCoordinatesVerticesIndexes() const
Returns the indexes of vertices that have changed coordinates.
QList< int > mNativeFacesIndexesGeometryChanged
QVector< QgsMeshFace > mFacesToAdd
QList< int > removedFaceIndexes() const
Returns the indexes of the faces that are removed with this changes.
QVector< FaceNeighbors > mFacesNeighborhoodToAdd
QList< std::array< int, 3 > > mVerticesToFaceChanges
QList< double > newVerticesZValues() const
Returns the new Z values of vertices that have changed their coordinates.
QVector< QgsMeshVertex > mVerticesToAdd
QVector< QgsMeshFace > addedFaces() const
Returns the face that are added with this changes.
QList< QgsPointXY > oldVerticesXYValues() const
Returns the old (X,Y) values of vertices that have changed their coordinates.
QList< QgsPointXY > newVerticesXYValues() const
Returns the new (X,Y) values of vertices that have changed their coordinates.
QVector< QgsMeshFace > mFacesToRemove
QList< int > nativeFacesIndexesGeometryChanged() const
Returns a list of the native face indexes that have a geometry changed.
QList< int > verticesToRemoveIndexes() const
Returns the indexes of vertices to remove.
Contains independent faces and topological information about these 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.
void clear()
Clears all data contained in the instance.
QVector< QgsMeshFace > meshFaces() const
Returns faces.
Wraps a QgsMesh to ensure the consistency of the mesh during editing and helps to access elements fro...
static QgsMeshEditingError checkTopologyOfVerticesAsFace(const QVector< QgsMeshVertex > &vertices, bool &clockwise)
Checks the topology of the vertices as they are contained in a face and returns indication on directi...
Changes changeZValue(const QList< int > &verticesIndexes, const QList< double > &newValues)
Changes the Z values of the vertices with indexes in vertices indexes with the values in newValues.
static QgsTopologicalMesh createTopologicalMesh(QgsMesh *mesh, int maxVerticesPerFace, QgsMeshEditingError &error)
Creates a topologicaly consistent mesh with mesh, this static method modifies mesh to be topological ...
bool isVertexFree(int vertexIndex) const
Returns whether the vertex is a free vertex.
static QgsMeshEditingError counterClockwiseFaces(QgsMeshFace &face, QgsMesh *mesh)
Checks the topology of the face and sets it counter clockwise if necessary.
Changes removeVertexFillHole(int vertexIndex)
Removes the vertex with index vertexIndex.
static int vertexPositionInFace(int vertexIndex, const QgsMeshFace &face)
Returns vertex position in face.
static QgsMeshEditingError checkTopology(const QgsMesh &mesh, int maxVerticesPerFace)
Checks the topology of the mesh mesh, if error occurs, this mesh can't be edited.
friend class QgsMeshVertexCirculator
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.
QgsMeshEditingError checkConsistency() const
Checks the consistency of the topological mesh and return false if there is a consistency issue.
Changes removeVertices(const QList< int > &vertices)
Removes all the vertices with index in the list vertices If vertices in linked with faces,...
Changes changeXYValue(const QList< int > &verticesIndexes, const QList< QgsPointXY > &newValues)
Changes the (X,Y) values of the vertices with indexes in vertices indexes with the values in newValue...
void reindex()
Reindexes faces and vertices, after this operation, the topological mesh can't be edited anymore and ...
QVector< int > neighborsOfFace(int faceIndex) const
Returns the indexes of neighbor faces of the face with index faceIndex.
QgsMeshEditingError facesCanBeAdded(const TopologicalFaces &topologicalFaces) const
Returns whether the faces can be added to the mesh.
bool renumber()
Renumbers the indexes of vertices and faces using the Reverse CutHill McKee Algorithm.
Changes flipEdge(int vertexIndex1, int vertexIndex2)
Flips edge (vertexIndex1, vertexIndex2) The method returns a instance of the class QgsTopologicalMesh...
QgsMeshEditingError facesCanBeRemoved(const QList< int > &facesIndexes)
Returns whether faces with index in faceIndexes can be removed/ The method an error object with type ...
QVector< int > FaceNeighbors
void reverseChanges(const Changes &changes)
Reverses the changes.
Changes addFaces(const TopologicalFaces &topologicFaces)
Adds faces topologicFaces to the topologic mesh.
Changes merge(int vertexIndex1, int vertexIndex2)
Merges faces separated by vertices with indexes vertexIndex1 and vertexIndex2 The method returns a in...
Changes removeFaces(const QList< int > &facesIndexes)
Removes faces with index in faceIndexes.
QList< int > freeVerticesIndexes() const
Returns a list of vertices are not linked to any faces.
bool edgeCanBeFlipped(int vertexIndex1, int vertexIndex2) const
Returns true if the edge can be flipped (only available for edge shared by two faces with 3 vertices)...
Changes addVertexInFace(int faceIndex, const QgsMeshVertex &vertex)
Adds a vertex in the face with index faceIndex.
bool canBeMerged(int vertexIndex1, int vertexIndex2) const
Returns true if faces separated by vertices with indexes vertexIndex1 and vertexIndex2 can be merged.
QList< int > facesAroundVertex(int vertexIndex) const
Returns the indexes of faces that are around the vertex with index vertexIndex.
bool canBeSplit(int faceIndex) const
Returns true if face with index faceIndex can be split.
Changes addFreeVertex(const QgsMeshVertex &vertex)
Adds a free vertex in the face, that is a vertex that is not included or linked with any faces.
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.
bool isVertexOnBoundary(int vertexIndex) const
Returns whether the vertex is on a boundary.
Changes splitFace(int faceIndex)
Splits face with index faceIndex The method returns a instance of the class QgsTopologicalMesh::Chang...
static TopologicalFaces createNewTopologicalFaces(const QVector< QgsMeshFace > &faces, bool uniqueSharedVertexAllowed, QgsMeshEditingError &error)
Creates new topological faces that are not yet included in the mesh.
QgsMeshVertexCirculator vertexCirculator(int vertexIndex) const
Returns a vertex circulator linked to this mesh around the vertex with index vertexIndex.
bool delaunayConditionForEdge(int vertexIndex1, int vertexIndex2)
Check if Delaunay condition holds for given edge returns true if delaunay condition holds false other...
Represent a 2-dimensional vector.
Definition qgsvector.h:31
double angle() const
Returns the angle of the vector in radians.
Definition qgsvector.h:169
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.