19#include "moc_qgsmesheditor.cpp"
34 : QObject( meshLayer )
35 , mMesh( meshLayer ? meshLayer->nativeMesh() : nullptr )
36 , mTriangularMesh( meshLayer ? meshLayer->triangularMeshByLodIndex( 0 ) : nullptr )
37 , mUndoStack( meshLayer ? meshLayer->undoStack() : nullptr )
49 , mTriangularMesh( triangularMesh )
51 mUndoStack =
new QUndoStack(
this );
57 std::unique_ptr<QgsMeshDatasetGroup> zValueDatasetGroup = std::make_unique<QgsMeshVerticesElevationDatasetGroup>( tr(
"vertices Z value" ), mMesh );
63 mZValueDatasetGroup = zValueDatasetGroup.get();
65 return zValueDatasetGroup.release();
80 for (
int vi : freeVertices )
108 mTriangularMesh->
update( mMesh );
135 auto faceIt = mMesh->
faces.begin();
136 while ( faceIt != mMesh->
faces.end() )
139 faceIt = mMesh->
faces.erase( faceIt );
165 Q_ASSERT( vertexIndexes.count() == vertices.count() );
167 QVector<QgsPoint> ring;
168 for (
int i = 0; i < vertices.size(); ++i )
170 const QgsPoint &vertex = vertices[i];
171 ring.append( vertex );
173 std::unique_ptr< QgsPolygon > polygon = std::make_unique< QgsPolygon >();
175 const QgsGeometry newFaceGeom( polygon.release() );
177 geomEngine->prepareGeometry();
179 const QgsRectangle boundingBox = newFaceGeom.boundingBox();
180 int newFaceSize = vertexIndexes.count();
182 if ( !concernedFaceIndex.isEmpty() )
186 for (
const int faceIndex : concernedFaceIndex )
189 int existingFaceSize = existingFace.count();
190 bool shareVertex =
false;
191 for (
int i = 0; i < existingFaceSize; ++i )
193 if ( vertexIndexes.contains( existingFace.at( i ) ) )
202 for (
int i = 0; i < existingFaceSize; ++i )
204 int index1 = existingFace.at( i );
205 int index2 = existingFace.at( ( i + 1 ) % existingFaceSize );
210 if ( ! vertexIndexes.contains( index1 ) && !vertexIndexes.contains( index2 ) )
213 if ( geomEngine->intersects( edgeGeom.
constGet() ) )
218 for (
int vi = 0; vi < vertexIndexes.count(); ++vi )
220 int vertInNewFace1 = vertexIndexes.at( vi );
221 int vertInNewFace2 = vertexIndexes.at( ( vi + 1 ) % newFaceSize );
222 bool hasToBeTest =
false;
224 if ( vertInNewFace1 != -1 && vertInNewFace2 != -1 )
226 hasToBeTest = vertInNewFace1 != index1 &&
227 vertInNewFace2 != index2 &&
228 vertInNewFace1 != index2 &&
229 vertInNewFace2 != index1;
233 if ( vertInNewFace1 == -1 )
234 hasToBeTest &= vertInNewFace2 != index1 && vertInNewFace2 != index2;
237 if ( vertInNewFace2 == -1 )
238 hasToBeTest &= vertInNewFace1 != index1 && vertInNewFace1 != index2;
244 const QgsMeshVertex &nv2 = vertices.at( ( vi + 1 ) % newFaceSize );
256 const QgsGeometry existingFaceGeom = QgsMeshUtils::toGeometry( existingFace, mTriangularMesh->
vertices() );
257 if ( geomEngine->intersects( existingFaceGeom.
constGet() ) )
265 for (
const int freeVertexIndex : freeVertices )
267 if ( vertexIndexes.contains( freeVertexIndex ) )
271 if ( geomEngine->contains( &vertex ) )
280 const QList<int> newFaceVerticesIndexes( face.toList() );
281 QList<QgsMeshVertex> allVertices;
282 allVertices.reserve( face.count() );
284 allVertices.append( mTriangularMesh->
vertices().at( i ) );
296 QVector<QgsMeshFace> facesToAdd = prepareFaces( {face}, error );
319 const QList<int> face = prepareFaceWithNewVertices( verticesIndex, newVertices, error );
321 if ( face.isEmpty() )
330 int size = face.size();
331 QList<QgsMeshVertex> allVertices;
332 allVertices.reserve( verticesIndex.size() );
334 for (
int i = 0; i < size; ++i )
336 int index = face.at( i );
339 if ( newVertPos >= newVertices.count() )
341 allVertices.append( newVertices.at( newVertPos++ ) );
345 allVertices.append( mTriangularMesh->
vertices().at( index ) );
350 int prevIndex = face.at( ( i - 1 + size ) % size );
351 int nextIndex = face.at( ( i + 1 ) % size );
358 if ( prevOppVertex == nextIndex )
365 if ( nextOppVertex == prevIndex )
368 if ( nextIndex != nextOppVertex && prevIndex != prevOppVertex )
375void QgsMeshEditor::applyEdit( QgsMeshEditor::Edit &edit )
377 mTopologicalMesh.
applyChanges( edit.topologicalChanges );
378 mTriangularMesh->
applyChanges( edit.triangularMeshChanges );
380 if ( mZValueDatasetGroup &&
381 ( !edit.topologicalChanges.newVerticesZValues().isEmpty() ||
382 !edit.topologicalChanges.verticesToRemoveIndexes().isEmpty() ||
383 !edit.topologicalChanges.addedVertices().isEmpty() ) )
386 updateElementsCount( edit.topologicalChanges );
389void QgsMeshEditor::reverseEdit( QgsMeshEditor::Edit &edit )
392 mTriangularMesh->
reverseChanges( edit.triangularMeshChanges, *mMesh );
394 if ( mZValueDatasetGroup &&
395 ( !edit.topologicalChanges.newVerticesZValues().isEmpty() ||
396 !edit.topologicalChanges.verticesToRemoveIndexes().isEmpty() ||
397 !edit.topologicalChanges.addedVertices().isEmpty() ) )
400 updateElementsCount( edit.topologicalChanges,
false );
403void QgsMeshEditor::applyAddVertex( QgsMeshEditor::Edit &edit,
const QgsMeshVertex &vertex,
double tolerance )
408 int faceEdgeIntersect = -1;
409 int edgePosition = -1;
413 if (
edgeIsClose( vertexInTriangularCoordinate, tolerance, faceEdgeIntersect, edgePosition ) )
421 if ( includingFaceIndex != -1 )
422 topologicChanges = mTopologicalMesh.
addVertexInFace( includingFaceIndex, vertex );
427 applyEditOnTriangularMesh( edit, topologicChanges );
429 if ( mZValueDatasetGroup )
432 updateElementsCount( edit.topologicalChanges );
435bool QgsMeshEditor::applyRemoveVertexFillHole( QgsMeshEditor::Edit &edit,
int vertexIndex )
441 applyEditOnTriangularMesh( edit, changes );
443 if ( mZValueDatasetGroup )
446 updateElementsCount( edit.topologicalChanges );
453void QgsMeshEditor::applyRemoveVerticesWithoutFillHole( QgsMeshEditor::Edit &edit,
const QList<int> &verticesIndexes )
455 applyEditOnTriangularMesh( edit, mTopologicalMesh.
removeVertices( verticesIndexes ) );
457 if ( mZValueDatasetGroup )
460 updateElementsCount( edit.topologicalChanges );
465 applyEditOnTriangularMesh( edit, mTopologicalMesh.
addFaces( faces ) );
467 updateElementsCount( edit.topologicalChanges );
470void QgsMeshEditor::applyRemoveFaces( QgsMeshEditor::Edit &edit,
const QList<int> &faceToRemoveIndex )
472 applyEditOnTriangularMesh( edit, mTopologicalMesh.
removeFaces( faceToRemoveIndex ) );
474 updateElementsCount( edit.topologicalChanges );
477void QgsMeshEditor::applyChangeZValue( QgsMeshEditor::Edit &edit,
const QList<int> &verticesIndexes,
const QList<double> &newValues )
479 applyEditOnTriangularMesh( edit, mTopologicalMesh.
changeZValue( verticesIndexes, newValues ) );
481 if ( mZValueDatasetGroup )
485void QgsMeshEditor::applyChangeXYValue( QgsMeshEditor::Edit &edit,
const QList<int> &verticesIndexes,
const QList<QgsPointXY> &newValues )
487 applyEditOnTriangularMesh( edit, mTopologicalMesh.
changeXYValue( verticesIndexes, newValues ) );
490void QgsMeshEditor::applyFlipEdge( QgsMeshEditor::Edit &edit,
int vertexIndex1,
int vertexIndex2 )
492 applyEditOnTriangularMesh( edit, mTopologicalMesh.
flipEdge( vertexIndex1, vertexIndex2 ) );
494 updateElementsCount( edit.topologicalChanges );
497void QgsMeshEditor::applyMerge( QgsMeshEditor::Edit &edit,
int vertexIndex1,
int vertexIndex2 )
499 applyEditOnTriangularMesh( edit, mTopologicalMesh.
merge( vertexIndex1, vertexIndex2 ) );
501 updateElementsCount( edit.topologicalChanges );
504void QgsMeshEditor::applySplit( QgsMeshEditor::Edit &edit,
int faceIndex )
506 applyEditOnTriangularMesh( edit, mTopologicalMesh.
splitFace( faceIndex ) );
508 updateElementsCount( edit.topologicalChanges );
513 applyEditOnTriangularMesh( edit, editing->
apply(
this ) );
515 updateElementsCount( edit.topologicalChanges );
517 if ( mZValueDatasetGroup )
526 edit.topologicalChanges = topologicChanges;
527 edit.triangularMeshChanges = triangularChanges;
573 point.
y() - tolerance,
574 point.
x() + tolerance,
575 point.
y() + tolerance );
578 double minDist = std::numeric_limits<double>::max();
580 double epsilon = std::numeric_limits<double>::epsilon() * tolerance;
581 for (
const int nativeFaceIndex : nativeFaces )
584 const int faceSize = face.size();
585 for (
int i = 0; i < faceSize; ++i )
601 if ( dist < tolerance && dist < minDist )
603 faceIndex = nativeFaceIndex;
610 if ( edgePosition != -1 )
619 return mValidFacesCount;
624 return mValidVerticesCount;
629 return mMaximumVerticesPerFace;
646 if ( triangleIndex == -1 )
667 return mTopologicalMesh.
canBeMerged( vertexIndex1, vertexIndex2 );
680 return mTopologicalMesh.
canBeSplit( faceIndex );
685 QList<int> faceIndexesSplittable;
687 for (
const int faceIndex : faceIndexes )
689 faceIndexesSplittable.append( faceIndex );
691 if ( faceIndexesSplittable.isEmpty() )
696 return faceIndexesSplittable.count();
699QVector<QgsMeshFace> QgsMeshEditor::prepareFaces(
const QVector<QgsMeshFace> &faces,
QgsMeshEditingError &error )
const
701 QVector<QgsMeshFace> treatedFaces = faces;
705 for (
int i = 0; i < treatedFaces.count(); ++i )
708 if ( mMaximumVerticesPerFace != 0 && face.count() > mMaximumVerticesPerFace )
722QList<int> QgsMeshEditor::prepareFaceWithNewVertices(
const QList<int> &face,
const QList<QgsMeshVertex> &newVertices,
QgsMeshEditingError &error )
const
724 if ( mMaximumVerticesPerFace != 0 && face.count() > mMaximumVerticesPerFace )
730 int faceSize = face.count();
731 QVector<QgsMeshVertex> vertices( faceSize );
732 int newVertexPos = 0;
733 for (
int i = 0; i < faceSize; ++i )
735 if ( face.at( i ) == -1 )
737 if ( newVertexPos >= newVertices.count() )
739 vertices[i] = newVertices.at( newVertexPos++ );
741 else if ( face.at( i ) >= 0 )
743 if ( face.at( i ) >= mTriangularMesh->
vertices().count() )
748 vertices[i] = mTriangularMesh->
vertices().at( face.at( i ) );
760 bool clockwise =
false;
766 QList<int> newFace = face;
767 for (
int i = 0; i < faceSize / 2; ++i )
769 int temp = newFace[i];
770 newFace[i] = face.at( faceSize - i - 1 );
771 newFace[faceSize - i - 1] = temp;
783 QVector<QgsMeshFace> facesToAdd = prepareFaces( faces, error );
807 mUndoStack->beginMacro( tr(
"Add a face with new %n vertices",
nullptr, newVertices.count() ) );
811 for (
int i = 0; i < vertexIndexes.count(); ++i )
813 int index = vertexIndexes.at( i );
815 face[i] = newVertexIndex++;
821 mUndoStack->endMacro();
828 QVector<QgsMeshVertex> verticesInLayerCoordinate( vertices.count() );
829 int ignoredVertex = 0;
830 for (
int i = 0; i < vertices.count(); ++i )
832 const QgsPointXY &pointInTriangularMesh = vertices.at( i );
833 bool isTooClose =
false;
835 if ( triangleIndex != -1 )
838 for (
int j = 0; j < 3; ++j )
841 double dist = pointInTriangularMesh.
distance( facePoint );
842 if ( dist < tolerance )
855 if ( verticesInLayerCoordinate.at( i ).isEmpty() )
859 if ( ignoredVertex < vertices.count() )
864 int effectivlyAddedVertex = vertices.count() - ignoredVertex;
866 return effectivlyAddedVertex;
878 QList<int> verticesIndexes = verticesToRemoveIndexes;
880 QSet<int> concernedNativeFaces;
881 for (
const int vi : std::as_const( verticesIndexes ) )
884 concernedNativeFaces.unite( QSet< int >( faces.begin(), faces.end() ) );
898 QList<int> remainingVertices;
901 return remainingVertices;
912 for (
const int faceIndex : facesToCheck )
915 int faceSize = face.count();
916 QVector<QgsPointXY> pointsInTriangularMeshCoordinate( faceSize );
917 QVector<QgsPointXY> points( faceSize );
918 for (
int i = 0; i < faceSize; ++i )
921 int ip1 = face[( i + 1 ) % faceSize];
922 int ip2 = face[( i + 2 ) % faceSize];
928 double ux = p0.x() - p1.
x();
929 double uy = p0.y() - p1.
y();
930 double vx = p2.
x() - p1.
x();
931 double vy = p2.
y() - p1.
y();
933 double crossProduct = ux * vy - uy * vx;
934 if ( crossProduct >= 0 )
943 QList<int> otherFaceIndexes =
946 for (
const int otherFaceIndex : otherFaceIndexes )
949 int existingFaceSize = otherFace.count();
950 bool shareVertex =
false;
951 for (
int i = 0; i < existingFaceSize; ++i )
953 if ( face.contains( otherFace.at( i ) ) )
962 for (
int i = 0; i < existingFaceSize; ++i )
964 int index1 = otherFace.at( i );
965 int index2 = otherFace.at( ( i + 1 ) % existingFaceSize );
966 if ( ! face.contains( index1 ) && !face.contains( index2 ) )
968 const QgsPointXY &v1 = transformFunction( index1 );
969 const QgsPointXY &v2 = transformFunction( index2 );
978 QVector<QgsPointXY> otherPoints( existingFaceSize );
979 for (
int i = 0; i < existingFaceSize; ++i )
980 otherPoints[i] = transformFunction( otherFace.at( i ) );
982 if ( deformedFace.
intersects( existingFaceGeom ) )
988 for (
const int vertexIndex : freeVerticesIndex )
990 const QgsPointXY &mapPoint = transformFunction( vertexIndex );
991 if ( deformedFace.
contains( &mapPoint ) )
998 for (
const int vertexIndex : freeVerticesIndex )
1000 const QgsMeshVertex &newFreeVertexPosition = transformFunction( vertexIndex );
1004 if ( originalIncludingFace != -1 )
1009 int faceSize = face.count();
1010 QVector<QgsPointXY> points( faceSize );
1011 for (
int i = 0; i < faceSize; ++i )
1012 points[i] = transformFunction( face.at( i ) );
1015 const QgsPointXY ptXY( newFreeVertexPosition );
1016 if ( deformedFace.
contains( &ptXY ) )
1043 mUndoStack->clear();
1047 : mMeshEditor( meshEditor )
1056 for (
int i =
mEdits.count() - 1; i >= 0; --i )
1065 for ( QgsMeshEditor::Edit &edit :
mEdits )
1071 , mVertices( vertices )
1072 , mTolerance( tolerance )
1074 setText( QObject::tr(
"Add %n vertices",
nullptr, mVertices.count() ) );
1079 if ( !mVertices.isEmpty() )
1081 for (
int i = 0; i < mVertices.count(); ++i )
1086 QgsMeshEditor::Edit edit;
1087 mMeshEditor->applyAddVertex( edit, vertex, mTolerance );
1094 for ( QgsMeshEditor::Edit &edit :
mEdits )
1101 const QList<int> &verticesToRemoveIndexes,
1102 QList<int> *remainingVerticesPointer )
1104 , mVerticesToRemoveIndexes( verticesToRemoveIndexes )
1105 , mRemainingVerticesPointer( remainingVerticesPointer )
1107 setText( QObject::tr(
"Remove %n vertices filling holes",
nullptr, verticesToRemoveIndexes.count() ) );
1112 int initialVertexCount = mVerticesToRemoveIndexes.count();
1113 if ( !mVerticesToRemoveIndexes.isEmpty() )
1115 QgsMeshEditor::Edit edit;
1116 QList<int> vertexToRetry;
1117 while ( !mVerticesToRemoveIndexes.isEmpty() )
1120 for (
const int &vertex : std::as_const( mVerticesToRemoveIndexes ) )
1122 if (
mMeshEditor->applyRemoveVertexFillHole( edit, vertex ) )
1125 vertexToRetry.append( vertex );
1128 if ( vertexToRetry.count() == mVerticesToRemoveIndexes.count() )
1131 mVerticesToRemoveIndexes = vertexToRetry;
1134 if ( initialVertexCount == mVerticesToRemoveIndexes.count() )
1135 setObsolete(
true );
1137 if ( mRemainingVerticesPointer )
1138 *mRemainingVerticesPointer = mVerticesToRemoveIndexes;
1140 mRemainingVerticesPointer =
nullptr;
1142 mVerticesToRemoveIndexes.clear();
1146 for ( QgsMeshEditor::Edit &edit :
mEdits )
1154 const QList<int> &verticesToRemoveIndexes )
1156 , mVerticesToRemoveIndexes( verticesToRemoveIndexes )
1158 setText( QObject::tr(
"Remove %n vertices without filling holes",
nullptr, verticesToRemoveIndexes.count() ) ) ;
1163 if ( !mVerticesToRemoveIndexes.isEmpty() )
1165 QgsMeshEditor::Edit edit;
1167 mMeshEditor->applyRemoveVerticesWithoutFillHole( edit, mVerticesToRemoveIndexes );
1170 mVerticesToRemoveIndexes.clear();
1174 for ( QgsMeshEditor::Edit &edit :
mEdits )
1183 setText( QObject::tr(
"Add %n face(s)",
nullptr, faces.
meshFaces().count() ) );
1190 QgsMeshEditor::Edit edit;
1198 for ( QgsMeshEditor::Edit &edit :
mEdits )
1205 , mfacesToRemoveIndexes( facesToRemoveIndexes )
1207 setText( QObject::tr(
"Remove %n face(s)",
nullptr, facesToRemoveIndexes.count() ) );
1212 if ( !mfacesToRemoveIndexes.isEmpty() )
1214 QgsMeshEditor::Edit edit;
1215 mMeshEditor->applyRemoveFaces( edit, mfacesToRemoveIndexes );
1218 mfacesToRemoveIndexes.clear();
1222 for ( QgsMeshEditor::Edit &edit :
mEdits )
1239 return !mUndoStack->isClean();
1247 mUndoStack->clear();
1257 if ( !mTopologicalMesh.
renumber() )
1292 return mTopologicalMesh;
1297 return mTriangularMesh;
1302 , mVerticesIndexes( verticesIndexes )
1303 , mNewValues( newValues )
1305 setText( QObject::tr(
"Change %n vertices Z Value",
nullptr, verticesIndexes.count() ) );
1310 if ( !mVerticesIndexes.isEmpty() )
1312 QgsMeshEditor::Edit edit;
1313 mMeshEditor->applyChangeZValue( edit, mVerticesIndexes, mNewValues );
1315 mVerticesIndexes.clear();
1320 for ( QgsMeshEditor::Edit &edit :
mEdits )
1327 , mVerticesIndexes( verticesIndexes )
1328 , mNewValues( newValues )
1330 setText( QObject::tr(
"Move %n vertices",
nullptr, verticesIndexes.count() ) );
1335 if ( !mVerticesIndexes.isEmpty() )
1337 QgsMeshEditor::Edit edit;
1338 mMeshEditor->applyChangeXYValue( edit, mVerticesIndexes, mNewValues );
1340 mVerticesIndexes.clear();
1345 for ( QgsMeshEditor::Edit &edit :
mEdits )
1353 , mVerticesIndexes( verticesIndexes )
1354 , mNewCoordinates( newCoordinates )
1356 setText( QObject::tr(
"Transform %n vertices coordinates",
nullptr, verticesIndexes.count() ) );
1361 if ( !mVerticesIndexes.isEmpty() )
1363 QgsMeshEditor::Edit editXY;
1364 QList<QgsPointXY> newXY;
1365 newXY.reserve( mNewCoordinates.count() );
1366 QgsMeshEditor::Edit editZ;
1368 newZ.reserve( mNewCoordinates.count() );
1370 for (
const QgsPoint &pt : std::as_const( mNewCoordinates ) )
1373 newZ.append( pt.z() );
1376 mMeshEditor->applyChangeXYValue( editXY, mVerticesIndexes, newXY );
1378 mMeshEditor->applyChangeZValue( editZ, mVerticesIndexes, newZ );
1380 mVerticesIndexes.clear();
1381 mNewCoordinates.clear();
1385 for ( QgsMeshEditor::Edit &edit :
mEdits )
1394 , mVertexIndex1( vertexIndex1 )
1395 , mVertexIndex2( vertexIndex2 )
1397 setText( QObject::tr(
"Flip edge" ) );
1402 if ( mVertexIndex1 >= 0 && mVertexIndex2 >= 0 )
1404 QgsMeshEditor::Edit edit;
1405 mMeshEditor->applyFlipEdge( edit, mVertexIndex1, mVertexIndex2 );
1412 for ( QgsMeshEditor::Edit &edit :
mEdits )
1419 , mVertexIndex1( vertexIndex1 )
1420 , mVertexIndex2( vertexIndex2 )
1422 setText( QObject::tr(
"Merge faces" ) );
1427 if ( mVertexIndex1 >= 0 && mVertexIndex2 >= 0 )
1429 QgsMeshEditor::Edit edit;
1430 mMeshEditor->applyMerge( edit, mVertexIndex1, mVertexIndex2 );
1437 for ( QgsMeshEditor::Edit &edit :
mEdits )
1444 , mFaceIndexes( faceIndexes )
1446 setText( QObject::tr(
"Split %n face(s)",
nullptr, faceIndexes.count() ) );
1451 if ( !mFaceIndexes.isEmpty() )
1453 for (
int faceIndex : std::as_const( mFaceIndexes ) )
1455 QgsMeshEditor::Edit edit;
1459 mFaceIndexes.clear();
1463 for ( QgsMeshEditor::Edit &edit :
mEdits )
1470 , mAdvancedEditing( advancdEdit )
1472 setText( advancdEdit->
text() );
1477 if ( mAdvancedEditing )
1479 QgsMeshEditor::Edit edit;
1482 mMeshEditor->applyAdvancedEdit( edit, mAdvancedEditing );
1486 mAdvancedEditing =
nullptr;
1490 for ( QgsMeshEditor::Edit &edit :
mEdits )
1501 , mTolerance( tolerance )
1503 setText( QObject::tr(
"Add vertex inside face with Delaunay refinement" ) );
1510 QgsMeshEditor::Edit edit;
1512 mMeshEditor->applyAddVertex( edit, mVertex, mTolerance );
1515 QList<std::pair<int, int>> sharedEdges = innerEdges( secondNeighboringTriangularFaces() );
1517 for ( std::pair<int, int> edge : sharedEdges )
1519 if (
mMeshEditor->edgeCanBeFlipped( edge.first, edge.second ) && !
mMeshEditor->topologicalMesh().delaunayConditionForEdge( edge.first, edge.second ) )
1521 mMeshEditor->applyFlipEdge( edit, edge.first, edge.second );
1530 for ( QgsMeshEditor::Edit &edit :
mEdits )
1535QSet<int> QgsMeshLayerUndoCommandAddVertexInFaceWithDelaunayRefinement::secondNeighboringTriangularFaces()
1537 const int vIndex =
mMeshEditor->topologicalMesh().mesh()->vertexCount() - 1;
1538 const QList<int> firstNeighborFaces =
mMeshEditor->topologicalMesh().facesAroundVertex( vIndex );
1539 QSet<int> firstNeighborVertices;
1540 for (
int face : firstNeighborFaces )
1543 for (
int vertex : meshFace )
1545 firstNeighborVertices.insert( vertex );
1549 QSet<int> secondNeighboringFaces;
1550 for (
int vertex : firstNeighborVertices )
1552 const QList<int> faces =
mMeshEditor->topologicalMesh().facesAroundVertex( vertex );
1553 for (
int face : faces )
1555 if (
mMeshEditor->topologicalMesh().mesh()->face( face ).count() == 3 )
1556 secondNeighboringFaces.insert( face );
1559 return secondNeighboringFaces;
1562QList<std::pair<int, int>> QgsMeshLayerUndoCommandAddVertexInFaceWithDelaunayRefinement::innerEdges(
const QSet<int> &faces )
1565 QMap<std::pair<int, int>,
int> edges;
1567 for (
int faceIndex : faces )
1571 for (
int i = 0; i < face.size(); i++ )
1574 if ( next == face.size() )
1579 int minIndex = std::min( face.at( i ), face.at( next ) );
1580 int maxIndex = std::max( face.at( i ), face.at( next ) );
1581 std::pair<int, int> edge = std::pair<int, int>( minIndex, maxIndex );
1584 if ( edges.contains( edge ) )
1586 count = edges.take( edge );
1590 edges.insert( edge, count );
1594 QList<std::pair<int, int>> sharedEdges;
1596 for (
auto it = edges.begin(); it != edges.end(); it++ )
1598 if ( it.value() == 2 )
1600 sharedEdges.push_back( it.key() );
The Qgis class provides global constants for use throughout the application.
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.
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.
Line string geometry type, with support for z-dimension and m-values.
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.
Qgis::MeshEditingErrorType errorType
QgsMeshEditingError()
Constructor of the default error, that is NoError.
Class that makes edit operation on a mesh.
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...
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.
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,...
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.
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.
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.
A class to represent a 2D point.
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.
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...
static QgsMeshEditingError checkTopologyOfVerticesAsFace(const QVector< QgsMeshVertex > &vertices, bool &clockwise)
Checks the topology of the vertices as they are contained in a face and returns indication on directi...
Changes changeZValue(const QList< int > &verticesIndexes, const QList< double > &newValues)
Changes the Z values of the vertices with indexes in vertices indexes with the values in newValues.
static QgsTopologicalMesh createTopologicalMesh(QgsMesh *mesh, int maxVerticesPerFace, QgsMeshEditingError &error)
Creates a topologicaly consistent mesh with mesh, this static method modifies mesh to be topological ...
bool isVertexFree(int vertexIndex) const
Returns whether the vertex is a free vertex.
static QgsMeshEditingError counterClockwiseFaces(QgsMeshFace &face, QgsMesh *mesh)
Checks the topology of the face and sets it counter clockwise if necessary.
Changes removeVertexFillHole(int vertexIndex)
Removes the vertex with index vertexIndex.
void applyChanges(const Changes &changes)
Applies the changes.
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...
QgsMeshEditingError facesCanBeRemoved(const QList< int > &facesIndexes)
Returns whether faces with index in faceIndexes can be removed/ The method an error object with type ...
void reverseChanges(const Changes &changes)
Reverses the changes.
Changes addFaces(const TopologicalFaces &topologicFaces)
Adds faces topologicFaces to the topologic mesh.
Changes merge(int vertexIndex1, int vertexIndex2)
Merges faces separated by vertices with indexes vertexIndex1 and vertexIndex2 The method returns a in...
Changes removeFaces(const QList< int > &facesIndexes)
Removes faces with index in faceIndexes.
QList< int > freeVerticesIndexes() const
Returns a list of vertices are not linked to any faces.
bool edgeCanBeFlipped(int vertexIndex1, int vertexIndex2) const
Returns true if the edge can be flipped (only available for edge shared by two faces with 3 vertices)
Changes addVertexInFace(int faceIndex, const QgsMeshVertex &vertex)
Adds a vertex in the face with index faceIndex.
bool canBeMerged(int vertexIndex1, int vertexIndex2) const
Returns true if faces separated by vertices with indexes vertexIndex1 and vertexIndex2 can be merged.
QList< int > facesAroundVertex(int vertexIndex) const
Returns the indexes of faces that are around the vertex with index vertexIndex.
bool canBeSplit(int faceIndex) const
Returns true if face with index faceIndex can be split.
Changes addFreeVertex(const QgsMeshVertex &vertex)
Adds a free vertex in the face, that is a vertex that is not included or linked with any faces.
Changes insertVertexInFacesEdge(int faceIndex, int position, const QgsMeshVertex &vertex)
Inserts a vertex in the edge of face with index faceIndex at position .
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.
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.