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