32#include "moc_qgsmesheditor.cpp"
35 : QObject( meshLayer )
36 , mMesh( meshLayer ? meshLayer->nativeMesh() : nullptr )
37 , mTriangularMesh( meshLayer ? meshLayer->triangularMeshByLodIndex( 0 ) : nullptr )
38 , mUndoStack( meshLayer ? meshLayer->undoStack() : nullptr )
52 mUndoStack =
new QUndoStack(
this );
58 std::unique_ptr<QgsMeshDatasetGroup> zValueDatasetGroup = std::make_unique<QgsMeshVerticesElevationDatasetGroup>( tr(
"vertices Z value" ), mMesh );
64 mZValueDatasetGroup = zValueDatasetGroup.get();
66 return zValueDatasetGroup;
80 const QList<int> freeVertices = mTopologicalMesh.freeVerticesIndexes();
81 for (
int vi : freeVertices )
83 if ( mTriangularMesh->faceIndexForPoint_v2( mTriangularMesh->vertices().at( vi ) ) != -1 )
91 mValidFacesCount = mMesh->faceCount();
92 mValidVerticesCount = mMesh->vertexCount();
109 mTriangularMesh->update( mMesh );
136 auto faceIt = mMesh->faces.begin();
137 while ( faceIt != mMesh->faces.end() )
140 faceIt = mMesh->faces.erase( faceIt );
166 Q_ASSERT( vertexIndexes.count() == vertices.count() );
168 QVector<QgsPoint> ring;
169 for (
int i = 0; i < vertices.size(); ++i )
171 const QgsPoint &vertex = vertices[i];
172 ring.append( vertex );
174 auto polygon = std::make_unique< QgsPolygon >();
175 polygon->setExteriorRing(
new QgsLineString( ring ) );
176 const QgsGeometry newFaceGeom( polygon.release() );
178 geomEngine->prepareGeometry();
180 const QgsRectangle boundingBox = newFaceGeom.boundingBox();
181 int newFaceSize = vertexIndexes.count();
182 const QList<int> concernedFaceIndex = mTriangularMesh->nativeFaceIndexForRectangle( boundingBox );
183 if ( !concernedFaceIndex.isEmpty() )
187 for (
const int faceIndex : concernedFaceIndex )
189 const QgsMeshFace &existingFace = mMesh->faces.at( faceIndex );
190 int existingFaceSize = existingFace.count();
191 bool shareVertex =
false;
192 for (
int i = 0; i < existingFaceSize; ++i )
194 if ( vertexIndexes.contains( existingFace.at( i ) ) )
203 for (
int i = 0; i < existingFaceSize; ++i )
205 int index1 = existingFace.at( i );
206 int index2 = existingFace.at( ( i + 1 ) % existingFaceSize );
209 QgsGeometry edgeGeom = QgsGeometry(
new QgsLineString( v1, v2 ) );
211 if ( !vertexIndexes.contains( index1 ) && !vertexIndexes.contains( index2 ) )
214 if ( geomEngine->intersects( edgeGeom.
constGet() ) )
219 for (
int vi = 0; vi < vertexIndexes.count(); ++vi )
221 int vertInNewFace1 = vertexIndexes.at( vi );
222 int vertInNewFace2 = vertexIndexes.at( ( vi + 1 ) % newFaceSize );
223 bool hasToBeTest =
false;
225 if ( vertInNewFace1 != -1 && vertInNewFace2 != -1 )
227 hasToBeTest = vertInNewFace1 != index1 && vertInNewFace2 != index2 && vertInNewFace1 != index2 && vertInNewFace2 != index1;
231 if ( vertInNewFace1 == -1 )
232 hasToBeTest &= vertInNewFace2 != index1 && vertInNewFace2 != index2;
235 if ( vertInNewFace2 == -1 )
236 hasToBeTest &= vertInNewFace1 != index1 && vertInNewFace1 != index2;
242 const QgsMeshVertex &nv2 = vertices.at( ( vi + 1 ) % newFaceSize );
243 const QgsGeometry newEdgeGeom = QgsGeometry(
new QgsLineString( nv1, nv2 ) );
255 if ( geomEngine->intersects( existingFaceGeom.
constGet() ) )
263 for (
const int freeVertexIndex : freeVertices )
265 if ( vertexIndexes.contains( freeVertexIndex ) )
269 if ( geomEngine->contains( &vertex ) )
278 const QList<int> newFaceVerticesIndexes( face.toList() );
279 QList<QgsMeshVertex> allVertices;
280 allVertices.reserve( face.count() );
282 allVertices.append( mTriangularMesh->vertices().at( i ) );
293 QVector<QgsMeshFace> facesToAdd = prepareFaces( { face }, error );
300 error = mTopologicalMesh.facesCanBeAdded( topologicalFaces );
316 const QList<int> face = prepareFaceWithNewVertices( verticesIndex, newVertices, error );
318 if ( face.isEmpty() )
327 int size = face.size();
328 QList<QgsMeshVertex> allVertices;
329 allVertices.reserve( verticesIndex.size() );
331 for (
int i = 0; i < size; ++i )
333 int index = face.at( i );
336 if ( newVertPos >= newVertices.count() )
338 allVertices.append( newVertices.at( newVertPos++ ) );
342 allVertices.append( mTriangularMesh->vertices().at( index ) );
347 int prevIndex = face.at( ( i - 1 + size ) % size );
348 int nextIndex = face.at( ( i + 1 ) % size );
355 if ( prevOppVertex == nextIndex )
362 if ( nextOppVertex == prevIndex )
365 if ( nextIndex != nextOppVertex && prevIndex != prevOppVertex )
372void QgsMeshEditor::applyEdit( QgsMeshEditor::Edit &edit )
374 mTopologicalMesh.
applyChanges( edit.topologicalChanges );
375 mTriangularMesh->
applyChanges( edit.triangularMeshChanges );
377 if ( mZValueDatasetGroup
381 updateElementsCount( edit.topologicalChanges );
384void QgsMeshEditor::reverseEdit( QgsMeshEditor::Edit &edit )
386 mTopologicalMesh.reverseChanges( edit.topologicalChanges );
387 mTriangularMesh->reverseChanges( edit.triangularMeshChanges, *mMesh );
389 if ( mZValueDatasetGroup
391 mZValueDatasetGroup->setStatisticObsolete();
393 updateElementsCount( edit.topologicalChanges,
false );
396void QgsMeshEditor::applyAddVertex( QgsMeshEditor::Edit &edit,
const QgsMeshVertex &vertex,
double tolerance )
398 QgsMeshVertex vertexInTriangularCoordinate = mTriangularMesh->nativeToTriangularCoordinates( vertex );
401 int faceEdgeIntersect = -1;
402 int edgePosition = -1;
404 QgsTopologicalMesh::Changes topologicChanges;
406 if (
edgeIsClose( vertexInTriangularCoordinate, tolerance, faceEdgeIntersect, edgePosition ) )
408 topologicChanges = mTopologicalMesh.insertVertexInFacesEdge( faceEdgeIntersect, edgePosition, vertex );
412 int includingFaceIndex = mTriangularMesh->nativeFaceIndexForPoint( vertexInTriangularCoordinate );
414 if ( includingFaceIndex != -1 )
415 topologicChanges = mTopologicalMesh.addVertexInFace( includingFaceIndex, vertex );
417 topologicChanges = mTopologicalMesh.addFreeVertex( vertex );
420 applyEditOnTriangularMesh( edit, topologicChanges );
422 if ( mZValueDatasetGroup )
423 mZValueDatasetGroup->setStatisticObsolete();
425 updateElementsCount( edit.topologicalChanges );
428bool QgsMeshEditor::applyRemoveVertexFillHole( QgsMeshEditor::Edit &edit,
int vertexIndex )
430 QgsTopologicalMesh::Changes changes = mTopologicalMesh.removeVertexFillHole( vertexIndex );
434 applyEditOnTriangularMesh( edit, changes );
436 if ( mZValueDatasetGroup )
437 mZValueDatasetGroup->setStatisticObsolete();
439 updateElementsCount( edit.topologicalChanges );
446void QgsMeshEditor::applyRemoveVerticesWithoutFillHole( QgsMeshEditor::Edit &edit,
const QList<int> &verticesIndexes )
448 applyEditOnTriangularMesh( edit, mTopologicalMesh.removeVertices( verticesIndexes ) );
450 if ( mZValueDatasetGroup )
451 mZValueDatasetGroup->setStatisticObsolete();
453 updateElementsCount( edit.topologicalChanges );
458 applyEditOnTriangularMesh( edit, mTopologicalMesh.addFaces( faces ) );
460 updateElementsCount( edit.topologicalChanges );
463void QgsMeshEditor::applyRemoveFaces( QgsMeshEditor::Edit &edit,
const QList<int> &faceToRemoveIndex )
465 applyEditOnTriangularMesh( edit, mTopologicalMesh.removeFaces( faceToRemoveIndex ) );
467 updateElementsCount( edit.topologicalChanges );
470void QgsMeshEditor::applyChangeZValue( QgsMeshEditor::Edit &edit,
const QList<int> &verticesIndexes,
const QList<double> &newValues )
472 applyEditOnTriangularMesh( edit, mTopologicalMesh.changeZValue( verticesIndexes, newValues ) );
474 if ( mZValueDatasetGroup )
475 mZValueDatasetGroup->setStatisticObsolete();
478void QgsMeshEditor::applyChangeXYValue( QgsMeshEditor::Edit &edit,
const QList<int> &verticesIndexes,
const QList<QgsPointXY> &newValues )
480 applyEditOnTriangularMesh( edit, mTopologicalMesh.changeXYValue( verticesIndexes, newValues ) );
483void QgsMeshEditor::applyFlipEdge( QgsMeshEditor::Edit &edit,
int vertexIndex1,
int vertexIndex2 )
485 applyEditOnTriangularMesh( edit, mTopologicalMesh.flipEdge( vertexIndex1, vertexIndex2 ) );
487 updateElementsCount( edit.topologicalChanges );
490void QgsMeshEditor::applyMerge( QgsMeshEditor::Edit &edit,
int vertexIndex1,
int vertexIndex2 )
492 applyEditOnTriangularMesh( edit, mTopologicalMesh.merge( vertexIndex1, vertexIndex2 ) );
494 updateElementsCount( edit.topologicalChanges );
497void QgsMeshEditor::applySplit( QgsMeshEditor::Edit &edit,
int faceIndex )
499 applyEditOnTriangularMesh( edit, mTopologicalMesh.splitFace( faceIndex ) );
501 updateElementsCount( edit.topologicalChanges );
506 applyEditOnTriangularMesh( edit, editing->
apply(
this ) );
508 updateElementsCount( edit.topologicalChanges );
510 if ( mZValueDatasetGroup )
511 mZValueDatasetGroup->setStatisticObsolete();
516 QgsTriangularMesh::Changes triangularChanges( topologicChanges, *mMesh );
517 mTriangularMesh->applyChanges( triangularChanges );
519 edit.topologicalChanges = topologicChanges;
520 edit.triangularMeshChanges = triangularChanges;
540 error = mTopologicalMesh.checkConsistency();
554 if ( mTriangularMesh->vertices().count() != mMesh->vertexCount() )
557 if ( mTriangularMesh->faceCentroids().count() != mMesh->faceCount() )
565 QgsRectangle toleranceZone( point.
x() - tolerance, point.
y() - tolerance, point.
x() + tolerance, point.
y() + tolerance );
568 double minDist = std::numeric_limits<double>::max();
569 const QList<int> &nativeFaces = mTriangularMesh->nativeFaceIndexForRectangle( toleranceZone );
570 double epsilon = std::numeric_limits<double>::epsilon() * tolerance;
571 for (
const int nativeFaceIndex : nativeFaces )
573 const QgsMeshFace &face = mMesh->face( nativeFaceIndex );
574 const int faceSize = face.size();
575 for (
int i = 0; i < faceSize; ++i )
583 if ( dist < tolerance && dist < minDist )
585 faceIndex = nativeFaceIndex;
592 if ( edgePosition != -1 )
600 return mValidFacesCount;
605 return mValidVerticesCount;
610 return mMaximumVerticesPerFace;
626 int triangleIndex = mTriangularMesh->faceIndexForPoint_v2( vertex );
627 if ( triangleIndex == -1 )
635 return mTopologicalMesh.edgeCanBeFlipped( vertexIndex1, vertexIndex2 );
648 return mTopologicalMesh.canBeMerged( vertexIndex1, vertexIndex2 );
661 return mTopologicalMesh.canBeSplit( faceIndex );
666 QList<int> faceIndexesSplittable;
668 for (
const int faceIndex : faceIndexes )
670 faceIndexesSplittable.append( faceIndex );
672 if ( faceIndexesSplittable.isEmpty() )
677 return faceIndexesSplittable.count();
680QVector<QgsMeshFace> QgsMeshEditor::prepareFaces(
const QVector<QgsMeshFace> &faces,
QgsMeshEditingError &error )
const
682 QVector<QgsMeshFace> treatedFaces = faces;
686 for (
int i = 0; i < treatedFaces.count(); ++i )
689 if ( mMaximumVerticesPerFace != 0 && face.count() > mMaximumVerticesPerFace )
703QList<int> QgsMeshEditor::prepareFaceWithNewVertices(
const QList<int> &face,
const QList<QgsMeshVertex> &newVertices,
QgsMeshEditingError &error )
const
705 if ( mMaximumVerticesPerFace != 0 && face.count() > mMaximumVerticesPerFace )
711 int faceSize = face.count();
712 QVector<QgsMeshVertex> vertices( faceSize );
713 int newVertexPos = 0;
714 for (
int i = 0; i < faceSize; ++i )
716 if ( face.at( i ) == -1 )
718 if ( newVertexPos >= newVertices.count() )
720 vertices[i] = newVertices.at( newVertexPos++ );
722 else if ( face.at( i ) >= 0 )
724 if ( face.at( i ) >= mTriangularMesh->vertices().count() )
729 vertices[i] = mTriangularMesh->vertices().at( face.at( i ) );
741 bool clockwise =
false;
746 QList<int> newFace = face;
747 for (
int i = 0; i < faceSize / 2; ++i )
749 int temp = newFace[i];
750 newFace[i] = face.at( faceSize - i - 1 );
751 newFace[faceSize - i - 1] = temp;
763 QVector<QgsMeshFace> facesToAdd = prepareFaces( faces, error );
770 error = mTopologicalMesh.facesCanBeAdded( topologicalFaces );
782 return addFaces( { vertexIndexes } );
787 mUndoStack->beginMacro( tr(
"Add a face with new %n vertices",
nullptr, newVertices.count() ) );
788 int newVertexIndex = mMesh->vertexCount();
791 for (
int i = 0; i < vertexIndexes.count(); ++i )
793 int index = vertexIndexes.at( i );
795 face[i] = newVertexIndex++;
801 mUndoStack->endMacro();
808 QVector<QgsMeshVertex> verticesInLayerCoordinate( vertices.count() );
809 int ignoredVertex = 0;
810 for (
int i = 0; i < vertices.count(); ++i )
812 const QgsPointXY &pointInTriangularMesh = vertices.at( i );
813 bool isTooClose =
false;
814 int triangleIndex = mTriangularMesh->faceIndexForPoint_v2( pointInTriangularMesh );
815 if ( triangleIndex != -1 )
817 const QgsMeshFace face = mTriangularMesh->triangles().at( triangleIndex );
818 for (
int j = 0; j < 3; ++j )
820 const QgsPointXY &facePoint = mTriangularMesh->vertices().at( face.at( j ) );
821 double dist = pointInTriangularMesh.
distance( facePoint );
822 if ( dist < tolerance )
831 verticesInLayerCoordinate[i] = mTriangularMesh->triangularToNativeCoordinates( vertices.at( i ) );
835 if ( verticesInLayerCoordinate.at( i ).isEmpty() )
839 if ( ignoredVertex < vertices.count() )
844 int effectivlyAddedVertex = vertices.count() - ignoredVertex;
846 return effectivlyAddedVertex;
858 QList<int> verticesIndexes = verticesToRemoveIndexes;
860 QSet<int> concernedNativeFaces;
861 for (
const int vi : std::as_const( verticesIndexes ) )
863 const QList<int> faces = mTopologicalMesh.facesAroundVertex( vi );
864 concernedNativeFaces.unite( QSet< int >( faces.begin(), faces.end() ) );
867 error = mTopologicalMesh.facesCanBeRemoved( concernedNativeFaces.values() );
878 QList<int> remainingVertices;
881 return remainingVertices;
892 for (
const int faceIndex : facesToCheck )
894 const QgsMeshFace &face = mMesh->face( faceIndex );
895 int faceSize = face.count();
896 QVector<QgsPointXY> pointsInTriangularMeshCoordinate( faceSize );
897 QVector<QgsPointXY> points( faceSize );
898 for (
int i = 0; i < faceSize; ++i )
901 int ip1 = face[( i + 1 ) % faceSize];
902 int ip2 = face[( i + 2 ) % faceSize];
908 double ux = p0.x() - p1.
x();
909 double uy = p0.y() - p1.
y();
910 double vx = p2.
x() - p1.
x();
911 double vy = p2.
y() - p1.
y();
913 double crossProduct = ux * vy - uy * vx;
914 if ( crossProduct >= 0 )
916 pointsInTriangularMeshCoordinate[i] = mTriangularMesh->nativeToTriangularCoordinates( p0 );
923 QList<int> otherFaceIndexes = mTriangularMesh->nativeFaceIndexForRectangle(
QgsGeometry::fromPolygonXY( { pointsInTriangularMeshCoordinate } ).boundingBox() );
925 for (
const int otherFaceIndex : otherFaceIndexes )
927 const QgsMeshFace &otherFace = mMesh->face( otherFaceIndex );
928 int existingFaceSize = otherFace.count();
929 bool shareVertex =
false;
930 for (
int i = 0; i < existingFaceSize; ++i )
932 if ( face.contains( otherFace.at( i ) ) )
941 for (
int i = 0; i < existingFaceSize; ++i )
943 int index1 = otherFace.at( i );
944 int index2 = otherFace.at( ( i + 1 ) % existingFaceSize );
945 if ( !face.contains( index1 ) && !face.contains( index2 ) )
947 const QgsPointXY &v1 = transformFunction( index1 );
948 const QgsPointXY &v2 = transformFunction( index2 );
957 QVector<QgsPointXY> otherPoints( existingFaceSize );
958 for (
int i = 0; i < existingFaceSize; ++i )
959 otherPoints[i] = transformFunction( otherFace.at( i ) );
961 if ( deformedFace.
intersects( existingFaceGeom ) )
967 for (
const int vertexIndex : freeVerticesIndex )
969 const QgsPointXY &mapPoint = transformFunction( vertexIndex );
970 if ( deformedFace.
contains( &mapPoint ) )
977 for (
const int vertexIndex : freeVerticesIndex )
979 const QgsMeshVertex &newFreeVertexPosition = transformFunction( vertexIndex );
980 const QgsMeshVertex pointInTriangularCoord = mTriangularMesh->nativeToTriangularCoordinates( newFreeVertexPosition );
981 const int originalIncludingFace = mTriangularMesh->nativeFaceIndexForPoint( pointInTriangularCoord );
983 if ( originalIncludingFace != -1 )
987 const QgsMeshFace &face = mMesh->face( originalIncludingFace );
988 int faceSize = face.count();
989 QVector<QgsPointXY> points( faceSize );
990 for (
int i = 0; i < faceSize; ++i )
991 points[i] = transformFunction( face.at( i ) );
994 const QgsPointXY ptXY( newFreeVertexPosition );
995 if ( deformedFace.
contains( &ptXY ) )
1021 mTopologicalMesh.reindex();
1022 mUndoStack->clear();
1034 for (
int i =
mEdits.count() - 1; i >= 0; --i )
1043 for ( QgsMeshEditor::Edit &edit :
mEdits )
1049 , mVertices( vertices )
1050 , mTolerance( tolerance )
1052 setText( QObject::tr(
"Add %n vertices",
nullptr, mVertices.count() ) );
1057 if ( !mVertices.isEmpty() )
1059 for (
int i = 0; i < mVertices.count(); ++i )
1064 QgsMeshEditor::Edit edit;
1065 mMeshEditor->applyAddVertex( edit, vertex, mTolerance );
1072 for ( QgsMeshEditor::Edit &edit :
mEdits )
1079 , mVerticesToRemoveIndexes( verticesToRemoveIndexes )
1080 , mRemainingVerticesPointer( remainingVerticesPointer )
1082 setText( QObject::tr(
"Remove %n vertices filling holes",
nullptr, verticesToRemoveIndexes.count() ) );
1087 int initialVertexCount = mVerticesToRemoveIndexes.count();
1088 if ( !mVerticesToRemoveIndexes.isEmpty() )
1090 QgsMeshEditor::Edit edit;
1091 QList<int> vertexToRetry;
1092 while ( !mVerticesToRemoveIndexes.isEmpty() )
1095 for (
const int &vertex : std::as_const( mVerticesToRemoveIndexes ) )
1097 if (
mMeshEditor->applyRemoveVertexFillHole( edit, vertex ) )
1100 vertexToRetry.append( vertex );
1103 if ( vertexToRetry.count() == mVerticesToRemoveIndexes.count() )
1106 mVerticesToRemoveIndexes = vertexToRetry;
1109 if ( initialVertexCount == mVerticesToRemoveIndexes.count() )
1110 setObsolete(
true );
1112 if ( mRemainingVerticesPointer )
1113 *mRemainingVerticesPointer = mVerticesToRemoveIndexes;
1115 mRemainingVerticesPointer =
nullptr;
1117 mVerticesToRemoveIndexes.clear();
1121 for ( QgsMeshEditor::Edit &edit :
mEdits )
1129 , mVerticesToRemoveIndexes( verticesToRemoveIndexes )
1131 setText( QObject::tr(
"Remove %n vertices without filling holes",
nullptr, verticesToRemoveIndexes.count() ) );
1136 if ( !mVerticesToRemoveIndexes.isEmpty() )
1138 QgsMeshEditor::Edit edit;
1140 mMeshEditor->applyRemoveVerticesWithoutFillHole( edit, mVerticesToRemoveIndexes );
1143 mVerticesToRemoveIndexes.clear();
1147 for ( QgsMeshEditor::Edit &edit :
mEdits )
1156 setText( QObject::tr(
"Add %n face(s)",
nullptr, faces.
meshFaces().count() ) );
1161 if ( !mFaces.meshFaces().isEmpty() )
1163 QgsMeshEditor::Edit edit;
1171 for ( QgsMeshEditor::Edit &edit :
mEdits )
1178 , mfacesToRemoveIndexes( facesToRemoveIndexes )
1180 setText( QObject::tr(
"Remove %n face(s)",
nullptr, facesToRemoveIndexes.count() ) );
1185 if ( !mfacesToRemoveIndexes.isEmpty() )
1187 QgsMeshEditor::Edit edit;
1188 mMeshEditor->applyRemoveFaces( edit, mfacesToRemoveIndexes );
1191 mfacesToRemoveIndexes.clear();
1195 for ( QgsMeshEditor::Edit &edit :
mEdits )
1210 return mTriangularMesh->nativeExtent();
1216 return !mUndoStack->isClean();
1223 mTopologicalMesh.reindex();
1224 mUndoStack->clear();
1226 mValidFacesCount = mMesh->faceCount();
1227 mValidVerticesCount = mMesh->vertexCount();
1234 if ( !mTopologicalMesh.renumber() )
1239 mValidFacesCount = mMesh->faceCount();
1240 mValidVerticesCount = mMesh->vertexCount();
1249 return mTopologicalMesh.freeVerticesIndexes();
1254 return mTopologicalMesh.isVertexOnBoundary( vertexIndex );
1259 return mTopologicalMesh.isVertexFree( vertexIndex );
1264 return mTopologicalMesh.vertexCirculator( vertexIndex );
1269 return mTopologicalMesh;
1274 return mTriangularMesh;
1279 , mVerticesIndexes( verticesIndexes )
1280 , mNewValues( newValues )
1282 setText( QObject::tr(
"Change %n vertices Z Value",
nullptr, verticesIndexes.count() ) );
1287 if ( !mVerticesIndexes.isEmpty() )
1289 QgsMeshEditor::Edit edit;
1290 mMeshEditor->applyChangeZValue( edit, mVerticesIndexes, mNewValues );
1292 mVerticesIndexes.clear();
1297 for ( QgsMeshEditor::Edit &edit :
mEdits )
1304 , mVerticesIndexes( verticesIndexes )
1305 , mNewValues( newValues )
1307 setText( QObject::tr(
"Move %n vertices",
nullptr, verticesIndexes.count() ) );
1312 if ( !mVerticesIndexes.isEmpty() )
1314 QgsMeshEditor::Edit edit;
1315 mMeshEditor->applyChangeXYValue( edit, mVerticesIndexes, mNewValues );
1317 mVerticesIndexes.clear();
1322 for ( QgsMeshEditor::Edit &edit :
mEdits )
1330 , mVerticesIndexes( verticesIndexes )
1331 , mNewCoordinates( newCoordinates )
1333 setText( QObject::tr(
"Transform %n vertices coordinates",
nullptr, verticesIndexes.count() ) );
1338 if ( !mVerticesIndexes.isEmpty() )
1340 QgsMeshEditor::Edit editXY;
1341 QList<QgsPointXY> newXY;
1342 newXY.reserve( mNewCoordinates.count() );
1343 QgsMeshEditor::Edit editZ;
1345 newZ.reserve( mNewCoordinates.count() );
1347 for (
const QgsPoint &pt : std::as_const( mNewCoordinates ) )
1350 newZ.append( pt.z() );
1353 mMeshEditor->applyChangeXYValue( editXY, mVerticesIndexes, newXY );
1355 mMeshEditor->applyChangeZValue( editZ, mVerticesIndexes, newZ );
1357 mVerticesIndexes.clear();
1358 mNewCoordinates.clear();
1362 for ( QgsMeshEditor::Edit &edit :
mEdits )
1370 , mVertexIndex1( vertexIndex1 )
1371 , mVertexIndex2( vertexIndex2 )
1373 setText( QObject::tr(
"Flip edge" ) );
1378 if ( mVertexIndex1 >= 0 && mVertexIndex2 >= 0 )
1380 QgsMeshEditor::Edit edit;
1381 mMeshEditor->applyFlipEdge( edit, mVertexIndex1, mVertexIndex2 );
1388 for ( QgsMeshEditor::Edit &edit :
mEdits )
1395 , mVertexIndex1( vertexIndex1 )
1396 , mVertexIndex2( vertexIndex2 )
1398 setText( QObject::tr(
"Merge faces" ) );
1403 if ( mVertexIndex1 >= 0 && mVertexIndex2 >= 0 )
1405 QgsMeshEditor::Edit edit;
1406 mMeshEditor->applyMerge( edit, mVertexIndex1, mVertexIndex2 );
1413 for ( QgsMeshEditor::Edit &edit :
mEdits )
1420 , mFaceIndexes( faceIndexes )
1422 setText( QObject::tr(
"Split %n face(s)",
nullptr, faceIndexes.count() ) );
1427 if ( !mFaceIndexes.isEmpty() )
1429 for (
int faceIndex : std::as_const( mFaceIndexes ) )
1431 QgsMeshEditor::Edit edit;
1435 mFaceIndexes.clear();
1439 for ( QgsMeshEditor::Edit &edit :
mEdits )
1446 , mAdvancedEditing( advancdEdit )
1448 setText( advancdEdit->
text() );
1453 if ( mAdvancedEditing )
1455 QgsMeshEditor::Edit edit;
1456 while ( !mAdvancedEditing->isFinished() )
1458 mMeshEditor->applyAdvancedEdit( edit, mAdvancedEditing );
1462 mAdvancedEditing =
nullptr;
1466 for ( QgsMeshEditor::Edit &edit :
mEdits )
1474 , mTolerance( tolerance )
1476 setText( QObject::tr(
"Add vertex inside face with Delaunay refinement" ) );
1481 if ( !mVertex.isEmpty() )
1483 QgsMeshEditor::Edit edit;
1485 mMeshEditor->applyAddVertex( edit, mVertex, mTolerance );
1488 QList<std::pair<int, int>> sharedEdges = innerEdges( secondNeighboringTriangularFaces() );
1490 for ( std::pair<int, int> edge : sharedEdges )
1492 if (
mMeshEditor->edgeCanBeFlipped( edge.first, edge.second ) && !
mMeshEditor->topologicalMesh().delaunayConditionForEdge( edge.first, edge.second ) )
1494 mMeshEditor->applyFlipEdge( edit, edge.first, edge.second );
1503 for ( QgsMeshEditor::Edit &edit :
mEdits )
1508QSet<int> QgsMeshLayerUndoCommandAddVertexInFaceWithDelaunayRefinement::secondNeighboringTriangularFaces()
1510 const int vIndex =
mMeshEditor->topologicalMesh().mesh()->vertexCount() - 1;
1511 const QList<int> firstNeighborFaces =
mMeshEditor->topologicalMesh().facesAroundVertex( vIndex );
1512 QSet<int> firstNeighborVertices;
1513 for (
int face : firstNeighborFaces )
1516 for (
int vertex : meshFace )
1518 firstNeighborVertices.insert( vertex );
1522 QSet<int> secondNeighboringFaces;
1523 for (
int vertex : firstNeighborVertices )
1525 const QList<int> faces =
mMeshEditor->topologicalMesh().facesAroundVertex( vertex );
1526 for (
int face : faces )
1528 if (
mMeshEditor->topologicalMesh().mesh()->face( face ).count() == 3 )
1529 secondNeighboringFaces.insert( face );
1532 return secondNeighboringFaces;
1535QList<std::pair<int, int>> QgsMeshLayerUndoCommandAddVertexInFaceWithDelaunayRefinement::innerEdges(
const QSet<int> &faces )
1538 QMap<std::pair<int, int>,
int> edges;
1540 for (
int faceIndex : faces )
1544 for (
int i = 0; i < face.size(); i++ )
1547 if ( next == face.size() )
1552 int minIndex = std::min( face.at( i ), face.at( next ) );
1553 int maxIndex = std::max( face.at( i ), face.at( next ) );
1554 std::pair<int, int> edge = std::pair<int, int>( minIndex, maxIndex );
1557 if ( edges.contains( edge ) )
1559 count = edges.take( edge );
1563 edges.insert( edge, count );
1567 QList<std::pair<int, int>> sharedEdges;
1569 for (
auto it = edges.begin(); it != edges.end(); it++ )
1571 if ( it.value() == 2 )
1573 sharedEdges.push_back( it.key() );
MeshEditingErrorType
Type of error that can occur during mesh frame editing.
@ 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.
QgsVertexIterator vertices() const
Returns a read-only, Java-style iterator for traversal of vertices of all the geometry,...
static double sqrDistToLine(double ptX, double ptY, double x1, double y1, double x2, double y2, double &minDistX, double &minDistY, double epsilon)
Returns the squared distance between a point and a line.
A geometry is the spatial representation of a feature.
static QgsGeometry fromPolylineXY(const QgsPolylineXY &polyline)
Creates a new LineString geometry from a list of QgsPointXY points.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
bool contains(const QgsPointXY *p) const
Returns true if the geometry contains the point p.
static QgsGeometry fromPolygonXY(const QgsPolygonXY &polygon)
Creates a new geometry from a QgsPolygonXY.
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry, double precision=0.0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlag::SkipEmptyInteriorRings)
Creates and returns a new geometry engine representing the specified geometry using precision on a gr...
bool intersects(const QgsRectangle &rectangle) const
Returns true if this geometry exactly intersects with a rectangle.
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 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,...
void setStatisticObsolete() const
Sets statistic obsolete, that means statistic will be recalculated when requested.
Represents an error which occurred during mesh editing.
Qgis::MeshEditingErrorType errorType
QgsMeshEditingError()
Constructor of the default error, that is NoError.
Handles edit operations on a mesh layer.
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...
bool isFaceGeometricallyCompatible(const QgsMeshFace &face) const
Returns true if the face does not intersect or contains any other elements (faces or vertices) The to...
QList< int > freeVerticesIndexes() const
Returns all the free vertices indexes.
friend class QgsMeshLayerUndoCommandChangeXYValue
void addVertexWithDelaunayRefinement(const QgsMeshVertex &vertex, const double tolerance)
Add a vertex in a face with Delaunay refinement of neighboring faces All neighboring faces sharing a ...
bool isVertexOnBoundary(int vertexIndex) const
Returns whether the vertex with index vertexIndex is on a boundary.
friend class QgsMeshLayerUndoCommandFlipEdge
bool faceCanBeAdded(const QgsMeshFace &face) const
Returns true if a face can be added to the mesh.
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.
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
QgsMeshEditingError addFaceWithNewVertices(const QList< int > &vertexIndexes, const QList< QgsMeshVertex > &newVertices)
Adds a face formed by some vertices vertexIndexes to the mesh, returns topological errors if this ope...
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.
bool faceCanBeAddedWithNewVertices(const QList< int > &verticesIndex, const QList< QgsMeshVertex > &newVertices) const
Returns true if a face formed by some vertices can be added to the mesh.
void meshEdited()
Emitted when the mesh is edited.
friend class QgsMeshLayerUndoCommandAddVertexInFaceWithDelaunayRefinement
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 reindex(bool renumbering)
Reindexes the mesh, that is remove unusued index of face and vertices, this operation void the undo/r...
std::unique_ptr< 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 QgsMeshLayerUndoCommandChangeZValue
QgsTriangularMesh * triangularMesh()
Returns a pointer to the triangular mesh.
bool fixError(const QgsMeshEditingError &error)
Tries to fix the topological error in the mesh.
friend class QgsMeshLayerUndoCommandRemoveFaces
QgsTopologicalMesh & topologicalMesh()
Returns a reference to the topological mesh.
~QgsMeshEditor() override
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.
QgsMeshLayerUndoCommandAddVertexInFaceWithDelaunayRefinement(QgsMeshEditor *meshEditor, const QgsMeshVertex &vertex, double tolerance)
Constructor with the associated meshEditor and indexes vertex and tolerance.
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,...
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.
QgsMeshDataProvider * dataProvider() override
Returns the layer's data provider, it may be nullptr.
static QgsGeometry toGeometry(const QgsMeshFace &face, const QVector< QgsMeshVertex > &vertices)
Returns face as polygon geometry.
Convenience class that turns around a vertex and provides information about faces and vertices.
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...
bool goBoundaryClockwise() const
Sets the circulator on the boundary face turning clockwise, return false is there isn't boundary face...
int oppositeVertexClockwise() const
Returns the opposite vertex of the current face and on the edge on the side turning clockwise.
double distance(double x, double y) const
Returns the distance between this point and a specified x, y coordinate.
Point geometry type, with support for z-dimension and m-values.
bool isEmpty() const override
Returns true if the geometry is empty.
A rectangle specified with double values.
Contains topological differences between two states of a topological mesh, only accessible from the Q...
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.
QList< double > newVerticesZValues() const
Returns the new Z values of vertices that have changed their coordinates.
QVector< QgsMeshFace > addedFaces() const
Returns the face that are added with this changes.
QList< int > verticesToRemoveIndexes() const
Returns the indexes of vertices to remove.
Contains independent faces and topological information about these faces.
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...
static QgsTopologicalMesh createTopologicalMesh(QgsMesh *mesh, int maxVerticesPerFace, QgsMeshEditingError &error)
Creates a topologicaly consistent mesh with mesh, this static method modifies mesh to be topological ...
static QgsMeshEditingError counterClockwiseFaces(QgsMeshFace &face, QgsMesh *mesh)
Checks the topology of the face and sets it counter clockwise if necessary.
void applyChanges(const Changes &changes)
Applies the changes.
static TopologicalFaces createNewTopologicalFaces(const QVector< QgsMeshFace > &faces, bool uniqueSharedVertexAllowed, QgsMeshEditingError &error)
Creates new topological faces that are not yet included in the mesh.
A triangular/derived mesh with vertices in map coordinates.
void applyChanges(const Changes &changes)
Applies the changes on the triangular mesh (see Changes).
QVector< int > QgsMeshFace
List of vertex indexes.
QgsPoint QgsMeshVertex
xyz coords of vertex
Mesh - vertices, edges and faces.