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