19#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 )
50 , mTriangularMesh( triangularMesh )
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.release();
81 for (
int vi : freeVertices )
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 >();
176 const QgsGeometry newFaceGeom( polygon.release() );
178 geomEngine->prepareGeometry();
180 const QgsRectangle boundingBox = newFaceGeom.boundingBox();
181 int newFaceSize = vertexIndexes.count();
183 if ( !concernedFaceIndex.isEmpty() )
187 for (
const int faceIndex : concernedFaceIndex )
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 );
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 &&
228 vertInNewFace2 != index2 &&
229 vertInNewFace1 != index2 &&
230 vertInNewFace2 != index1;
234 if ( vertInNewFace1 == -1 )
235 hasToBeTest &= vertInNewFace2 != index1 && vertInNewFace2 != index2;
238 if ( vertInNewFace2 == -1 )
239 hasToBeTest &= vertInNewFace1 != index1 && vertInNewFace1 != index2;
245 const QgsMeshVertex &nv2 = vertices.at( ( vi + 1 ) % newFaceSize );
258 if ( geomEngine->intersects( existingFaceGeom.
constGet() ) )
266 for (
const int freeVertexIndex : freeVertices )
268 if ( vertexIndexes.contains( freeVertexIndex ) )
272 if ( geomEngine->contains( &vertex ) )
281 const QList<int> newFaceVerticesIndexes( face.toList() );
282 QList<QgsMeshVertex> allVertices;
283 allVertices.reserve( face.count() );
285 allVertices.append( mTriangularMesh->
vertices().at( i ) );
297 QVector<QgsMeshFace> facesToAdd = prepareFaces( {face}, error );
320 const QList<int> face = prepareFaceWithNewVertices( verticesIndex, newVertices, error );
322 if ( face.isEmpty() )
331 int size = face.size();
332 QList<QgsMeshVertex> allVertices;
333 allVertices.reserve( verticesIndex.size() );
335 for (
int i = 0; i < size; ++i )
337 int index = face.at( i );
340 if ( newVertPos >= newVertices.count() )
342 allVertices.append( newVertices.at( newVertPos++ ) );
346 allVertices.append( mTriangularMesh->
vertices().at( index ) );
351 int prevIndex = face.at( ( i - 1 + size ) % size );
352 int nextIndex = face.at( ( i + 1 ) % size );
359 if ( prevOppVertex == nextIndex )
366 if ( nextOppVertex == prevIndex )
369 if ( nextIndex != nextOppVertex && prevIndex != prevOppVertex )
376void QgsMeshEditor::applyEdit( QgsMeshEditor::Edit &edit )
378 mTopologicalMesh.
applyChanges( edit.topologicalChanges );
379 mTriangularMesh->
applyChanges( edit.triangularMeshChanges );
381 if ( mZValueDatasetGroup &&
382 ( !edit.topologicalChanges.newVerticesZValues().isEmpty() ||
383 !edit.topologicalChanges.verticesToRemoveIndexes().isEmpty() ||
384 !edit.topologicalChanges.addedVertices().isEmpty() ) )
387 updateElementsCount( edit.topologicalChanges );
390void QgsMeshEditor::reverseEdit( QgsMeshEditor::Edit &edit )
393 mTriangularMesh->
reverseChanges( edit.triangularMeshChanges, *mMesh );
395 if ( mZValueDatasetGroup &&
396 ( !edit.topologicalChanges.newVerticesZValues().isEmpty() ||
397 !edit.topologicalChanges.verticesToRemoveIndexes().isEmpty() ||
398 !edit.topologicalChanges.addedVertices().isEmpty() ) )
401 updateElementsCount( edit.topologicalChanges,
false );
404void QgsMeshEditor::applyAddVertex( QgsMeshEditor::Edit &edit,
const QgsMeshVertex &vertex,
double tolerance )
409 int faceEdgeIntersect = -1;
410 int edgePosition = -1;
414 if (
edgeIsClose( vertexInTriangularCoordinate, tolerance, faceEdgeIntersect, edgePosition ) )
422 if ( includingFaceIndex != -1 )
423 topologicChanges = mTopologicalMesh.
addVertexInFace( includingFaceIndex, vertex );
428 applyEditOnTriangularMesh( edit, topologicChanges );
430 if ( mZValueDatasetGroup )
433 updateElementsCount( edit.topologicalChanges );
436bool QgsMeshEditor::applyRemoveVertexFillHole( QgsMeshEditor::Edit &edit,
int vertexIndex )
442 applyEditOnTriangularMesh( edit, changes );
444 if ( mZValueDatasetGroup )
447 updateElementsCount( edit.topologicalChanges );
454void QgsMeshEditor::applyRemoveVerticesWithoutFillHole( QgsMeshEditor::Edit &edit,
const QList<int> &verticesIndexes )
456 applyEditOnTriangularMesh( edit, mTopologicalMesh.
removeVertices( verticesIndexes ) );
458 if ( mZValueDatasetGroup )
461 updateElementsCount( edit.topologicalChanges );
466 applyEditOnTriangularMesh( edit, mTopologicalMesh.
addFaces( faces ) );
468 updateElementsCount( edit.topologicalChanges );
471void QgsMeshEditor::applyRemoveFaces( QgsMeshEditor::Edit &edit,
const QList<int> &faceToRemoveIndex )
473 applyEditOnTriangularMesh( edit, mTopologicalMesh.
removeFaces( faceToRemoveIndex ) );
475 updateElementsCount( edit.topologicalChanges );
478void QgsMeshEditor::applyChangeZValue( QgsMeshEditor::Edit &edit,
const QList<int> &verticesIndexes,
const QList<double> &newValues )
480 applyEditOnTriangularMesh( edit, mTopologicalMesh.
changeZValue( verticesIndexes, newValues ) );
482 if ( mZValueDatasetGroup )
486void QgsMeshEditor::applyChangeXYValue( QgsMeshEditor::Edit &edit,
const QList<int> &verticesIndexes,
const QList<QgsPointXY> &newValues )
488 applyEditOnTriangularMesh( edit, mTopologicalMesh.
changeXYValue( verticesIndexes, newValues ) );
491void QgsMeshEditor::applyFlipEdge( QgsMeshEditor::Edit &edit,
int vertexIndex1,
int vertexIndex2 )
493 applyEditOnTriangularMesh( edit, mTopologicalMesh.
flipEdge( vertexIndex1, vertexIndex2 ) );
495 updateElementsCount( edit.topologicalChanges );
498void QgsMeshEditor::applyMerge( QgsMeshEditor::Edit &edit,
int vertexIndex1,
int vertexIndex2 )
500 applyEditOnTriangularMesh( edit, mTopologicalMesh.
merge( vertexIndex1, vertexIndex2 ) );
502 updateElementsCount( edit.topologicalChanges );
505void QgsMeshEditor::applySplit( QgsMeshEditor::Edit &edit,
int faceIndex )
507 applyEditOnTriangularMesh( edit, mTopologicalMesh.
splitFace( faceIndex ) );
509 updateElementsCount( edit.topologicalChanges );
514 applyEditOnTriangularMesh( edit, editing->
apply(
this ) );
516 updateElementsCount( edit.topologicalChanges );
518 if ( mZValueDatasetGroup )
527 edit.topologicalChanges = topologicChanges;
528 edit.triangularMeshChanges = triangularChanges;
574 point.
y() - tolerance,
575 point.
x() + tolerance,
576 point.
y() + tolerance );
579 double minDist = std::numeric_limits<double>::max();
581 double epsilon = std::numeric_limits<double>::epsilon() * tolerance;
582 for (
const int nativeFaceIndex : nativeFaces )
585 const int faceSize = face.size();
586 for (
int i = 0; i < faceSize; ++i )
602 if ( dist < tolerance && dist < minDist )
604 faceIndex = nativeFaceIndex;
611 if ( edgePosition != -1 )
620 return mValidFacesCount;
625 return mValidVerticesCount;
630 return mMaximumVerticesPerFace;
647 if ( triangleIndex == -1 )
668 return mTopologicalMesh.
canBeMerged( vertexIndex1, vertexIndex2 );
681 return mTopologicalMesh.
canBeSplit( faceIndex );
686 QList<int> faceIndexesSplittable;
688 for (
const int faceIndex : faceIndexes )
690 faceIndexesSplittable.append( faceIndex );
692 if ( faceIndexesSplittable.isEmpty() )
697 return faceIndexesSplittable.count();
700QVector<QgsMeshFace> QgsMeshEditor::prepareFaces(
const QVector<QgsMeshFace> &faces,
QgsMeshEditingError &error )
const
702 QVector<QgsMeshFace> treatedFaces = faces;
706 for (
int i = 0; i < treatedFaces.count(); ++i )
709 if ( mMaximumVerticesPerFace != 0 && face.count() > mMaximumVerticesPerFace )
723QList<int> QgsMeshEditor::prepareFaceWithNewVertices(
const QList<int> &face,
const QList<QgsMeshVertex> &newVertices,
QgsMeshEditingError &error )
const
725 if ( mMaximumVerticesPerFace != 0 && face.count() > mMaximumVerticesPerFace )
731 int faceSize = face.count();
732 QVector<QgsMeshVertex> vertices( faceSize );
733 int newVertexPos = 0;
734 for (
int i = 0; i < faceSize; ++i )
736 if ( face.at( i ) == -1 )
738 if ( newVertexPos >= newVertices.count() )
740 vertices[i] = newVertices.at( newVertexPos++ );
742 else if ( face.at( i ) >= 0 )
744 if ( face.at( i ) >= mTriangularMesh->
vertices().count() )
749 vertices[i] = mTriangularMesh->
vertices().at( face.at( i ) );
761 bool clockwise =
false;
767 QList<int> newFace = face;
768 for (
int i = 0; i < faceSize / 2; ++i )
770 int temp = newFace[i];
771 newFace[i] = face.at( faceSize - i - 1 );
772 newFace[faceSize - i - 1] = temp;
784 QVector<QgsMeshFace> facesToAdd = prepareFaces( faces, error );
808 mUndoStack->beginMacro( tr(
"Add a face with new %n vertices",
nullptr, newVertices.count() ) );
812 for (
int i = 0; i < vertexIndexes.count(); ++i )
814 int index = vertexIndexes.at( i );
816 face[i] = newVertexIndex++;
822 mUndoStack->endMacro();
829 QVector<QgsMeshVertex> verticesInLayerCoordinate( vertices.count() );
830 int ignoredVertex = 0;
831 for (
int i = 0; i < vertices.count(); ++i )
833 const QgsPointXY &pointInTriangularMesh = vertices.at( i );
834 bool isTooClose =
false;
836 if ( triangleIndex != -1 )
839 for (
int j = 0; j < 3; ++j )
842 double dist = pointInTriangularMesh.
distance( facePoint );
843 if ( dist < tolerance )
856 if ( verticesInLayerCoordinate.at( i ).isEmpty() )
860 if ( ignoredVertex < vertices.count() )
865 int effectivlyAddedVertex = vertices.count() - ignoredVertex;
867 return effectivlyAddedVertex;
879 QList<int> verticesIndexes = verticesToRemoveIndexes;
881 QSet<int> concernedNativeFaces;
882 for (
const int vi : std::as_const( verticesIndexes ) )
885 concernedNativeFaces.unite( QSet< int >( faces.begin(), faces.end() ) );
899 QList<int> remainingVertices;
902 return remainingVertices;
913 for (
const int faceIndex : facesToCheck )
916 int faceSize = face.count();
917 QVector<QgsPointXY> pointsInTriangularMeshCoordinate( faceSize );
918 QVector<QgsPointXY> points( faceSize );
919 for (
int i = 0; i < faceSize; ++i )
922 int ip1 = face[( i + 1 ) % faceSize];
923 int ip2 = face[( i + 2 ) % faceSize];
929 double ux = p0.x() - p1.
x();
930 double uy = p0.y() - p1.
y();
931 double vx = p2.
x() - p1.
x();
932 double vy = p2.
y() - p1.
y();
934 double crossProduct = ux * vy - uy * vx;
935 if ( crossProduct >= 0 )
944 QList<int> otherFaceIndexes =
947 for (
const int otherFaceIndex : otherFaceIndexes )
950 int existingFaceSize = otherFace.count();
951 bool shareVertex =
false;
952 for (
int i = 0; i < existingFaceSize; ++i )
954 if ( face.contains( otherFace.at( i ) ) )
963 for (
int i = 0; i < existingFaceSize; ++i )
965 int index1 = otherFace.at( i );
966 int index2 = otherFace.at( ( i + 1 ) % existingFaceSize );
967 if ( ! face.contains( index1 ) && !face.contains( index2 ) )
969 const QgsPointXY &v1 = transformFunction( index1 );
970 const QgsPointXY &v2 = transformFunction( index2 );
979 QVector<QgsPointXY> otherPoints( existingFaceSize );
980 for (
int i = 0; i < existingFaceSize; ++i )
981 otherPoints[i] = transformFunction( otherFace.at( i ) );
983 if ( deformedFace.
intersects( existingFaceGeom ) )
989 for (
const int vertexIndex : freeVerticesIndex )
991 const QgsPointXY &mapPoint = transformFunction( vertexIndex );
992 if ( deformedFace.
contains( &mapPoint ) )
999 for (
const int vertexIndex : freeVerticesIndex )
1001 const QgsMeshVertex &newFreeVertexPosition = transformFunction( vertexIndex );
1005 if ( originalIncludingFace != -1 )
1010 int faceSize = face.count();
1011 QVector<QgsPointXY> points( faceSize );
1012 for (
int i = 0; i < faceSize; ++i )
1013 points[i] = transformFunction( face.at( i ) );
1016 const QgsPointXY ptXY( newFreeVertexPosition );
1017 if ( deformedFace.
contains( &ptXY ) )
1044 mUndoStack->clear();
1048 : mMeshEditor( meshEditor )
1057 for (
int i =
mEdits.count() - 1; i >= 0; --i )
1066 for ( QgsMeshEditor::Edit &edit :
mEdits )
1072 , mVertices( vertices )
1073 , mTolerance( tolerance )
1075 setText( QObject::tr(
"Add %n vertices",
nullptr, mVertices.count() ) );
1080 if ( !mVertices.isEmpty() )
1082 for (
int i = 0; i < mVertices.count(); ++i )
1087 QgsMeshEditor::Edit edit;
1088 mMeshEditor->applyAddVertex( edit, vertex, mTolerance );
1095 for ( QgsMeshEditor::Edit &edit :
mEdits )
1102 const QList<int> &verticesToRemoveIndexes,
1103 QList<int> *remainingVerticesPointer )
1105 , mVerticesToRemoveIndexes( verticesToRemoveIndexes )
1106 , mRemainingVerticesPointer( remainingVerticesPointer )
1108 setText( QObject::tr(
"Remove %n vertices filling holes",
nullptr, verticesToRemoveIndexes.count() ) );
1113 int initialVertexCount = mVerticesToRemoveIndexes.count();
1114 if ( !mVerticesToRemoveIndexes.isEmpty() )
1116 QgsMeshEditor::Edit edit;
1117 QList<int> vertexToRetry;
1118 while ( !mVerticesToRemoveIndexes.isEmpty() )
1121 for (
const int &vertex : std::as_const( mVerticesToRemoveIndexes ) )
1123 if (
mMeshEditor->applyRemoveVertexFillHole( edit, vertex ) )
1126 vertexToRetry.append( vertex );
1129 if ( vertexToRetry.count() == mVerticesToRemoveIndexes.count() )
1132 mVerticesToRemoveIndexes = vertexToRetry;
1135 if ( initialVertexCount == mVerticesToRemoveIndexes.count() )
1136 setObsolete(
true );
1138 if ( mRemainingVerticesPointer )
1139 *mRemainingVerticesPointer = mVerticesToRemoveIndexes;
1141 mRemainingVerticesPointer =
nullptr;
1143 mVerticesToRemoveIndexes.clear();
1147 for ( QgsMeshEditor::Edit &edit :
mEdits )
1155 const QList<int> &verticesToRemoveIndexes )
1157 , mVerticesToRemoveIndexes( verticesToRemoveIndexes )
1159 setText( QObject::tr(
"Remove %n vertices without filling holes",
nullptr, verticesToRemoveIndexes.count() ) ) ;
1164 if ( !mVerticesToRemoveIndexes.isEmpty() )
1166 QgsMeshEditor::Edit edit;
1168 mMeshEditor->applyRemoveVerticesWithoutFillHole( edit, mVerticesToRemoveIndexes );
1171 mVerticesToRemoveIndexes.clear();
1175 for ( QgsMeshEditor::Edit &edit :
mEdits )
1184 setText( QObject::tr(
"Add %n face(s)",
nullptr, faces.
meshFaces().count() ) );
1191 QgsMeshEditor::Edit edit;
1199 for ( QgsMeshEditor::Edit &edit :
mEdits )
1206 , mfacesToRemoveIndexes( facesToRemoveIndexes )
1208 setText( QObject::tr(
"Remove %n face(s)",
nullptr, facesToRemoveIndexes.count() ) );
1213 if ( !mfacesToRemoveIndexes.isEmpty() )
1215 QgsMeshEditor::Edit edit;
1216 mMeshEditor->applyRemoveFaces( edit, mfacesToRemoveIndexes );
1219 mfacesToRemoveIndexes.clear();
1223 for ( QgsMeshEditor::Edit &edit :
mEdits )
1240 return !mUndoStack->isClean();
1248 mUndoStack->clear();
1258 if ( !mTopologicalMesh.
renumber() )
1293 return mTopologicalMesh;
1298 return mTriangularMesh;
1303 , mVerticesIndexes( verticesIndexes )
1304 , mNewValues( newValues )
1306 setText( QObject::tr(
"Change %n vertices Z Value",
nullptr, verticesIndexes.count() ) );
1311 if ( !mVerticesIndexes.isEmpty() )
1313 QgsMeshEditor::Edit edit;
1314 mMeshEditor->applyChangeZValue( edit, mVerticesIndexes, mNewValues );
1316 mVerticesIndexes.clear();
1321 for ( QgsMeshEditor::Edit &edit :
mEdits )
1328 , mVerticesIndexes( verticesIndexes )
1329 , mNewValues( newValues )
1331 setText( QObject::tr(
"Move %n vertices",
nullptr, verticesIndexes.count() ) );
1336 if ( !mVerticesIndexes.isEmpty() )
1338 QgsMeshEditor::Edit edit;
1339 mMeshEditor->applyChangeXYValue( edit, mVerticesIndexes, mNewValues );
1341 mVerticesIndexes.clear();
1346 for ( QgsMeshEditor::Edit &edit :
mEdits )
1354 , mVerticesIndexes( verticesIndexes )
1355 , mNewCoordinates( newCoordinates )
1357 setText( QObject::tr(
"Transform %n vertices coordinates",
nullptr, verticesIndexes.count() ) );
1362 if ( !mVerticesIndexes.isEmpty() )
1364 QgsMeshEditor::Edit editXY;
1365 QList<QgsPointXY> newXY;
1366 newXY.reserve( mNewCoordinates.count() );
1367 QgsMeshEditor::Edit editZ;
1369 newZ.reserve( mNewCoordinates.count() );
1371 for (
const QgsPoint &pt : std::as_const( mNewCoordinates ) )
1374 newZ.append( pt.z() );
1377 mMeshEditor->applyChangeXYValue( editXY, mVerticesIndexes, newXY );
1379 mMeshEditor->applyChangeZValue( editZ, mVerticesIndexes, newZ );
1381 mVerticesIndexes.clear();
1382 mNewCoordinates.clear();
1386 for ( QgsMeshEditor::Edit &edit :
mEdits )
1395 , mVertexIndex1( vertexIndex1 )
1396 , mVertexIndex2( vertexIndex2 )
1398 setText( QObject::tr(
"Flip edge" ) );
1403 if ( mVertexIndex1 >= 0 && mVertexIndex2 >= 0 )
1405 QgsMeshEditor::Edit edit;
1406 mMeshEditor->applyFlipEdge( edit, mVertexIndex1, mVertexIndex2 );
1413 for ( QgsMeshEditor::Edit &edit :
mEdits )
1420 , mVertexIndex1( vertexIndex1 )
1421 , mVertexIndex2( vertexIndex2 )
1423 setText( QObject::tr(
"Merge faces" ) );
1428 if ( mVertexIndex1 >= 0 && mVertexIndex2 >= 0 )
1430 QgsMeshEditor::Edit edit;
1431 mMeshEditor->applyMerge( edit, mVertexIndex1, mVertexIndex2 );
1438 for ( QgsMeshEditor::Edit &edit :
mEdits )
1445 , mFaceIndexes( faceIndexes )
1447 setText( QObject::tr(
"Split %n face(s)",
nullptr, faceIndexes.count() ) );
1452 if ( !mFaceIndexes.isEmpty() )
1454 for (
int faceIndex : std::as_const( mFaceIndexes ) )
1456 QgsMeshEditor::Edit edit;
1460 mFaceIndexes.clear();
1464 for ( QgsMeshEditor::Edit &edit :
mEdits )
1471 , mAdvancedEditing( advancdEdit )
1473 setText( advancdEdit->
text() );
1478 if ( mAdvancedEditing )
1480 QgsMeshEditor::Edit edit;
1483 mMeshEditor->applyAdvancedEdit( edit, mAdvancedEditing );
1487 mAdvancedEditing =
nullptr;
1491 for ( QgsMeshEditor::Edit &edit :
mEdits )
1502 , mTolerance( tolerance )
1504 setText( QObject::tr(
"Add vertex inside face with Delaunay refinement" ) );
1511 QgsMeshEditor::Edit edit;
1513 mMeshEditor->applyAddVertex( edit, mVertex, mTolerance );
1516 QList<std::pair<int, int>> sharedEdges = innerEdges( secondNeighboringTriangularFaces() );
1518 for ( std::pair<int, int> edge : sharedEdges )
1520 if (
mMeshEditor->edgeCanBeFlipped( edge.first, edge.second ) && !
mMeshEditor->topologicalMesh().delaunayConditionForEdge( edge.first, edge.second ) )
1522 mMeshEditor->applyFlipEdge( edit, edge.first, edge.second );
1531 for ( QgsMeshEditor::Edit &edit :
mEdits )
1536QSet<int> QgsMeshLayerUndoCommandAddVertexInFaceWithDelaunayRefinement::secondNeighboringTriangularFaces()
1538 const int vIndex =
mMeshEditor->topologicalMesh().mesh()->vertexCount() - 1;
1539 const QList<int> firstNeighborFaces =
mMeshEditor->topologicalMesh().facesAroundVertex( vIndex );
1540 QSet<int> firstNeighborVertices;
1541 for (
int face : firstNeighborFaces )
1544 for (
int vertex : meshFace )
1546 firstNeighborVertices.insert( vertex );
1550 QSet<int> secondNeighboringFaces;
1551 for (
int vertex : firstNeighborVertices )
1553 const QList<int> faces =
mMeshEditor->topologicalMesh().facesAroundVertex( vertex );
1554 for (
int face : faces )
1556 if (
mMeshEditor->topologicalMesh().mesh()->face( face ).count() == 3 )
1557 secondNeighboringFaces.insert( face );
1560 return secondNeighboringFaces;
1563QList<std::pair<int, int>> QgsMeshLayerUndoCommandAddVertexInFaceWithDelaunayRefinement::innerEdges(
const QSet<int> &faces )
1566 QMap<std::pair<int, int>,
int> edges;
1568 for (
int faceIndex : faces )
1572 for (
int i = 0; i < face.size(); i++ )
1575 if ( next == face.size() )
1580 int minIndex = std::min( face.at( i ), face.at( next ) );
1581 int maxIndex = std::max( face.at( i ), face.at( next ) );
1582 std::pair<int, int> edge = std::pair<int, int>( minIndex, maxIndex );
1585 if ( edges.contains( edge ) )
1587 count = edges.take( edge );
1591 edges.insert( edge, count );
1595 QList<std::pair<int, int>> sharedEdges;
1597 for (
auto it = edges.begin(); it != edges.end(); it++ )
1599 if ( it.value() == 2 )
1601 sharedEdges.push_back( it.key() );
Provides global constants and enumerations 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.
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...
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.
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.
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.
void clear()
Clears all data contained in the instance.
QVector< QgsMeshFace > meshFaces() const
Returns faces.
Wraps a QgsMesh to ensure the consistency of the mesh during editing and helps to access elements fro...
static QgsMeshEditingError checkTopologyOfVerticesAsFace(const QVector< QgsMeshVertex > &vertices, bool &clockwise)
Checks the topology of the vertices as they are contained in a face and returns indication on directi...
Changes changeZValue(const QList< int > &verticesIndexes, const QList< double > &newValues)
Changes the Z values of the vertices with indexes in vertices indexes with the values in newValues.
static QgsTopologicalMesh createTopologicalMesh(QgsMesh *mesh, int maxVerticesPerFace, QgsMeshEditingError &error)
Creates a topologicaly consistent mesh with mesh, this static method modifies mesh to be topological ...
bool isVertexFree(int vertexIndex) const
Returns whether the vertex is a free vertex.
static QgsMeshEditingError counterClockwiseFaces(QgsMeshFace &face, QgsMesh *mesh)
Checks the topology of the face and sets it counter clockwise if necessary.
Changes removeVertexFillHole(int vertexIndex)
Removes the vertex with index vertexIndex.
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.
Makes changes to a triangular mesh and keeps track of these changes.
A triangular/derived 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.