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 return std::make_unique<QgsMeshVerticesElevationDatasetGroup>( tr(
"vertices Z value" ), mMesh );
72 const QList<int> freeVertices = mTopologicalMesh.freeVerticesIndexes();
73 for (
int vi : freeVertices )
75 if ( mTriangularMesh->faceIndexForPoint_v2( mTriangularMesh->vertices().at( vi ) ) != -1 )
83 mValidFacesCount = mMesh->faceCount();
84 mValidVerticesCount = mMesh->vertexCount();
101 mTriangularMesh->update( mMesh );
128 auto faceIt = mMesh->faces.begin();
129 while ( faceIt != mMesh->faces.end() )
132 faceIt = mMesh->faces.erase( faceIt );
158 Q_ASSERT( vertexIndexes.count() == vertices.count() );
160 QVector<QgsPoint> ring;
161 for (
int i = 0; i < vertices.size(); ++i )
163 const QgsPoint &vertex = vertices[i];
164 ring.append( vertex );
166 auto polygon = std::make_unique< QgsPolygon >();
167 polygon->setExteriorRing(
new QgsLineString( ring ) );
168 const QgsGeometry newFaceGeom( polygon.release() );
170 geomEngine->prepareGeometry();
172 const QgsRectangle boundingBox = newFaceGeom.boundingBox();
173 int newFaceSize = vertexIndexes.count();
174 const QList<int> concernedFaceIndex = mTriangularMesh->nativeFaceIndexForRectangle( boundingBox );
175 if ( !concernedFaceIndex.isEmpty() )
179 for (
const int faceIndex : concernedFaceIndex )
181 const QgsMeshFace &existingFace = mMesh->faces.at( faceIndex );
182 int existingFaceSize = existingFace.count();
183 bool shareVertex =
false;
184 for (
int i = 0; i < existingFaceSize; ++i )
186 if ( vertexIndexes.contains( existingFace.at( i ) ) )
195 for (
int i = 0; i < existingFaceSize; ++i )
197 int index1 = existingFace.at( i );
198 int index2 = existingFace.at( ( i + 1 ) % existingFaceSize );
201 QgsGeometry edgeGeom = QgsGeometry(
new QgsLineString( v1, v2 ) );
203 if ( !vertexIndexes.contains( index1 ) && !vertexIndexes.contains( index2 ) )
206 if ( geomEngine->intersects( edgeGeom.
constGet() ) )
211 for (
int vi = 0; vi < vertexIndexes.count(); ++vi )
213 int vertInNewFace1 = vertexIndexes.at( vi );
214 int vertInNewFace2 = vertexIndexes.at( ( vi + 1 ) % newFaceSize );
215 bool hasToBeTest =
false;
217 if ( vertInNewFace1 != -1 && vertInNewFace2 != -1 )
219 hasToBeTest = vertInNewFace1 != index1 && vertInNewFace2 != index2 && vertInNewFace1 != index2 && vertInNewFace2 != index1;
223 if ( vertInNewFace1 == -1 )
224 hasToBeTest &= vertInNewFace2 != index1 && vertInNewFace2 != index2;
227 if ( vertInNewFace2 == -1 )
228 hasToBeTest &= vertInNewFace1 != index1 && vertInNewFace1 != index2;
234 const QgsMeshVertex &nv2 = vertices.at( ( vi + 1 ) % newFaceSize );
235 const QgsGeometry newEdgeGeom = QgsGeometry(
new QgsLineString( nv1, nv2 ) );
247 if ( geomEngine->intersects( existingFaceGeom.
constGet() ) )
255 for (
const int freeVertexIndex : freeVertices )
257 if ( vertexIndexes.contains( freeVertexIndex ) )
261 if ( geomEngine->contains( &vertex ) )
270 const QList<int> newFaceVerticesIndexes( face.toList() );
271 QList<QgsMeshVertex> allVertices;
272 allVertices.reserve( face.count() );
274 allVertices.append( mTriangularMesh->vertices().at( i ) );
285 QVector<QgsMeshFace> facesToAdd = prepareFaces( { face }, error );
292 error = mTopologicalMesh.facesCanBeAdded( topologicalFaces );
308 const QList<int> face = prepareFaceWithNewVertices( verticesIndex, newVertices, error );
310 if ( face.isEmpty() )
319 int size = face.size();
320 QList<QgsMeshVertex> allVertices;
321 allVertices.reserve( verticesIndex.size() );
323 for (
int i = 0; i < size; ++i )
325 int index = face.at( i );
328 if ( newVertPos >= newVertices.count() )
330 allVertices.append( newVertices.at( newVertPos++ ) );
334 allVertices.append( mTriangularMesh->vertices().at( index ) );
339 int prevIndex = face.at( ( i - 1 + size ) % size );
340 int nextIndex = face.at( ( i + 1 ) % size );
347 if ( prevOppVertex == nextIndex )
354 if ( nextOppVertex == prevIndex )
357 if ( nextIndex != nextOppVertex && prevIndex != prevOppVertex )
364void QgsMeshEditor::applyEdit( QgsMeshEditor::Edit &edit )
369 mTopologicalMesh.
applyChanges( edit.topologicalChanges );
370 mTriangularMesh->
applyChanges( edit.triangularMeshChanges );
375 updateElementsCount( edit.topologicalChanges );
378void QgsMeshEditor::reverseEdit( QgsMeshEditor::Edit &edit )
383 mTopologicalMesh.reverseChanges( edit.topologicalChanges );
384 mTriangularMesh->reverseChanges( edit.triangularMeshChanges, *mMesh );
387 mMesh->invalidateZMinMax();
389 updateElementsCount( edit.topologicalChanges,
false );
392void QgsMeshEditor::applyAddVertex( QgsMeshEditor::Edit &edit,
const QgsMeshVertex &vertex,
double tolerance )
394 QgsMeshVertex vertexInTriangularCoordinate = mTriangularMesh->nativeToTriangularCoordinates( vertex );
397 int faceEdgeIntersect = -1;
398 int edgePosition = -1;
400 QgsTopologicalMesh::Changes topologicChanges;
402 if (
edgeIsClose( vertexInTriangularCoordinate, tolerance, faceEdgeIntersect, edgePosition ) )
404 topologicChanges = mTopologicalMesh.insertVertexInFacesEdge( faceEdgeIntersect, edgePosition, vertex );
408 int includingFaceIndex = mTriangularMesh->nativeFaceIndexForPoint( vertexInTriangularCoordinate );
410 if ( includingFaceIndex != -1 )
411 topologicChanges = mTopologicalMesh.addVertexInFace( includingFaceIndex, vertex );
413 topologicChanges = mTopologicalMesh.addFreeVertex( vertex );
416 applyEditOnTriangularMesh( edit, topologicChanges );
419 mMesh->invalidateZMinMax();
421 updateElementsCount( edit.topologicalChanges );
424bool QgsMeshEditor::applyRemoveVertexFillHole( QgsMeshEditor::Edit &edit,
int vertexIndex )
426 QgsTopologicalMesh::Changes changes = mTopologicalMesh.removeVertexFillHole( vertexIndex );
430 applyEditOnTriangularMesh( edit, changes );
433 mMesh->invalidateZMinMax();
435 updateElementsCount( edit.topologicalChanges );
442void QgsMeshEditor::applyRemoveVerticesWithoutFillHole( QgsMeshEditor::Edit &edit,
const QList<int> &verticesIndexes )
444 applyEditOnTriangularMesh( edit, mTopologicalMesh.removeVertices( verticesIndexes ) );
447 mMesh->invalidateZMinMax();
449 updateElementsCount( edit.topologicalChanges );
454 applyEditOnTriangularMesh( edit, mTopologicalMesh.addFaces( faces ) );
456 updateElementsCount( edit.topologicalChanges );
459void QgsMeshEditor::applyRemoveFaces( QgsMeshEditor::Edit &edit,
const QList<int> &faceToRemoveIndex )
461 applyEditOnTriangularMesh( edit, mTopologicalMesh.removeFaces( faceToRemoveIndex ) );
463 updateElementsCount( edit.topologicalChanges );
466void QgsMeshEditor::applyChangeZValue( QgsMeshEditor::Edit &edit,
const QList<int> &verticesIndexes,
const QList<double> &newValues )
468 applyEditOnTriangularMesh( edit, mTopologicalMesh.changeZValue( verticesIndexes, newValues ) );
471 mMesh->invalidateZMinMax();
474void QgsMeshEditor::applyChangeXYValue( QgsMeshEditor::Edit &edit,
const QList<int> &verticesIndexes,
const QList<QgsPointXY> &newValues )
476 applyEditOnTriangularMesh( edit, mTopologicalMesh.changeXYValue( verticesIndexes, newValues ) );
479void QgsMeshEditor::applyFlipEdge( QgsMeshEditor::Edit &edit,
int vertexIndex1,
int vertexIndex2 )
481 applyEditOnTriangularMesh( edit, mTopologicalMesh.flipEdge( vertexIndex1, vertexIndex2 ) );
483 updateElementsCount( edit.topologicalChanges );
486void QgsMeshEditor::applyMerge( QgsMeshEditor::Edit &edit,
int vertexIndex1,
int vertexIndex2 )
488 applyEditOnTriangularMesh( edit, mTopologicalMesh.merge( vertexIndex1, vertexIndex2 ) );
490 updateElementsCount( edit.topologicalChanges );
493void QgsMeshEditor::applySplit( QgsMeshEditor::Edit &edit,
int faceIndex )
495 applyEditOnTriangularMesh( edit, mTopologicalMesh.splitFace( faceIndex ) );
497 updateElementsCount( edit.topologicalChanges );
502 applyEditOnTriangularMesh( edit, editing->
apply(
this ) );
504 updateElementsCount( edit.topologicalChanges );
507 mMesh->invalidateZMinMax();
512 QgsTriangularMesh::Changes triangularChanges( topologicChanges, *mMesh );
513 mTriangularMesh->applyChanges( triangularChanges );
515 edit.topologicalChanges = topologicChanges;
516 edit.triangularMeshChanges = triangularChanges;
536 error = mTopologicalMesh.checkConsistency();
550 if ( mTriangularMesh->vertices().count() != mMesh->vertexCount() )
553 if ( mTriangularMesh->faceCentroids().count() != mMesh->faceCount() )
561 QgsRectangle toleranceZone( point.
x() - tolerance, point.
y() - tolerance, point.
x() + tolerance, point.
y() + tolerance );
564 double minDist = std::numeric_limits<double>::max();
565 const QList<int> &nativeFaces = mTriangularMesh->nativeFaceIndexForRectangle( toleranceZone );
566 double epsilon = std::numeric_limits<double>::epsilon() * tolerance;
567 for (
const int nativeFaceIndex : nativeFaces )
569 const QgsMeshFace &face = mMesh->face( nativeFaceIndex );
570 const int faceSize = face.size();
571 for (
int i = 0; i < faceSize; ++i )
579 if ( dist < tolerance && dist < minDist )
581 faceIndex = nativeFaceIndex;
588 if ( edgePosition != -1 )
596 return mValidFacesCount;
601 return mValidVerticesCount;
606 return mMaximumVerticesPerFace;
622 int triangleIndex = mTriangularMesh->faceIndexForPoint_v2( vertex );
623 if ( triangleIndex == -1 )
631 return mTopologicalMesh.edgeCanBeFlipped( vertexIndex1, vertexIndex2 );
644 return mTopologicalMesh.canBeMerged( vertexIndex1, vertexIndex2 );
657 return mTopologicalMesh.canBeSplit( faceIndex );
662 QList<int> faceIndexesSplittable;
664 for (
const int faceIndex : faceIndexes )
666 faceIndexesSplittable.append( faceIndex );
668 if ( faceIndexesSplittable.isEmpty() )
673 return faceIndexesSplittable.count();
676QVector<QgsMeshFace> QgsMeshEditor::prepareFaces(
const QVector<QgsMeshFace> &faces,
QgsMeshEditingError &error )
const
678 QVector<QgsMeshFace> treatedFaces = faces;
682 for (
int i = 0; i < treatedFaces.count(); ++i )
685 if ( mMaximumVerticesPerFace != 0 && face.count() > mMaximumVerticesPerFace )
699QList<int> QgsMeshEditor::prepareFaceWithNewVertices(
const QList<int> &face,
const QList<QgsMeshVertex> &newVertices,
QgsMeshEditingError &error )
const
701 if ( mMaximumVerticesPerFace != 0 && face.count() > mMaximumVerticesPerFace )
707 int faceSize = face.count();
708 QVector<QgsMeshVertex> vertices( faceSize );
709 int newVertexPos = 0;
710 for (
int i = 0; i < faceSize; ++i )
712 if ( face.at( i ) == -1 )
714 if ( newVertexPos >= newVertices.count() )
716 vertices[i] = newVertices.at( newVertexPos++ );
718 else if ( face.at( i ) >= 0 )
720 if ( face.at( i ) >= mTriangularMesh->vertices().count() )
725 vertices[i] = mTriangularMesh->vertices().at( face.at( i ) );
737 bool clockwise =
false;
742 QList<int> newFace = face;
743 for (
int i = 0; i < faceSize / 2; ++i )
745 int temp = newFace[i];
746 newFace[i] = face.at( faceSize - i - 1 );
747 newFace[faceSize - i - 1] = temp;
759 QVector<QgsMeshFace> facesToAdd = prepareFaces( faces, error );
766 error = mTopologicalMesh.facesCanBeAdded( topologicalFaces );
778 return addFaces( { vertexIndexes } );
783 mUndoStack->beginMacro( tr(
"Add a face with new %n vertices",
nullptr, newVertices.count() ) );
784 int newVertexIndex = mMesh->vertexCount();
787 for (
int i = 0; i < vertexIndexes.count(); ++i )
789 int index = vertexIndexes.at( i );
791 face[i] = newVertexIndex++;
797 mUndoStack->endMacro();
804 QVector<QgsMeshVertex> verticesInLayerCoordinate( vertices.count() );
805 int ignoredVertex = 0;
806 for (
int i = 0; i < vertices.count(); ++i )
808 const QgsPointXY &pointInTriangularMesh = vertices.at( i );
809 bool isTooClose =
false;
810 int triangleIndex = mTriangularMesh->faceIndexForPoint_v2( pointInTriangularMesh );
811 if ( triangleIndex != -1 )
813 const QgsMeshFace face = mTriangularMesh->triangles().at( triangleIndex );
814 for (
int j = 0; j < 3; ++j )
816 const QgsPointXY &facePoint = mTriangularMesh->vertices().at( face.at( j ) );
817 double dist = pointInTriangularMesh.
distance( facePoint );
818 if ( dist < tolerance )
827 verticesInLayerCoordinate[i] = mTriangularMesh->triangularToNativeCoordinates( vertices.at( i ) );
831 if ( verticesInLayerCoordinate.at( i ).isEmpty() )
835 if ( ignoredVertex < vertices.count() )
840 int effectivlyAddedVertex = vertices.count() - ignoredVertex;
842 return effectivlyAddedVertex;
854 QList<int> verticesIndexes = verticesToRemoveIndexes;
856 QSet<int> concernedNativeFaces;
857 for (
const int vi : std::as_const( verticesIndexes ) )
859 const QList<int> faces = mTopologicalMesh.facesAroundVertex( vi );
860 concernedNativeFaces.unite( QSet< int >( faces.begin(), faces.end() ) );
863 error = mTopologicalMesh.facesCanBeRemoved( concernedNativeFaces.values() );
874 QList<int> remainingVertices;
877 return remainingVertices;
888 for (
const int faceIndex : facesToCheck )
890 const QgsMeshFace &face = mMesh->face( faceIndex );
891 int faceSize = face.count();
892 QVector<QgsPointXY> pointsInTriangularMeshCoordinate( faceSize );
893 QVector<QgsPointXY> points( faceSize );
894 for (
int i = 0; i < faceSize; ++i )
897 int ip1 = face[( i + 1 ) % faceSize];
898 int ip2 = face[( i + 2 ) % faceSize];
904 double ux = p0.x() - p1.
x();
905 double uy = p0.y() - p1.
y();
906 double vx = p2.
x() - p1.
x();
907 double vy = p2.
y() - p1.
y();
909 double crossProduct = ux * vy - uy * vx;
910 if ( crossProduct >= 0 )
912 pointsInTriangularMeshCoordinate[i] = mTriangularMesh->nativeToTriangularCoordinates( p0 );
919 QList<int> otherFaceIndexes = mTriangularMesh->nativeFaceIndexForRectangle(
QgsGeometry::fromPolygonXY( { pointsInTriangularMeshCoordinate } ).boundingBox() );
921 for (
const int otherFaceIndex : otherFaceIndexes )
923 const QgsMeshFace &otherFace = mMesh->face( otherFaceIndex );
924 int existingFaceSize = otherFace.count();
925 bool shareVertex =
false;
926 for (
int i = 0; i < existingFaceSize; ++i )
928 if ( face.contains( otherFace.at( i ) ) )
937 for (
int i = 0; i < existingFaceSize; ++i )
939 int index1 = otherFace.at( i );
940 int index2 = otherFace.at( ( i + 1 ) % existingFaceSize );
941 if ( !face.contains( index1 ) && !face.contains( index2 ) )
943 const QgsPointXY &v1 = transformFunction( index1 );
944 const QgsPointXY &v2 = transformFunction( index2 );
953 QVector<QgsPointXY> otherPoints( existingFaceSize );
954 for (
int i = 0; i < existingFaceSize; ++i )
955 otherPoints[i] = transformFunction( otherFace.at( i ) );
957 if ( deformedFace.
intersects( existingFaceGeom ) )
963 for (
const int vertexIndex : freeVerticesIndex )
965 const QgsPointXY &mapPoint = transformFunction( vertexIndex );
966 if ( deformedFace.
contains( &mapPoint ) )
973 for (
const int vertexIndex : freeVerticesIndex )
975 const QgsMeshVertex &newFreeVertexPosition = transformFunction( vertexIndex );
976 const QgsMeshVertex pointInTriangularCoord = mTriangularMesh->nativeToTriangularCoordinates( newFreeVertexPosition );
977 const int originalIncludingFace = mTriangularMesh->nativeFaceIndexForPoint( pointInTriangularCoord );
979 if ( originalIncludingFace != -1 )
983 const QgsMeshFace &face = mMesh->face( originalIncludingFace );
984 int faceSize = face.count();
985 QVector<QgsPointXY> points( faceSize );
986 for (
int i = 0; i < faceSize; ++i )
987 points[i] = transformFunction( face.at( i ) );
990 const QgsPointXY ptXY( newFreeVertexPosition );
991 if ( deformedFace.
contains( &ptXY ) )
1017 mTopologicalMesh.reindex();
1018 mUndoStack->clear();
1030 for (
int i =
mEdits.count() - 1; i >= 0; --i )
1039 for ( QgsMeshEditor::Edit &edit :
mEdits )
1045 , mVertices( vertices )
1046 , mTolerance( tolerance )
1048 setText( QObject::tr(
"Add %n vertices",
nullptr, mVertices.count() ) );
1053 if ( !mVertices.isEmpty() )
1055 for (
int i = 0; i < mVertices.count(); ++i )
1060 QgsMeshEditor::Edit edit;
1061 mMeshEditor->applyAddVertex( edit, vertex, mTolerance );
1068 for ( QgsMeshEditor::Edit &edit :
mEdits )
1075 , mVerticesToRemoveIndexes( verticesToRemoveIndexes )
1076 , mRemainingVerticesPointer( remainingVerticesPointer )
1078 setText( QObject::tr(
"Remove %n vertices filling holes",
nullptr, verticesToRemoveIndexes.count() ) );
1083 int initialVertexCount = mVerticesToRemoveIndexes.count();
1084 if ( !mVerticesToRemoveIndexes.isEmpty() )
1086 QgsMeshEditor::Edit edit;
1087 QList<int> vertexToRetry;
1088 while ( !mVerticesToRemoveIndexes.isEmpty() )
1091 for (
const int &vertex : std::as_const( mVerticesToRemoveIndexes ) )
1093 if (
mMeshEditor->applyRemoveVertexFillHole( edit, vertex ) )
1096 vertexToRetry.append( vertex );
1099 if ( vertexToRetry.count() == mVerticesToRemoveIndexes.count() )
1102 mVerticesToRemoveIndexes = vertexToRetry;
1105 if ( initialVertexCount == mVerticesToRemoveIndexes.count() )
1106 setObsolete(
true );
1108 if ( mRemainingVerticesPointer )
1109 *mRemainingVerticesPointer = mVerticesToRemoveIndexes;
1111 mRemainingVerticesPointer =
nullptr;
1113 mVerticesToRemoveIndexes.clear();
1117 for ( QgsMeshEditor::Edit &edit :
mEdits )
1125 , mVerticesToRemoveIndexes( verticesToRemoveIndexes )
1127 setText( QObject::tr(
"Remove %n vertices without filling holes",
nullptr, verticesToRemoveIndexes.count() ) );
1132 if ( !mVerticesToRemoveIndexes.isEmpty() )
1134 QgsMeshEditor::Edit edit;
1136 mMeshEditor->applyRemoveVerticesWithoutFillHole( edit, mVerticesToRemoveIndexes );
1139 mVerticesToRemoveIndexes.clear();
1143 for ( QgsMeshEditor::Edit &edit :
mEdits )
1152 setText( QObject::tr(
"Add %n face(s)",
nullptr, faces.
meshFaces().count() ) );
1157 if ( !mFaces.meshFaces().isEmpty() )
1159 QgsMeshEditor::Edit edit;
1167 for ( QgsMeshEditor::Edit &edit :
mEdits )
1174 , mfacesToRemoveIndexes( facesToRemoveIndexes )
1176 setText( QObject::tr(
"Remove %n face(s)",
nullptr, facesToRemoveIndexes.count() ) );
1181 if ( !mfacesToRemoveIndexes.isEmpty() )
1183 QgsMeshEditor::Edit edit;
1184 mMeshEditor->applyRemoveFaces( edit, mfacesToRemoveIndexes );
1187 mfacesToRemoveIndexes.clear();
1191 for ( QgsMeshEditor::Edit &edit :
mEdits )
1206 return mTriangularMesh->nativeExtent();
1212 return !mUndoStack->isClean();
1219 mTopologicalMesh.reindex();
1220 mUndoStack->clear();
1222 mValidFacesCount = mMesh->faceCount();
1223 mValidVerticesCount = mMesh->vertexCount();
1230 if ( !mTopologicalMesh.renumber() )
1235 mValidFacesCount = mMesh->faceCount();
1236 mValidVerticesCount = mMesh->vertexCount();
1245 return mTopologicalMesh.freeVerticesIndexes();
1250 return mTopologicalMesh.isVertexOnBoundary( vertexIndex );
1255 return mTopologicalMesh.isVertexFree( vertexIndex );
1260 return mTopologicalMesh.vertexCirculator( vertexIndex );
1265 return mTopologicalMesh;
1270 return mTriangularMesh;
1275 , mVerticesIndexes( verticesIndexes )
1276 , mNewValues( newValues )
1278 setText( QObject::tr(
"Change %n vertices Z Value",
nullptr, verticesIndexes.count() ) );
1283 if ( !mVerticesIndexes.isEmpty() )
1285 QgsMeshEditor::Edit edit;
1286 mMeshEditor->applyChangeZValue( edit, mVerticesIndexes, mNewValues );
1288 mVerticesIndexes.clear();
1293 for ( QgsMeshEditor::Edit &edit :
mEdits )
1300 , mVerticesIndexes( verticesIndexes )
1301 , mNewValues( newValues )
1303 setText( QObject::tr(
"Move %n vertices",
nullptr, verticesIndexes.count() ) );
1308 if ( !mVerticesIndexes.isEmpty() )
1310 QgsMeshEditor::Edit edit;
1311 mMeshEditor->applyChangeXYValue( edit, mVerticesIndexes, mNewValues );
1313 mVerticesIndexes.clear();
1318 for ( QgsMeshEditor::Edit &edit :
mEdits )
1326 , mVerticesIndexes( verticesIndexes )
1327 , mNewCoordinates( newCoordinates )
1329 setText( QObject::tr(
"Transform %n vertices coordinates",
nullptr, verticesIndexes.count() ) );
1334 if ( !mVerticesIndexes.isEmpty() )
1336 QgsMeshEditor::Edit editXY;
1337 QList<QgsPointXY> newXY;
1338 newXY.reserve( mNewCoordinates.count() );
1339 QgsMeshEditor::Edit editZ;
1341 newZ.reserve( mNewCoordinates.count() );
1343 for (
const QgsPoint &pt : std::as_const( mNewCoordinates ) )
1346 newZ.append( pt.z() );
1349 mMeshEditor->applyChangeXYValue( editXY, mVerticesIndexes, newXY );
1351 mMeshEditor->applyChangeZValue( editZ, mVerticesIndexes, newZ );
1353 mVerticesIndexes.clear();
1354 mNewCoordinates.clear();
1358 for ( QgsMeshEditor::Edit &edit :
mEdits )
1366 , mVertexIndex1( vertexIndex1 )
1367 , mVertexIndex2( vertexIndex2 )
1369 setText( QObject::tr(
"Flip edge" ) );
1374 if ( mVertexIndex1 >= 0 && mVertexIndex2 >= 0 )
1376 QgsMeshEditor::Edit edit;
1377 mMeshEditor->applyFlipEdge( edit, mVertexIndex1, mVertexIndex2 );
1384 for ( QgsMeshEditor::Edit &edit :
mEdits )
1391 , mVertexIndex1( vertexIndex1 )
1392 , mVertexIndex2( vertexIndex2 )
1394 setText( QObject::tr(
"Merge faces" ) );
1399 if ( mVertexIndex1 >= 0 && mVertexIndex2 >= 0 )
1401 QgsMeshEditor::Edit edit;
1402 mMeshEditor->applyMerge( edit, mVertexIndex1, mVertexIndex2 );
1409 for ( QgsMeshEditor::Edit &edit :
mEdits )
1416 , mFaceIndexes( faceIndexes )
1418 setText( QObject::tr(
"Split %n face(s)",
nullptr, faceIndexes.count() ) );
1423 if ( !mFaceIndexes.isEmpty() )
1425 for (
int faceIndex : std::as_const( mFaceIndexes ) )
1427 QgsMeshEditor::Edit edit;
1431 mFaceIndexes.clear();
1435 for ( QgsMeshEditor::Edit &edit :
mEdits )
1442 , mAdvancedEditing( advancdEdit )
1444 setText( advancdEdit->
text() );
1449 if ( mAdvancedEditing )
1451 QgsMeshEditor::Edit edit;
1452 while ( !mAdvancedEditing->isFinished() )
1454 mMeshEditor->applyAdvancedEdit( edit, mAdvancedEditing );
1458 mAdvancedEditing =
nullptr;
1462 for ( QgsMeshEditor::Edit &edit :
mEdits )
1470 , mTolerance( tolerance )
1472 setText( QObject::tr(
"Add vertex inside face with Delaunay refinement" ) );
1477 if ( !mVertex.isEmpty() )
1479 QgsMeshEditor::Edit edit;
1481 mMeshEditor->applyAddVertex( edit, mVertex, mTolerance );
1484 QList<std::pair<int, int>> sharedEdges = innerEdges( secondNeighboringTriangularFaces() );
1486 for ( std::pair<int, int> edge : sharedEdges )
1488 if (
mMeshEditor->edgeCanBeFlipped( edge.first, edge.second ) && !
mMeshEditor->topologicalMesh().delaunayConditionForEdge( edge.first, edge.second ) )
1490 mMeshEditor->applyFlipEdge( edit, edge.first, edge.second );
1499 for ( QgsMeshEditor::Edit &edit :
mEdits )
1504QSet<int> QgsMeshLayerUndoCommandAddVertexInFaceWithDelaunayRefinement::secondNeighboringTriangularFaces()
1506 const int vIndex =
mMeshEditor->topologicalMesh().mesh()->vertexCount() - 1;
1507 const QList<int> firstNeighborFaces =
mMeshEditor->topologicalMesh().facesAroundVertex( vIndex );
1508 QSet<int> firstNeighborVertices;
1509 for (
int face : firstNeighborFaces )
1512 for (
int vertex : meshFace )
1514 firstNeighborVertices.insert( vertex );
1518 QSet<int> secondNeighboringFaces;
1519 for (
int vertex : firstNeighborVertices )
1521 const QList<int> faces =
mMeshEditor->topologicalMesh().facesAroundVertex( vertex );
1522 for (
int face : faces )
1524 if (
mMeshEditor->topologicalMesh().mesh()->face( face ).count() == 3 )
1525 secondNeighboringFaces.insert( face );
1528 return secondNeighboringFaces;
1531QList<std::pair<int, int>> QgsMeshLayerUndoCommandAddVertexInFaceWithDelaunayRefinement::innerEdges(
const QSet<int> &faces )
1534 QMap<std::pair<int, int>,
int> edges;
1536 for (
int faceIndex : faces )
1540 for (
int i = 0; i < face.size(); i++ )
1543 if ( next == face.size() )
1548 int minIndex = std::min( face.at( i ), face.at( next ) );
1549 int maxIndex = std::max( face.at( i ), face.at( next ) );
1550 std::pair<int, int> edge = std::pair<int, int>( minIndex, maxIndex );
1553 if ( edges.contains( edge ) )
1555 count = edges.take( edge );
1559 edges.insert( edge, count );
1563 QList<std::pair<int, int>> sharedEdges;
1565 for (
auto it = edges.begin(); it != edges.end(); it++ )
1567 if ( it.value() == 2 )
1569 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...
Q_INVOKABLE 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,...
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.
void invalidateZMinMax()
Invalidates the minimum and maximum elevation values, therefoe when querying zMinimum() or zMaximum()...