QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
qgsmesheditor.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsmesheditor.cpp - QgsMeshEditor
3
4 ---------------------
5 begin : 8.6.2021
6 copyright : (C) 2021 by Vincent Cloarec
7 email : vcloarec at gmail dot com
8 ***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
17#include "qgis.h"
18#include "qgsmesheditor.h"
19#include "qgsmeshdataprovider.h"
20#include "qgstriangularmesh.h"
21#include "qgsmeshlayer.h"
22#include "qgsgeometryengine.h"
24#include "qgsgeometryutils.h"
25
26#include <poly2tri.h>
27
28#include <QSet>
29
30
32 : QObject( meshLayer )
33 , mMesh( meshLayer ? meshLayer->nativeMesh() : nullptr )
34 , mTriangularMesh( meshLayer ? meshLayer->triangularMeshByLodIndex( 0 ) : nullptr )
35 , mUndoStack( meshLayer ? meshLayer->undoStack() : nullptr )
36{
37 if ( meshLayer && meshLayer->dataProvider() )
38 mMaximumVerticesPerFace = meshLayer->dataProvider()->maximumVerticesCountPerFace();
39
40 if ( meshLayer )
41 connect( mUndoStack, &QUndoStack::indexChanged, this, &QgsMeshEditor::meshEdited );
42}
43
44QgsMeshEditor::QgsMeshEditor( QgsMesh *nativeMesh, QgsTriangularMesh *triangularMesh, QObject *parent )
45 : QObject( parent )
46 , mMesh( nativeMesh )
47 , mTriangularMesh( triangularMesh )
48{
49 mUndoStack = new QUndoStack( this );
50 connect( mUndoStack, &QUndoStack::indexChanged, this, &QgsMeshEditor::meshEdited );
51}
52
54{
55 std::unique_ptr<QgsMeshDatasetGroup> zValueDatasetGroup = std::make_unique<QgsMeshVerticesElevationDatasetGroup>( tr( "vertices Z value" ), mMesh );
56 mZValueDatasetGroup = zValueDatasetGroup.get();
57 return zValueDatasetGroup.release();
58}
59
61
63{
65 mTopologicalMesh = QgsTopologicalMesh::createTopologicalMesh( mMesh, mMaximumVerticesPerFace, error );
66
68 {
69 // we check for free vertices that could be included in face here
70 // because we need the spatial index of the triangular mesh
71 const QList<int> freeVertices = mTopologicalMesh.freeVerticesIndexes();
72 for ( int vi : freeVertices )
73 {
74 if ( mTriangularMesh->faceIndexForPoint_v2( mTriangularMesh->vertices().at( vi ) ) != -1 )
75 {
77 break;
78 }
79 }
80 }
81
82 mValidFacesCount = mMesh->faceCount();
83 mValidVerticesCount = mMesh->vertexCount();
84 return error;
85}
86
88{
89 QgsMeshEditingError lastError;
90
91 while ( true )
92 {
93 lastError = initialize();
95 break;
96
97 if ( !fixError( lastError ) )
98 break;
99
100 mTriangularMesh->update( mMesh );
101 };
102
103 return lastError;
104}
105
107{
108 switch ( error.errorType )
109 {
111 return true;
112 break;
117 if ( error.elementIndex != -1 && error.elementIndex < mMesh->faceCount() )
118 {
119 mMesh->faces.removeAt( error.elementIndex );
120 return true;
121 }
122 return false;
123 break;
126 {
127 auto faceIt = mMesh->faces.begin();
128 while ( faceIt != mMesh->faces.end() )
129 {
130 if ( faceIt->contains( error.elementIndex ) )
131 faceIt = mMesh->faces.erase( faceIt );
132 else
133 ++faceIt;
134 }
135
136 if ( error.elementIndex >= 0 && error.elementIndex < mMesh->vertexCount() )
137 {
138 mMesh->vertices[error.elementIndex] = QgsMeshVertex();
139 reindex( false );
140 }
141 return true;
142 }
143 break;
144 }
145
146 return false;
147}
148
150{
151 mTriangularMesh = triangularMesh;
152}
153
155{
156 const QgsGeometry newFaceGeom = QgsMeshUtils::toGeometry( face, mTriangularMesh->vertices() );
157 std::unique_ptr<QgsGeometryEngine> geomEngine( QgsGeometry::createGeometryEngine( newFaceGeom.constGet() ) );
158 geomEngine->prepareGeometry();
159
160 QgsRectangle boundingBox = newFaceGeom.boundingBox();
161 QList<int> newFaceVerticesIndexes( face.toList() );
162 int newFaceSize = face.count();
163 QList<int> concernedFaceIndex = mTriangularMesh->nativeFaceIndexForRectangle( boundingBox );
164 if ( !concernedFaceIndex.isEmpty() )
165 {
166 // for each concerned face, we take edges and, if no common vertex with the new face,
167 // check is the edge intersects or is contained in the new face
168 for ( const int faceIndex : concernedFaceIndex )
169 {
170 const QgsMeshFace &existingFace = mMesh->faces.at( faceIndex );
171 int existingFaceSize = existingFace.count();
172 bool shareVertex = false;
173 for ( int i = 0; i < existingFaceSize; ++i )
174 {
175 if ( newFaceVerticesIndexes.contains( existingFace.at( i ) ) )
176 {
177 shareVertex = true;
178 break;
179 }
180 }
181
182 if ( shareVertex )
183 {
184 for ( int i = 0; i < existingFaceSize; ++i )
185 {
186 int index1 = existingFace.at( i );
187 int index2 = existingFace.at( ( i + 1 ) % existingFaceSize );
188 const QgsMeshVertex &v1 = mTriangularMesh->vertices().at( index1 );
189 const QgsMeshVertex &v2 = mTriangularMesh->vertices().at( index2 );
190 QgsGeometry edgeGeom = QgsGeometry( new QgsLineString( v1, v2 ) );
191
192 if ( ! newFaceVerticesIndexes.contains( index1 ) && !newFaceVerticesIndexes.contains( index2 ) )
193 {
194 // test if the edge that not contains a shared vertex intersect the entire new face
195 if ( geomEngine->intersects( edgeGeom.constGet() ) )
196 return false;
197 }
198 else
199 {
200 for ( int vi = 0; vi < newFaceVerticesIndexes.count(); ++vi )
201 {
202 int vertInNewFace1 = newFaceVerticesIndexes.at( vi );
203 int vertInNewFace2 = newFaceVerticesIndexes.at( ( vi + 1 ) % newFaceSize );
204 if ( vertInNewFace1 != index1 && vertInNewFace2 != index2 && vertInNewFace1 != index2 && vertInNewFace2 != index1 )
205 {
206 const QgsMeshVertex &nv1 = mTriangularMesh->vertices().at( vertInNewFace1 );
207 const QgsMeshVertex &nv2 = mTriangularMesh->vertices().at( vertInNewFace2 );
208 QgsGeometry newEdgeGeom = QgsGeometry( new QgsLineString( nv1, nv2 ) );
209
210 if ( newEdgeGeom.intersects( edgeGeom ) )
211 return false;
212 }
213 }
214 }
215 }
216 }
217 else
218 {
219 const QgsGeometry existingFaceGeom = QgsMeshUtils::toGeometry( existingFace, mTriangularMesh->vertices() );
220 if ( geomEngine->intersects( existingFaceGeom.constGet() ) )
221 return false;
222 }
223 }
224 }
225
226 // Then search for free vertices included in the new face
227 const QList<int> &freeVertices = freeVerticesIndexes();
228
229 for ( const int freeVertexIndex : freeVertices )
230 {
231 if ( newFaceVerticesIndexes.contains( freeVertexIndex ) )
232 continue;
233
234 const QgsMeshVertex &vertex = mTriangularMesh->vertices().at( freeVertexIndex );
235 if ( geomEngine->contains( &vertex ) )
236 return false;
237 }
238
239 return true;
240}
241
242
244{
246
247 // Prepare and check the face
248 QVector<QgsMeshFace> facesToAdd = prepareFaces( {face}, error );
249
251 return false;
252
253 // Check if there is topological error with the mesh
255 error = mTopologicalMesh.facesCanBeAdded( topologicalFaces );
256
258 return false;
259
260 // Check geometry compatibility
261 // With the topological check, we know that the new face is not included in an existing one
262 // But maybe, the new face includes or intersects existing faces or free vertices, we need to check
263 // First search for faces intersecting the bounding box of the new face.
264
265 return isFaceGeometricallyCompatible( face );
266}
267
268void QgsMeshEditor::applyEdit( QgsMeshEditor::Edit &edit )
269{
270 mTopologicalMesh.applyChanges( edit.topologicalChanges );
271 mTriangularMesh->applyChanges( edit.triangularMeshChanges );
272
273 if ( mZValueDatasetGroup &&
274 ( !edit.topologicalChanges.newVerticesZValues().isEmpty() ||
275 !edit.topologicalChanges.verticesToRemoveIndexes().isEmpty() ||
276 !edit.topologicalChanges.addedVertices().isEmpty() ) )
277 mZValueDatasetGroup->setStatisticObsolete();
278
279 updateElementsCount( edit.topologicalChanges );
280}
281
282void QgsMeshEditor::reverseEdit( QgsMeshEditor::Edit &edit )
283{
284 mTopologicalMesh.reverseChanges( edit.topologicalChanges );
285 mTriangularMesh->reverseChanges( edit.triangularMeshChanges, *mMesh );
286
287 if ( mZValueDatasetGroup &&
288 ( !edit.topologicalChanges.newVerticesZValues().isEmpty() ||
289 !edit.topologicalChanges.verticesToRemoveIndexes().isEmpty() ||
290 !edit.topologicalChanges.addedVertices().isEmpty() ) )
291 mZValueDatasetGroup->setStatisticObsolete();
292
293 updateElementsCount( edit.topologicalChanges, false );
294}
295
296void QgsMeshEditor::applyAddVertex( QgsMeshEditor::Edit &edit, const QgsMeshVertex &vertex, double tolerance )
297{
298 QgsMeshVertex vertexInTriangularCoordinate = mTriangularMesh->nativeToTriangularCoordinates( vertex );
299
300 //check if edges is closest than the tolerance from the vertex
301 int faceEdgeIntersect = -1;
302 int edgePosition = -1;
303
304 QgsTopologicalMesh::Changes topologicChanges;
305
306 if ( edgeIsClose( vertexInTriangularCoordinate, tolerance, faceEdgeIntersect, edgePosition ) )
307 {
308 topologicChanges = mTopologicalMesh.insertVertexInFacesEdge( faceEdgeIntersect, edgePosition, vertex );
309 }
310 else
311 {
312 int includingFaceIndex = mTriangularMesh->nativeFaceIndexForPoint( vertexInTriangularCoordinate );
313
314 if ( includingFaceIndex != -1 )
315 topologicChanges = mTopologicalMesh.addVertexInFace( includingFaceIndex, vertex );
316 else
317 topologicChanges = mTopologicalMesh.addFreeVertex( vertex );
318 }
319
320 applyEditOnTriangularMesh( edit, topologicChanges );
321
322 if ( mZValueDatasetGroup )
323 mZValueDatasetGroup->setStatisticObsolete();
324
325 updateElementsCount( edit.topologicalChanges );
326}
327
328bool QgsMeshEditor::applyRemoveVertexFillHole( QgsMeshEditor::Edit &edit, int vertexIndex )
329{
330 QgsTopologicalMesh::Changes changes = mTopologicalMesh.removeVertexFillHole( vertexIndex );
331
332 if ( !changes.isEmpty() )
333 {
334 applyEditOnTriangularMesh( edit, changes );
335
336 if ( mZValueDatasetGroup )
337 mZValueDatasetGroup->setStatisticObsolete();
338
339 updateElementsCount( edit.topologicalChanges );
340 return true;
341 }
342 else
343 return false;
344}
345
346void QgsMeshEditor::applyRemoveVerticesWithoutFillHole( QgsMeshEditor::Edit &edit, const QList<int> &verticesIndexes )
347{
348 applyEditOnTriangularMesh( edit, mTopologicalMesh.removeVertices( verticesIndexes ) );
349
350 if ( mZValueDatasetGroup )
351 mZValueDatasetGroup->setStatisticObsolete();
352
353 updateElementsCount( edit.topologicalChanges );
354}
355
356void QgsMeshEditor::applyAddFaces( QgsMeshEditor::Edit &edit, const QgsTopologicalMesh::TopologicalFaces &faces )
357{
358 applyEditOnTriangularMesh( edit, mTopologicalMesh.addFaces( faces ) );
359
360 updateElementsCount( edit.topologicalChanges );
361}
362
363void QgsMeshEditor::applyRemoveFaces( QgsMeshEditor::Edit &edit, const QList<int> &faceToRemoveIndex )
364{
365 applyEditOnTriangularMesh( edit, mTopologicalMesh.removeFaces( faceToRemoveIndex ) );
366
367 updateElementsCount( edit.topologicalChanges );
368}
369
370void QgsMeshEditor::applyChangeZValue( QgsMeshEditor::Edit &edit, const QList<int> &verticesIndexes, const QList<double> &newValues )
371{
372 applyEditOnTriangularMesh( edit, mTopologicalMesh.changeZValue( verticesIndexes, newValues ) );
373
374 if ( mZValueDatasetGroup )
375 mZValueDatasetGroup->setStatisticObsolete();
376}
377
378void QgsMeshEditor::applyChangeXYValue( QgsMeshEditor::Edit &edit, const QList<int> &verticesIndexes, const QList<QgsPointXY> &newValues )
379{
380 applyEditOnTriangularMesh( edit, mTopologicalMesh.changeXYValue( verticesIndexes, newValues ) );
381}
382
383void QgsMeshEditor::applyFlipEdge( QgsMeshEditor::Edit &edit, int vertexIndex1, int vertexIndex2 )
384{
385 applyEditOnTriangularMesh( edit, mTopologicalMesh.flipEdge( vertexIndex1, vertexIndex2 ) );
386
387 updateElementsCount( edit.topologicalChanges );
388}
389
390void QgsMeshEditor::applyMerge( QgsMeshEditor::Edit &edit, int vertexIndex1, int vertexIndex2 )
391{
392 applyEditOnTriangularMesh( edit, mTopologicalMesh.merge( vertexIndex1, vertexIndex2 ) );
393
394 updateElementsCount( edit.topologicalChanges );
395}
396
397void QgsMeshEditor::applySplit( QgsMeshEditor::Edit &edit, int faceIndex )
398{
399 applyEditOnTriangularMesh( edit, mTopologicalMesh.splitFace( faceIndex ) );
400
401 updateElementsCount( edit.topologicalChanges );
402}
403
404void QgsMeshEditor::applyAdvancedEdit( QgsMeshEditor::Edit &edit, QgsMeshAdvancedEditing *editing )
405{
406 applyEditOnTriangularMesh( edit, editing->apply( this ) );
407
408 updateElementsCount( edit.topologicalChanges );
409
410 if ( mZValueDatasetGroup )
411 mZValueDatasetGroup->setStatisticObsolete();
412}
413
414void QgsMeshEditor::applyEditOnTriangularMesh( QgsMeshEditor::Edit &edit, const QgsTopologicalMesh::Changes &topologicChanges )
415{
416 QgsTriangularMesh::Changes triangularChanges( topologicChanges, *mMesh );
417 mTriangularMesh->applyChanges( triangularChanges );
418
419 edit.topologicalChanges = topologicChanges;
420 edit.triangularMeshChanges = triangularChanges;
421}
422
423void QgsMeshEditor::updateElementsCount( const QgsTopologicalMesh::Changes &changes, bool apply )
424{
425 if ( apply )
426 {
427 mValidFacesCount += changes.addedFaces().count() - changes.removedFaces().count();
428 mValidVerticesCount += changes.addedVertices().count() - changes.verticesToRemoveIndexes().count();
429 }
430 else
431 {
432 //reverse
433 mValidFacesCount -= changes.addedFaces().count() - changes.removedFaces().count();
434 mValidVerticesCount -= changes.addedVertices().count() - changes.verticesToRemoveIndexes().count();
435 }
436}
437
439{
440 error = mTopologicalMesh.checkConsistency();
441 switch ( error.errorType )
442 {
444 break;
451 return false;
452 }
453
454 if ( mTriangularMesh->vertices().count() != mMesh->vertexCount() )
455 return false;
456
457 if ( mTriangularMesh->faceCentroids().count() != mMesh->faceCount() )
458 return false;
459
460 return true;
461}
462
463bool QgsMeshEditor::edgeIsClose( QgsPointXY point, double tolerance, int &faceIndex, int &edgePosition )
464{
465 QgsRectangle toleranceZone( point.x() - tolerance,
466 point.y() - tolerance,
467 point.x() + tolerance,
468 point.y() + tolerance );
469
470 edgePosition = -1;
471 double minDist = std::numeric_limits<double>::max();
472 const QList<int> &nativeFaces = mTriangularMesh->nativeFaceIndexForRectangle( toleranceZone );
473 double epsilon = std::numeric_limits<double>::epsilon() * tolerance;
474 for ( const int nativeFaceIndex : nativeFaces )
475 {
476 const QgsMeshFace &face = mMesh->face( nativeFaceIndex );
477 const int faceSize = face.size();
478 for ( int i = 0; i < faceSize; ++i )
479 {
480 const QgsMeshVertex &v1 = mTriangularMesh->vertices().at( face.at( i ) );
481 const QgsMeshVertex &v2 = mTriangularMesh->vertices().at( face.at( ( i + 1 ) % faceSize ) );
482
483 double mx, my;
484 double dist = sqrt( QgsGeometryUtils::sqrDistToLine( point.x(),
485 point.y(),
486 v1.x(),
487 v1.y(),
488 v2.x(),
489 v2.y(),
490 mx,
491 my,
492 epsilon ) );
493
494 if ( dist < tolerance && dist < minDist )
495 {
496 faceIndex = nativeFaceIndex;
497 edgePosition = i;
498 minDist = dist;
499 }
500 }
501 }
502
503 if ( edgePosition != -1 )
504 return true;
505
506 return false;
507
508}
509
511{
512 return mValidFacesCount;
513}
514
516{
517 return mValidVerticesCount;
518}
519
521{
522 return mMaximumVerticesPerFace;
523}
524
525QgsMeshEditingError QgsMeshEditor::removeFaces( const QList<int> &facesToRemove )
526{
527 QgsMeshEditingError error = mTopologicalMesh.facesCanBeRemoved( facesToRemove );
529 return error;
530
531 mUndoStack->push( new QgsMeshLayerUndoCommandRemoveFaces( this, facesToRemove ) );
532
533 return error;
534}
535
536bool QgsMeshEditor::edgeCanBeFlipped( int vertexIndex1, int vertexIndex2 ) const
537{
538 return mTopologicalMesh.edgeCanBeFlipped( vertexIndex1, vertexIndex2 );
539}
540
541void QgsMeshEditor::flipEdge( int vertexIndex1, int vertexIndex2 )
542{
543 if ( !edgeCanBeFlipped( vertexIndex1, vertexIndex2 ) )
544 return;
545
546 mUndoStack->push( new QgsMeshLayerUndoCommandFlipEdge( this, vertexIndex1, vertexIndex2 ) );
547}
548
549bool QgsMeshEditor::canBeMerged( int vertexIndex1, int vertexIndex2 ) const
550{
551 return mTopologicalMesh.canBeMerged( vertexIndex1, vertexIndex2 );
552}
553
554void QgsMeshEditor::merge( int vertexIndex1, int vertexIndex2 )
555{
556 if ( !canBeMerged( vertexIndex1, vertexIndex2 ) )
557 return;
558
559 mUndoStack->push( new QgsMeshLayerUndoCommandMerge( this, vertexIndex1, vertexIndex2 ) );
560}
561
562bool QgsMeshEditor::faceCanBeSplit( int faceIndex ) const
563{
564 return mTopologicalMesh.canBeSplit( faceIndex );
565}
566
567int QgsMeshEditor::splitFaces( const QList<int> &faceIndexes )
568{
569 QList<int> faceIndexesSplittable;
570
571 for ( const int faceIndex : faceIndexes )
572 if ( faceCanBeSplit( faceIndex ) )
573 faceIndexesSplittable.append( faceIndex );
574
575 if ( faceIndexesSplittable.isEmpty() )
576 return 0;
577
578 mUndoStack->push( new QgsMeshLayerUndoCommandSplitFaces( this, faceIndexesSplittable ) );
579
580 return faceIndexesSplittable.count();
581}
582
583QVector<QgsMeshFace> QgsMeshEditor::prepareFaces( const QVector<QgsMeshFace> &faces, QgsMeshEditingError &error )
584{
585 QVector<QgsMeshFace> treatedFaces = faces;
586
587 // here we could add later some filters, for example, removing faces intersecting with existing one
588
589 for ( int i = 0; i < treatedFaces.count(); ++i )
590 {
591 QgsMeshFace &face = treatedFaces[i];
592 if ( mMaximumVerticesPerFace != 0 && face.count() > mMaximumVerticesPerFace )
593 {
595 break;
596 }
597
598 error = QgsTopologicalMesh::counterClockwiseFaces( face, mMesh );
600 break;
601 }
602
603 return treatedFaces;
604}
605
606QgsMeshEditingError QgsMeshEditor::addFaces( const QVector<QVector<int> > &faces )
607{
609 QVector<QgsMeshFace> facesToAdd = prepareFaces( faces, error );
610
612 return error;
613
615
616 error = mTopologicalMesh.facesCanBeAdded( topologicalFaces );
617
619 return error;
620
621 mUndoStack->push( new QgsMeshLayerUndoCommandAddFaces( this, topologicalFaces ) );
622
623 return error;
624}
625
626QgsMeshEditingError QgsMeshEditor::addFace( const QVector<int> &vertexIndexes )
627{
628 return addFaces( {vertexIndexes} );
629}
630
631int QgsMeshEditor::addVertices( const QVector<QgsMeshVertex> &vertices, double tolerance )
632{
633 QVector<QgsMeshVertex> verticesInLayerCoordinate( vertices.count() );
634 int ignoredVertex = 0;
635 for ( int i = 0; i < vertices.count(); ++i )
636 {
637 const QgsPointXY &pointInTriangularMesh = vertices.at( i );
638 bool isTooClose = false;
639 int triangleIndex = mTriangularMesh->faceIndexForPoint_v2( pointInTriangularMesh );
640 if ( triangleIndex != -1 )
641 {
642 const QgsMeshFace face = mTriangularMesh->triangles().at( triangleIndex );
643 for ( int j = 0; j < 3; ++j )
644 {
645 const QgsPointXY &facePoint = mTriangularMesh->vertices().at( face.at( j ) );
646 double dist = pointInTriangularMesh.distance( facePoint );
647 if ( dist < tolerance )
648 {
649 isTooClose = true;
650 break;
651 }
652 }
653 }
654
655 if ( !isTooClose )
656 verticesInLayerCoordinate[i] = mTriangularMesh->triangularToNativeCoordinates( vertices.at( i ) );
657 else
658 verticesInLayerCoordinate[i] = QgsMeshVertex();
659
660 if ( verticesInLayerCoordinate.at( i ).isEmpty() )
661 ignoredVertex++;
662 }
663
664 if ( ignoredVertex < vertices.count() )
665 {
666 mUndoStack->push( new QgsMeshLayerUndoCommandAddVertices( this, verticesInLayerCoordinate, tolerance ) );
667 }
668
669 int effectivlyAddedVertex = vertices.count() - ignoredVertex;
670
671 return effectivlyAddedVertex;
672}
673
674int QgsMeshEditor::addPointsAsVertices( const QVector<QgsPoint> &point, double tolerance )
675{
676 return addVertices( point, tolerance );
677}
678
679QgsMeshEditingError QgsMeshEditor::removeVerticesWithoutFillHoles( const QList<int> &verticesToRemoveIndexes )
680{
682
683 QList<int> verticesIndexes = verticesToRemoveIndexes;
684
685 QSet<int> concernedNativeFaces;
686 for ( const int vi : std::as_const( verticesIndexes ) )
687 {
688 const QList<int> faces = mTopologicalMesh.facesAroundVertex( vi );
689 concernedNativeFaces.unite( QSet< int >( faces.begin(), faces.end() ) );
690 }
691
692 error = mTopologicalMesh.facesCanBeRemoved( concernedNativeFaces.values() );
693
695 return error;
696
697 mUndoStack->push( new QgsMeshLayerUndoCommandRemoveVerticesWithoutFillHoles( this, verticesIndexes ) );
698 return error;
699}
700
701QList<int> QgsMeshEditor::removeVerticesFillHoles( const QList<int> &verticesToRemoveIndexes )
702{
703 QList<int> remainingVertices;
704 mUndoStack->push( new QgsMeshLayerUndoCommandRemoveVerticesFillHoles( this, verticesToRemoveIndexes, &remainingVertices ) );
705
706 return remainingVertices;
707}
708
709
710void QgsMeshEditor::changeZValues( const QList<int> &verticesIndexes, const QList<double> &newZValues )
711{
712 mUndoStack->push( new QgsMeshLayerUndoCommandChangeZValue( this, verticesIndexes, newZValues ) );
713}
714
715bool QgsMeshEditor::canBeTransformed( const QList<int> &facesToCheck, const std::function<const QgsMeshVertex( int )> &transformFunction ) const
716{
717 for ( const int faceIndex : facesToCheck )
718 {
719 const QgsMeshFace &face = mMesh->face( faceIndex );
720 int faceSize = face.count();
721 QVector<QgsPointXY> pointsInTriangularMeshCoordinate( faceSize );
722 QVector<QgsPointXY> points( faceSize );
723 for ( int i = 0; i < faceSize; ++i )
724 {
725 int ip0 = face[i];
726 int ip1 = face[( i + 1 ) % faceSize];
727 int ip2 = face[( i + 2 ) % faceSize];
728
729 QgsMeshVertex p0 = transformFunction( ip0 );
730 QgsMeshVertex p1 = transformFunction( ip1 );
731 QgsMeshVertex p2 = transformFunction( ip2 );
732
733 double ux = p0.x() - p1.x();
734 double uy = p0.y() - p1.y();
735 double vx = p2.x() - p1.x();
736 double vy = p2.y() - p1.y();
737
738 double crossProduct = ux * vy - uy * vx;
739 if ( crossProduct >= 0 ) //if cross product>0, we have two edges clockwise
740 return false;
741 pointsInTriangularMeshCoordinate[i] = mTriangularMesh->nativeToTriangularCoordinates( p0 );
742 points[i] = p0;
743 }
744
745 const QgsGeometry &deformedFace = QgsGeometry::fromPolygonXY( {points} );
746
747 // now test if the deformed face contain something else
748 QList<int> otherFaceIndexes =
749 mTriangularMesh->nativeFaceIndexForRectangle( QgsGeometry::fromPolygonXY( {pointsInTriangularMeshCoordinate} ).boundingBox() );
750
751 for ( const int otherFaceIndex : otherFaceIndexes )
752 {
753 const QgsMeshFace &otherFace = mMesh->face( otherFaceIndex );
754 int existingFaceSize = otherFace.count();
755 bool shareVertex = false;
756 for ( int i = 0; i < existingFaceSize; ++i )
757 {
758 if ( face.contains( otherFace.at( i ) ) )
759 {
760 shareVertex = true;
761 break;
762 }
763 }
764 if ( shareVertex )
765 {
766 //only test the edge that not contains a shared vertex
767 for ( int i = 0; i < existingFaceSize; ++i )
768 {
769 int index1 = otherFace.at( i );
770 int index2 = otherFace.at( ( i + 1 ) % existingFaceSize );
771 if ( ! face.contains( index1 ) && !face.contains( index2 ) )
772 {
773 const QgsPointXY &v1 = transformFunction( index1 );
774 const QgsPointXY &v2 = transformFunction( index2 );
775 QgsGeometry edgeGeom = QgsGeometry::fromPolylineXY( { v1, v2} );
776 if ( deformedFace.intersects( edgeGeom ) )
777 return false;
778 }
779 }
780 }
781 else
782 {
783 QVector<QgsPointXY> otherPoints( existingFaceSize );
784 for ( int i = 0; i < existingFaceSize; ++i )
785 otherPoints[i] = transformFunction( otherFace.at( i ) );
786 const QgsGeometry existingFaceGeom = QgsGeometry::fromPolygonXY( {otherPoints } );
787 if ( deformedFace.intersects( existingFaceGeom ) )
788 return false;
789 }
790 }
791
792 const QList<int> freeVerticesIndex = freeVerticesIndexes();
793 for ( const int vertexIndex : freeVerticesIndex )
794 {
795 const QgsPointXY &mapPoint = transformFunction( vertexIndex ); //free vertices can be transformed
796 if ( deformedFace.contains( &mapPoint ) )
797 return false;
798 }
799 }
800
801 // free vertices
802 const QList<int> freeVerticesIndex = freeVerticesIndexes();
803 for ( const int vertexIndex : freeVerticesIndex )
804 {
805 const QgsMeshVertex &newFreeVertexPosition = transformFunction( vertexIndex ); // transformed free vertex
806 const QgsMeshVertex pointInTriangularCoord = mTriangularMesh->nativeToTriangularCoordinates( newFreeVertexPosition );
807 const int originalIncludingFace = mTriangularMesh->nativeFaceIndexForPoint( pointInTriangularCoord );
808
809 if ( originalIncludingFace != -1 )
810 {
811 // That means two things: the free vertex is moved AND is included in a face before transform
812 // Before returning false, we need to check if the vertex is still in the face after transform
813 const QgsMeshFace &face = mMesh->face( originalIncludingFace );
814 int faceSize = face.count();
815 QVector<QgsPointXY> points( faceSize );
816 for ( int i = 0; i < faceSize; ++i )
817 points[i] = transformFunction( face.at( i ) );
818
819 const QgsGeometry &deformedFace = QgsGeometry::fromPolygonXY( {points} );
820 const QgsPointXY ptXY( newFreeVertexPosition );
821 if ( deformedFace.contains( &ptXY ) )
822 return false;
823 }
824 }
825
826 return true;
827}
828
829void QgsMeshEditor::changeXYValues( const QList<int> &verticesIndexes, const QList<QgsPointXY> &newValues )
830{
831 // TODO : implement a check if it is possible to change the (x,y) values. For now, this check is made in the APP part
832 mUndoStack->push( new QgsMeshLayerUndoCommandChangeXYValue( this, verticesIndexes, newValues ) );
833}
834
835void QgsMeshEditor::changeCoordinates( const QList<int> &verticesIndexes, const QList<QgsPoint> &newCoordinates )
836{
837 mUndoStack->push( new QgsMeshLayerUndoCommandChangeCoordinates( this, verticesIndexes, newCoordinates ) );
838}
839
841{
842 mUndoStack->push( new QgsMeshLayerUndoCommandAdvancedEditing( this, editing ) );
843}
844
846{
847 mTopologicalMesh.reindex();
848 mUndoStack->clear();
849}
850
852 : mMeshEditor( meshEditor )
853{
854}
855
857{
858 if ( mMeshEditor.isNull() )
859 return;
860
861 for ( int i = mEdits.count() - 1; i >= 0; --i )
862 mMeshEditor->reverseEdit( mEdits[i] );
863}
864
866{
867 if ( mMeshEditor.isNull() )
868 return;
869
870 for ( QgsMeshEditor::Edit &edit : mEdits )
871 mMeshEditor->applyEdit( edit );
872}
873
874QgsMeshLayerUndoCommandAddVertices::QgsMeshLayerUndoCommandAddVertices( QgsMeshEditor *meshEditor, const QVector<QgsMeshVertex> &vertices, double tolerance )
875 : QgsMeshLayerUndoCommandMeshEdit( meshEditor )
876 , mVertices( vertices )
877 , mTolerance( tolerance )
878{
879 setText( QObject::tr( "Add %n vertices", nullptr, mVertices.count() ) );
880}
881
883{
884 if ( !mVertices.isEmpty() )
885 {
886 for ( int i = 0; i < mVertices.count(); ++i )
887 {
888 const QgsMeshVertex &vertex = mVertices.at( i );
889 if ( vertex.isEmpty() )
890 continue;
891 QgsMeshEditor::Edit edit;
892 mMeshEditor->applyAddVertex( edit, vertex, mTolerance );
893 mEdits.append( edit );
894 }
895 mVertices.clear(); //not needed anymore, changes are store in mEdits
896 }
897 else
898 {
899 for ( QgsMeshEditor::Edit &edit : mEdits )
900 mMeshEditor->applyEdit( edit );
901 }
902}
903
905 QgsMeshEditor *meshEditor,
906 const QList<int> &verticesToRemoveIndexes,
907 QList<int> *remainingVerticesPointer )
908 : QgsMeshLayerUndoCommandMeshEdit( meshEditor )
909 , mVerticesToRemoveIndexes( verticesToRemoveIndexes )
910 , mRemainingVerticesPointer( remainingVerticesPointer )
911{
912 setText( QObject::tr( "Remove %n vertices filling holes", nullptr, verticesToRemoveIndexes.count() ) );
913}
914
916{
917 int initialVertexCount = mVerticesToRemoveIndexes.count();
918 if ( !mVerticesToRemoveIndexes.isEmpty() )
919 {
920 QgsMeshEditor::Edit edit;
921 QList<int> vertexToRetry;
922 while ( !mVerticesToRemoveIndexes.isEmpty() )
923 {
924 // try again and again until there is no vertices to remove anymore or nothing is removed.
925 for ( const int &vertex : std::as_const( mVerticesToRemoveIndexes ) )
926 {
927 if ( mMeshEditor->applyRemoveVertexFillHole( edit, vertex ) )
928 mEdits.append( edit );
929 else
930 vertexToRetry.append( vertex );
931 }
932
933 if ( vertexToRetry.count() == mVerticesToRemoveIndexes.count() )
934 break;
935 else
936 mVerticesToRemoveIndexes = vertexToRetry;
937 }
938
939 if ( initialVertexCount == mVerticesToRemoveIndexes.count() )
940 setObsolete( true );
941
942 if ( mRemainingVerticesPointer != nullptr )
943 *mRemainingVerticesPointer = mVerticesToRemoveIndexes;
944
945 mRemainingVerticesPointer = nullptr;
946
947 mVerticesToRemoveIndexes.clear(); //not needed anymore, changes are store in mEdits
948 }
949 else
950 {
951 for ( QgsMeshEditor::Edit &edit : mEdits )
952 mMeshEditor->applyEdit( edit );
953 }
954}
955
956
958 QgsMeshEditor *meshEditor,
959 const QList<int> &verticesToRemoveIndexes )
960 : QgsMeshLayerUndoCommandMeshEdit( meshEditor )
961 , mVerticesToRemoveIndexes( verticesToRemoveIndexes )
962{
963 setText( QObject::tr( "Remove %n vertices without filling holes", nullptr, verticesToRemoveIndexes.count() ) ) ;
964}
965
967{
968 if ( !mVerticesToRemoveIndexes.isEmpty() )
969 {
970 QgsMeshEditor::Edit edit;
971
972 mMeshEditor->applyRemoveVerticesWithoutFillHole( edit, mVerticesToRemoveIndexes );
973 mEdits.append( edit );
974
975 mVerticesToRemoveIndexes.clear(); //not needed anymore, changes are store in mEdits
976 }
977 else
978 {
979 for ( QgsMeshEditor::Edit &edit : mEdits )
980 mMeshEditor->applyEdit( edit );
981 }
982}
983
985 : QgsMeshLayerUndoCommandMeshEdit( meshEditor )
986 , mFaces( faces )
987{
988 setText( QObject::tr( "Add %n face(s)", nullptr, faces.meshFaces().count() ) );
989}
990
992{
993 if ( !mFaces.meshFaces().isEmpty() )
994 {
995 QgsMeshEditor::Edit edit;
996 mMeshEditor->applyAddFaces( edit, mFaces );
997 mEdits.append( edit );
998
999 mFaces.clear(); //not needed anymore, now changes are store in edit
1000 }
1001 else
1002 {
1003 for ( QgsMeshEditor::Edit &edit : mEdits )
1004 mMeshEditor->applyEdit( edit );
1005 }
1006}
1007
1009 : QgsMeshLayerUndoCommandMeshEdit( meshEditor )
1010 , mfacesToRemoveIndexes( facesToRemoveIndexes )
1011{
1012 setText( QObject::tr( "Remove %n face(s)", nullptr, facesToRemoveIndexes.count() ) );
1013}
1014
1016{
1017 if ( !mfacesToRemoveIndexes.isEmpty() )
1018 {
1019 QgsMeshEditor::Edit edit;
1020 mMeshEditor->applyRemoveFaces( edit, mfacesToRemoveIndexes );
1021 mEdits.append( edit );
1022
1023 mfacesToRemoveIndexes.clear(); //not needed anymore, now changes are store in edit
1024 }
1025 else
1026 {
1027 for ( QgsMeshEditor::Edit &edit : mEdits )
1028 mMeshEditor->applyEdit( edit );
1029 }
1030}
1031
1032QgsMeshEditingError::QgsMeshEditingError(): errorType( Qgis::MeshEditingErrorType::NoError ), elementIndex( -1 ) {}
1033
1034QgsMeshEditingError::QgsMeshEditingError( Qgis::MeshEditingErrorType type, int elementIndex ): errorType( type ), elementIndex( elementIndex ) {}
1035
1037{
1038 return mTriangularMesh->nativeExtent();
1039}
1040
1042{
1043 if ( mUndoStack )
1044 return !mUndoStack->isClean();
1045
1046 return false;
1047}
1048
1049bool QgsMeshEditor::reindex( bool renumbering )
1050{
1051 mTopologicalMesh.reindex();
1052 mUndoStack->clear();
1054 mValidFacesCount = mMesh->faceCount();
1055 mValidVerticesCount = mMesh->vertexCount();
1056
1058 return false;
1059
1060 if ( renumbering )
1061 {
1062 if ( !mTopologicalMesh.renumber() )
1063 return false;
1064
1065 QgsMeshEditingError error;
1066 mTopologicalMesh = QgsTopologicalMesh::createTopologicalMesh( mMesh, mMaximumVerticesPerFace, error );
1067 mValidFacesCount = mMesh->faceCount();
1068 mValidVerticesCount = mMesh->vertexCount();
1070 }
1071 else
1072 return true;
1073}
1074
1076{
1077 return mTopologicalMesh.freeVerticesIndexes();
1078}
1079
1080bool QgsMeshEditor::isVertexOnBoundary( int vertexIndex ) const
1081{
1082 return mTopologicalMesh.isVertexOnBoundary( vertexIndex );
1083}
1084
1085bool QgsMeshEditor::isVertexFree( int vertexIndex ) const
1086{
1087 return mTopologicalMesh.isVertexFree( vertexIndex );
1088}
1089
1091{
1092 return mTopologicalMesh.vertexCirculator( vertexIndex );
1093}
1094
1096{
1097 return mTopologicalMesh;
1098}
1099
1101{
1102 return mTriangularMesh;
1103}
1104
1105QgsMeshLayerUndoCommandChangeZValue::QgsMeshLayerUndoCommandChangeZValue( QgsMeshEditor *meshEditor, const QList<int> &verticesIndexes, const QList<double> &newValues )
1106 : QgsMeshLayerUndoCommandMeshEdit( meshEditor )
1107 , mVerticesIndexes( verticesIndexes )
1108 , mNewValues( newValues )
1109{
1110 setText( QObject::tr( "Change %n vertices Z Value", nullptr, verticesIndexes.count() ) );
1111}
1112
1114{
1115 if ( !mVerticesIndexes.isEmpty() )
1116 {
1117 QgsMeshEditor::Edit edit;
1118 mMeshEditor->applyChangeZValue( edit, mVerticesIndexes, mNewValues );
1119 mEdits.append( edit );
1120 mVerticesIndexes.clear();
1121 mNewValues.clear();
1122 }
1123 else
1124 {
1125 for ( QgsMeshEditor::Edit &edit : mEdits )
1126 mMeshEditor->applyEdit( edit );
1127 }
1128}
1129
1130QgsMeshLayerUndoCommandChangeXYValue::QgsMeshLayerUndoCommandChangeXYValue( QgsMeshEditor *meshEditor, const QList<int> &verticesIndexes, const QList<QgsPointXY> &newValues )
1131 : QgsMeshLayerUndoCommandMeshEdit( meshEditor )
1132 , mVerticesIndexes( verticesIndexes )
1133 , mNewValues( newValues )
1134{
1135 setText( QObject::tr( "Move %n vertices", nullptr, verticesIndexes.count() ) );
1136}
1137
1139{
1140 if ( !mVerticesIndexes.isEmpty() )
1141 {
1142 QgsMeshEditor::Edit edit;
1143 mMeshEditor->applyChangeXYValue( edit, mVerticesIndexes, mNewValues );
1144 mEdits.append( edit );
1145 mVerticesIndexes.clear();
1146 mNewValues.clear();
1147 }
1148 else
1149 {
1150 for ( QgsMeshEditor::Edit &edit : mEdits )
1151 mMeshEditor->applyEdit( edit );
1152 }
1153}
1154
1155
1156QgsMeshLayerUndoCommandChangeCoordinates::QgsMeshLayerUndoCommandChangeCoordinates( QgsMeshEditor *meshEditor, const QList<int> &verticesIndexes, const QList<QgsPoint> &newCoordinates )
1157 : QgsMeshLayerUndoCommandMeshEdit( meshEditor )
1158 , mVerticesIndexes( verticesIndexes )
1159 , mNewCoordinates( newCoordinates )
1160{
1161 setText( QObject::tr( "Transform %n vertices coordinates", nullptr, verticesIndexes.count() ) );
1162}
1163
1165{
1166 if ( !mVerticesIndexes.isEmpty() )
1167 {
1168 QgsMeshEditor::Edit editXY;
1169 QList<QgsPointXY> newXY;
1170 newXY.reserve( mNewCoordinates.count() );
1171 QgsMeshEditor::Edit editZ;
1172 QList<double> newZ;
1173 newZ.reserve( mNewCoordinates.count() );
1174
1175 for ( const QgsPoint &pt : std::as_const( mNewCoordinates ) )
1176 {
1177 newXY.append( pt );
1178 newZ.append( pt.z() );
1179 }
1180
1181 mMeshEditor->applyChangeXYValue( editXY, mVerticesIndexes, newXY );
1182 mEdits.append( editXY );
1183 mMeshEditor->applyChangeZValue( editZ, mVerticesIndexes, newZ );
1184 mEdits.append( editZ );
1185 mVerticesIndexes.clear();
1186 mNewCoordinates.clear();
1187 }
1188 else
1189 {
1190 for ( QgsMeshEditor::Edit &edit : mEdits )
1191 mMeshEditor->applyEdit( edit );
1192 }
1193}
1194
1195
1196
1198 : QgsMeshLayerUndoCommandMeshEdit( meshEditor )
1199 , mVertexIndex1( vertexIndex1 )
1200 , mVertexIndex2( vertexIndex2 )
1201{
1202 setText( QObject::tr( "Flip edge" ) );
1203}
1204
1206{
1207 if ( mVertexIndex1 >= 0 && mVertexIndex2 >= 0 )
1208 {
1209 QgsMeshEditor::Edit edit;
1210 mMeshEditor->applyFlipEdge( edit, mVertexIndex1, mVertexIndex2 );
1211 mEdits.append( edit );
1212 mVertexIndex1 = -1;
1213 mVertexIndex2 = -1;
1214 }
1215 else
1216 {
1217 for ( QgsMeshEditor::Edit &edit : mEdits )
1218 mMeshEditor->applyEdit( edit );
1219 }
1220}
1221
1222QgsMeshLayerUndoCommandMerge::QgsMeshLayerUndoCommandMerge( QgsMeshEditor *meshEditor, int vertexIndex1, int vertexIndex2 )
1223 : QgsMeshLayerUndoCommandMeshEdit( meshEditor )
1224 , mVertexIndex1( vertexIndex1 )
1225 , mVertexIndex2( vertexIndex2 )
1226{
1227 setText( QObject::tr( "Merge faces" ) );
1228}
1229
1231{
1232 if ( mVertexIndex1 >= 0 && mVertexIndex2 >= 0 )
1233 {
1234 QgsMeshEditor::Edit edit;
1235 mMeshEditor->applyMerge( edit, mVertexIndex1, mVertexIndex2 );
1236 mEdits.append( edit );
1237 mVertexIndex1 = -1;
1238 mVertexIndex2 = -1;
1239 }
1240 else
1241 {
1242 for ( QgsMeshEditor::Edit &edit : mEdits )
1243 mMeshEditor->applyEdit( edit );
1244 }
1245}
1246
1248 : QgsMeshLayerUndoCommandMeshEdit( meshEditor )
1249 , mFaceIndexes( faceIndexes )
1250{
1251 setText( QObject::tr( "Split %n face(s)", nullptr, faceIndexes.count() ) );
1252}
1253
1255{
1256 if ( !mFaceIndexes.isEmpty() )
1257 {
1258 for ( int faceIndex : std::as_const( mFaceIndexes ) )
1259 {
1260 QgsMeshEditor::Edit edit;
1261 mMeshEditor->applySplit( edit, faceIndex );
1262 mEdits.append( edit );
1263 }
1264 mFaceIndexes.clear();
1265 }
1266 else
1267 {
1268 for ( QgsMeshEditor::Edit &edit : mEdits )
1269 mMeshEditor->applyEdit( edit );
1270 }
1271}
1272
1274 : QgsMeshLayerUndoCommandMeshEdit( meshEditor )
1275 , mAdvancedEditing( advancdEdit )
1276{
1277 setText( advancdEdit->text() );
1278}
1279
1281{
1282 if ( mAdvancedEditing )
1283 {
1284 QgsMeshEditor::Edit edit;
1285 while ( !mAdvancedEditing->isFinished() )
1286 {
1287 mMeshEditor->applyAdvancedEdit( edit, mAdvancedEditing );
1288 mEdits.append( edit );
1289 }
1290
1291 mAdvancedEditing = nullptr;
1292 }
1293 else
1294 {
1295 for ( QgsMeshEditor::Edit &edit : mEdits )
1296 mMeshEditor->applyEdit( edit );
1297 }
1298}
The Qgis class provides global constants for use throughout the application.
Definition: qgis.h:72
MeshEditingErrorType
Flags which control behavior of raster renderers.
Definition: qgis.h:755
@ TooManyVerticesInFace
A face has more vertices than the maximum number supported per face.
@ InvalidFace
An error occurs due to an invalid face (for example, vertex indexes are unordered)
@ UniqueSharedVertex
A least two faces share only one vertices.
@ ManifoldFace
ManifoldFace.
@ InvalidVertex
An error occurs due to an invalid vertex (for example, vertex index is out of range the available ver...
@ FlatFace
A flat face is present.
static double sqrDistToLine(double ptX, double ptY, double x1, double y1, double x2, double y2, double &minDistX, double &minDistY, double epsilon) SIP_HOLDGIL
Returns the squared distance between a point and a line.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:164
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
static QgsGeometry fromPolylineXY(const QgsPolylineXY &polyline)
Creates a new LineString geometry from a list of QgsPointXY points.
bool contains(const QgsPointXY *p) const
Returns true if the geometry contains the point p.
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine representing the specified geometry.
static QgsGeometry fromPolygonXY(const QgsPolygonXY &polygon)
Creates a new geometry from a QgsPolygonXY.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
bool intersects(const QgsRectangle &rectangle) const
Returns true if this geometry exactly intersects with a rectangle.
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:45
Abstract class that can be derived to implement advanced editing on mesh.
virtual QgsTopologicalMesh::Changes apply(QgsMeshEditor *meshEditor)=0
Apply a change to mesh Editor.
virtual bool isFinished() const
Returns whether the advanced edit is finished, if not, this edit has to be applied again with QgsMesh...
virtual QString text() const
Returns a short text string describing what this advanced edit does. Default implementation return a ...
virtual int maximumVerticesCountPerFace() const
Returns the maximum number of vertices per face supported by the current mesh, if returns 0,...
Abstract class that represents a dataset group.
void setStatisticObsolete() const
Sets statistic obsolete, that means statistic will be recalculated when requested.
Class that represents an error during mesh editing.
Definition: qgsmesheditor.h:43
Qgis::MeshEditingErrorType errorType
Definition: qgsmesheditor.h:52
QgsMeshEditingError()
Constructor of the default error, that is NoError.
Class that makes edit operation on a mesh.
Definition: qgsmesheditor.h:68
friend class QgsMeshLayerUndoCommandSplitFaces
QgsMeshEditingError initialize()
Initializes the mesh editor and returns first error if the internal native mesh has topological error...
friend class QgsMeshLayerUndoCommandMerge
void changeXYValues(const QList< int > &verticesIndexes, const QList< QgsPointXY > &newValues)
Changes the (X,Y) coordinates values of the vertices with indexes in verticesIndexes with the values ...
int validFacesCount() const
Returns the count of valid faces, that is non void faces in the mesh.
friend class QgsMeshLayerUndoCommandRemoveVerticesWithoutFillHoles
QgsMeshEditingError removeFaces(const QList< int > &facesToRemove)
Removes faces faces to the mesh, returns topological errors if this operation fails (operation is not...
QgsMeshEditingError addFaces(const QVector< QgsMeshFace > &faces)
Adds faces faces to the mesh, returns topological errors if this operation fails (operation is not re...
bool checkConsistency(QgsMeshEditingError &error) const
Return true if the edited mesh is consistent.
QList< int > removeVerticesFillHoles(const QList< int > &verticesToRemoveIndexes)
Removes vertices with indexes in the list verticesToRemoveIndexes in the mesh the surrounding faces A...
void flipEdge(int vertexIndex1, int vertexIndex2)
Flips edge (vertexIndex1, vertexIndex2)
QgsRectangle extent() const
Returns the extent of the edited mesh.
friend class QgsMeshLayerUndoCommandAddVertices
bool faceCanBeSplit(int faceIndex) const
Returns true if face with index faceIndex can be split.
QgsMeshEditingError initializeWithErrorsFix()
Initializes the mesh editor.
int maximumVerticesPerFace() const
Returns the maximum count of vertices per face that the mesh can support.
QgsMeshEditingError addFace(const QVector< int > &vertexIndexes)
Adds a face face to the mesh with vertex indexes vertexIndexes, returns topological errors if this op...
QgsMeshEditor(QgsMeshLayer *meshLayer)
Constructor with a specified layer meshLayer.
void merge(int vertexIndex1, int vertexIndex2)
Merges faces separated by vertices with indexes vertexIndex1 and vertexIndex2.
bool edgeIsClose(QgsPointXY point, double tolerance, int &faceIndex, int &edgePosition)
Returns true if an edge of face is closest than the tolerance from the point in triangular mesh coord...
QgsMeshEditingError removeVerticesWithoutFillHoles(const QList< int > &verticesToRemoveIndexes)
Removes vertices with indexes in the list verticesToRemoveIndexes in the mesh removing the surroundin...
QList< int > freeVerticesIndexes() const
Returns all the free vertices indexes.
friend class QgsMeshLayerUndoCommandChangeXYValue
bool isVertexOnBoundary(int vertexIndex) const
Returns whether the vertex with index vertexIndex is on a boundary.
friend class QgsMeshLayerUndoCommandFlipEdge
void changeCoordinates(const QList< int > &verticesIndexes, const QList< QgsPoint > &newCoordinates)
Changes the (X,Y,Z) coordinates values of the vertices with indexes in vertices indexes with the valu...
void stopEditing()
Stops editing.
bool isFaceGeometricallyCompatible(const QgsMeshFace &face)
Returns true if the face does not intersect or contains any other elements (faces or vertices) The to...
friend class QgsMeshLayerUndoCommandAdvancedEditing
bool canBeMerged(int vertexIndex1, int vertexIndex2) const
Returns true if faces separated by vertices with indexes vertexIndex1 and vertexIndex2 can be merged.
friend class QgsMeshLayerUndoCommandAddFaces
friend class QgsMeshLayerUndoCommandChangeCoordinates
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)
int splitFaces(const QList< int > &faceIndexes)
Splits faces with index faceIndexes.
QgsMeshVertexCirculator vertexCirculator(int vertexIndex) const
Returns a vertex circulator linked to this mesh around the vertex with index vertexIndex.
void meshEdited()
Emitted when the mesh is edited.
void changeZValues(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.
void resetTriangularMesh(QgsTriangularMesh *triangularMesh)
Resets the triangular mesh.
bool isModified() const
Returns whether the mesh has been modified.
void advancedEdit(QgsMeshAdvancedEditing *editing)
Applies an advance editing on the edited mesh, see QgsMeshAdvancedEditing.
bool canBeTransformed(const QList< int > &facesToCheck, const std::function< const QgsMeshVertex(int)> &transformFunction) const
Returns true if faces with index in transformedFaces can be transformed without obtaining topologic o...
int addVertices(const QVector< QgsMeshVertex > &vertices, double tolerance)
Adds vertices in triangular mesh coordinate in the mesh.
int validVerticesCount() const
Returns the count of valid vertices, that is non void vertices in the mesh.
friend class QgsMeshLayerUndoCommandRemoveVerticesFillHoles
bool isVertexFree(int vertexIndex) const
Returns whether the vertex with index vertexIndex is a free vertex.
bool faceCanBeAdded(const QgsMeshFace &face)
Returns true if a face can be added to the mesh.
bool reindex(bool renumbering)
Reindexes the mesh, that is remove unusued index of face and vertices, this operation void the undo/r...
friend class QgsMeshLayerUndoCommandChangeZValue
QgsTriangularMesh * triangularMesh()
Returns a pointer to the triangular mesh.
bool fixError(const QgsMeshEditingError &error)
Tries to fix the topological error in the mesh.
QgsMeshDatasetGroup * createZValueDatasetGroup()
Creates and returns a scalar dataset group with value on vertex that is can be used to access the Z v...
friend class QgsMeshLayerUndoCommandRemoveFaces
QgsTopologicalMesh & topologicalMesh()
Returns a reference to the topological mesh.
int addPointsAsVertices(const QVector< QgsPoint > &point, double tolerance)
Adds points as vertices in triangular mesh coordinate in the mesh.
QgsMeshLayerUndoCommandAddFaces(QgsMeshEditor *meshEditor, QgsTopologicalMesh::TopologicalFaces &faces)
Constructor with the associated meshEditor and faces that will be added.
QgsMeshLayerUndoCommandAddVertices(QgsMeshEditor *meshEditor, const QVector< QgsMeshVertex > &vertices, double tolerance)
Constructor with the associated meshEditor and vertices that will be added.
QgsMeshLayerUndoCommandAdvancedEditing(QgsMeshEditor *meshEditor, QgsMeshAdvancedEditing *advancdEdit)
Constructor with the associated meshEditor.
QgsMeshLayerUndoCommandChangeCoordinates(QgsMeshEditor *meshEditor, const QList< int > &verticesIndexes, const QList< QgsPoint > &newCoordinates)
Constructor with the associated meshEditor and indexes verticesIndexes of the vertices that will have...
QgsMeshLayerUndoCommandChangeXYValue(QgsMeshEditor *meshEditor, const QList< int > &verticesIndexes, const QList< QgsPointXY > &newValues)
Constructor with the associated meshEditor and indexes verticesIndexes of the vertices that will have...
QgsMeshLayerUndoCommandChangeZValue(QgsMeshEditor *meshEditor, const QList< int > &verticesIndexes, const QList< double > &newValues)
Constructor with the associated meshEditor and indexes verticesIndexes of the vertices that will have...
QgsMeshLayerUndoCommandFlipEdge(QgsMeshEditor *meshEditor, int vertexIndex1, int vertexIndex2)
Constructor with the associated meshEditor and the vertex indexes of the edge (vertexIndex1,...
QgsMeshLayerUndoCommandMerge(QgsMeshEditor *meshEditor, int vertexIndex1, int vertexIndex2)
Constructor with the associated meshEditor and the vertex indexes of the edge (vertexIndex1,...
Base class for undo/redo command for mesh editing.
QList< QgsMeshEditor::Edit > mEdits
QgsMeshLayerUndoCommandMeshEdit(QgsMeshEditor *meshEditor)
Constructor for the base class.
QPointer< QgsMeshEditor > mMeshEditor
QgsMeshLayerUndoCommandRemoveFaces(QgsMeshEditor *meshEditor, const QList< int > &facesToRemoveIndexes)
Constructor with the associated meshEditor and indexes facesToRemoveIndexes of the faces that will be...
QgsMeshLayerUndoCommandRemoveVerticesFillHoles(QgsMeshEditor *meshEditor, const QList< int > &verticesToRemoveIndexes, QList< int > *remainingVerticesPointer=nullptr)
Constructor with the associated meshEditor and vertices that will be removed.
QgsMeshLayerUndoCommandRemoveVerticesWithoutFillHoles(QgsMeshEditor *meshEditor, const QList< int > &verticesToRemoveIndexes)
Constructor with the associated meshEditor and vertices that will be removed.
QgsMeshLayerUndoCommandSplitFaces(QgsMeshEditor *meshEditor, const QList< int > &faceIndexes)
Constructor with the associated meshEditor and indexes faceIndexes of the faces to split.
Represents a mesh layer supporting display of data on structured or unstructured meshes.
Definition: qgsmeshlayer.h:100
QgsMeshDataProvider * dataProvider() override
Returns the layer's data provider, it may be nullptr.
Convenient class that turn around a vertex and provide information about faces and vertices.
A class to represent a 2D point.
Definition: qgspointxy.h:59
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
double distance(double x, double y) const SIP_HOLDGIL
Returns the distance between this point and a specified x, y coordinate.
Definition: qgspointxy.h:211
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
bool isEmpty() const override SIP_HOLDGIL
Returns true if the geometry is empty.
Definition: qgspoint.cpp:767
Q_GADGET double x
Definition: qgspoint.h:52
double y
Definition: qgspoint.h:53
A rectangle specified with double values.
Definition: qgsrectangle.h:42
Class that contains topological differences between two states of a topological mesh,...
QVector< QgsMeshFace > removedFaces() const
Returns the faces that are removed with this changes.
QVector< QgsMeshVertex > addedVertices() const
Returns the added vertices with this changes.
bool isEmpty() const
Returns whether changes are empty, that there is nothing to change.
QVector< QgsMeshFace > addedFaces() const
Returns the face that are added with this changes.
QList< int > verticesToRemoveIndexes() const
Returns the indexes of vertices to remove.
Class that contains independent faces an topological information about this faces.
void clear()
Clears all data contained in the instance.
QVector< QgsMeshFace > meshFaces() const
Returns faces.
Class that wraps a QgsMesh to ensure the consistency of the mesh during editing and help to access to...
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.
void applyChanges(const Changes &changes)
Applies the changes.
QgsMeshEditingError facesCanBeRemoved(const QList< int > facesIndexes)
Returns whether faces with index in faceIndexes can be removed/ The method an error object with type ...
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 ...
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...
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...
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.
Changes removeFaces(const QList< int > facesIndexes)
Removes faces with index in faceIndexes.
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 tha tis 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 .
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.
The Changes class is used to make changes of the triangular and to keep traces of this changes If a C...
Triangular/Derived Mesh is mesh with vertices in map coordinates.
const QVector< QgsMeshFace > & triangles() const
Returns triangles.
QgsRectangle nativeExtent()
Returns the extent of the mesh in the native mesh coordinates system, returns empty extent if the tra...
int nativeFaceIndexForPoint(const QgsPointXY &point) const
Finds index of native face at given point It uses spatial indexing.
void reverseChanges(const Changes &changes, const QgsMesh &nativeMesh)
Reverses the changes on the triangular mesh (see Changes)
void applyChanges(const Changes &changes)
Applies the changes on the triangular mesh (see Changes)
const QVector< QgsMeshVertex > & vertices() const
Returns vertices in map coordinate system.
QgsMeshVertex triangularToNativeCoordinates(const QgsMeshVertex &vertex) const
Transforms the vertex from triangular mesh coordinates system to native coordinates system.
QgsMeshVertex nativeToTriangularCoordinates(const QgsMeshVertex &vertex) const
Transforms the vertex from native coordinates system to triangular mesh coordinates system.
bool update(QgsMesh *nativeMesh, const QgsCoordinateTransform &transform)
Constructs triangular mesh from layer's native mesh and transform to destination CRS.
const QVector< QgsMeshVertex > & faceCentroids() const
Returns centroids of the native faces in map CRS.
QList< int > nativeFaceIndexForRectangle(const QgsRectangle &rectangle) const
Finds indexes of native faces which bounding boxes intersect given bounding box It uses spatial index...
int faceIndexForPoint_v2(const QgsPointXY &point) const
Finds index of triangle at given point It uses spatial indexing and don't use geos to be faster.
CORE_EXPORT QgsGeometry toGeometry(const QgsMeshFace &face, const QVector< QgsMeshVertex > &vertices)
Returns face as polygon geometry.
QVector< int > QgsMeshFace
List of vertex indexes.
QgsPoint QgsMeshVertex
xyz coords of vertex
Mesh - vertices, edges and faces.
int vertexCount() const
Returns number of vertices.
QVector< QgsMeshVertex > vertices
QgsMeshFace face(int index) const
Returns a face at the index.
QVector< QgsMeshFace > faces
int faceCount() const
Returns number of faces.