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