Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

suggest to fix topological mesh error when starting mesh editing #50310

Merged
merged 9 commits into from
Oct 21, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion python/core/auto_generated/mesh/qgsmesheditor.sip.in
Original file line number Diff line number Diff line change
@@ -70,7 +70,22 @@ The caller takes ownership.

QgsMeshEditingError initialize();
%Docstring
Initialize the mesh editor and return errors if the internal native mesh have topologic errors
Initializes the mesh editor and returns first error if the internal native mesh has topological errors
%End

QgsMeshEditingError initializeWithErrorsFix();
%Docstring
Initializes the mesh editor. If topological errors occur,tries to fix these errors
and returns error if there is one that couldn't be fixed

.. versionadded:: 3.28
%End

bool fixError( const QgsMeshEditingError &error );
%Docstring
Tries to fix the topological ``error`` in the mesh. Returns ``False`` if the fix fails

.. versionadded:: 3.28
%End

bool faceCanBeAdded( const QgsMeshFace &face );
28 changes: 22 additions & 6 deletions python/core/auto_generated/mesh/qgsmeshlayer.sip.in
Original file line number Diff line number Diff line change
@@ -760,17 +760,33 @@ Returns the relative time (in milliseconds) of the dataset from the reference ti
.. versionadded:: 3.16
%End

bool startFrameEditing( const QgsCoordinateTransform &transform );
bool startFrameEditing( const QgsCoordinateTransform &transform );
%Docstring
Starts edition of the mesh frame. Coordinate ``transform`` used to initialize the triangular mesh if needed.
This operation will disconnect the mesh layer from the data provider anf removes all existing dataset group
Starts editing of the mesh frame. Coordinate ``transform`` used to initialize the triangular mesh if needed.
This operation will disconnect the mesh layer from the data provider and removes all existing dataset group

.. versionadded:: 3.22

.. deprecated:: QGIS 3.28
use the version with :py:class:`QgsMeshEditingError` instead
%End

bool startFrameEditing( const QgsCoordinateTransform &transform, QgsMeshEditingError &error /Out/, bool fixErrors );
%Docstring
Starts editing of the mesh frame. Coordinate ``transform`` used to initialize the triangular mesh if needed.
This operation will disconnect the mesh layer from the data provider and removes all existing dataset group.
Returns ``False`` if starting fails and the ``error`` that is the reason (No error, if the mesh is not editable or already in edit mode).

If ``fixErrors`` is set to ``True``, errors will be attempted to be fixed.
In that case returns ``False`` if there is an error that could not be fixed and the remaining ``error``.

.. versionadded:: 3.28
%End


bool commitFrameEditing( const QgsCoordinateTransform &transform, bool continueEditing = true );
%Docstring
Commits edition of the mesh frame,
Commits editing of the mesh frame,
Rebuilds the triangular mesh and its spatial index with ``transform``,
Continue editing with the same mesh editor if ``continueEditing`` is True

@@ -781,7 +797,7 @@ Continue editing with the same mesh editor if ``continueEditing`` is True

bool rollBackFrameEditing( const QgsCoordinateTransform &transform, bool continueEditing = true );
%Docstring
Rolls Back edition of the mesh frame.
Rolls Back editing of the mesh frame.
Reload mesh from file, rebuilds the triangular mesh and its spatial index with ``transform``,
Continue editing with the same mesh editor if ``continueEditing`` is ``True``

@@ -792,7 +808,7 @@ Continue editing with the same mesh editor if ``continueEditing`` is ``True``

void stopFrameEditing( const QgsCoordinateTransform &transform );
%Docstring
Stops edition of the mesh, re-indexes the faces and vertices,
Stops editing of the mesh, re-indexes the faces and vertices,
rebuilds the triangular mesh and its spatial index with ``transform``,
clean the undostack

32 changes: 27 additions & 5 deletions src/app/qgisapp.cpp
Original file line number Diff line number Diff line change
@@ -10750,15 +10750,37 @@ bool QgisApp::toggleEditingMeshLayer( QgsMeshLayer *mlayer, bool allowCancel )
}
}

res = mlayer->startFrameEditing( transform );
mActionToggleEditing->setChecked( res );
QgsMeshEditingError error;
{
QgsTemporaryCursorOverride waitCursor( Qt::WaitCursor );
res = mlayer->startFrameEditing( transform, error, false );
}

if ( !res )
{
visibleMessageBar()->pushWarning(
tr( "Mesh editing" ),
tr( "Unable to start mesh editing for layer \"%1\"" ).arg( mlayer->name() ) );
if ( error.errorType != Qgis::MeshEditingErrorType::NoError )
{
if ( QMessageBox::question( this, tr( "Mesh Editing" ),
tr( "At least one topological error in the mesh prevents starting editing.\n"
"Some errors can be fixed by removing invalid elements.\n\n"
"Do you want to try to fix errors before starting editing?" ),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes ) == QMessageBox::Yes )
{
QgsTemporaryCursorOverride waitCursor( Qt::WaitCursor );
res = mlayer->startFrameEditing( transform, error, true );
}
}

if ( !res )
{
visibleMessageBar()->pushWarning(
tr( "Mesh editing" ),
tr( "Unable to start mesh editing for layer \"%1\"" ).arg( mlayer->name() ) );
}

}

mActionToggleEditing->setChecked( res );
}
else if ( mlayer->isModified() )
{
86 changes: 82 additions & 4 deletions src/core/mesh/qgsmesheditor.cpp
Original file line number Diff line number Diff line change
@@ -19,8 +19,6 @@
#include "qgsmeshdataprovider.h"
#include "qgstriangularmesh.h"
#include "qgsmeshlayer.h"
#include "qgsmeshlayerutils.h"
#include "qgslogger.h"
#include "qgsgeometryengine.h"
#include "qgsmeshadvancedediting.h"
#include "qgsgeometryutils.h"
@@ -65,11 +63,89 @@ QgsMeshEditingError QgsMeshEditor::initialize()
{
QgsMeshEditingError error;
mTopologicalMesh = QgsTopologicalMesh::createTopologicalMesh( mMesh, mMaximumVerticesPerFace, error );

if ( error.errorType == Qgis::MeshEditingErrorType::NoError )
{
// we check for free vertices that could be included in face here
// because we need the spatial index of the triangular mesh
const QList<int> freeVertices = mTopologicalMesh.freeVerticesIndexes();
for ( int vi : freeVertices )
{
if ( mTriangularMesh->faceIndexForPoint_v2( mTriangularMesh->vertices().at( vi ) ) != -1 )
{
error = QgsMeshEditingError( Qgis::MeshEditingErrorType::InvalidVertex, vi );
break;
}
}
}

mValidFacesCount = mMesh->faceCount();
mValidVerticesCount = mMesh->vertexCount();
return error;
}

QgsMeshEditingError QgsMeshEditor::initializeWithErrorsFix()
{
QgsMeshEditingError lastError;

while ( true )
{
lastError = initialize();
if ( lastError.errorType == Qgis::MeshEditingErrorType::NoError )
break;

if ( !fixError( lastError ) )
break;

mTriangularMesh->update( mMesh );
};

return lastError;
}

bool QgsMeshEditor::fixError( const QgsMeshEditingError &error )
{
switch ( error.errorType )
{
case Qgis::MeshEditingErrorType::NoError:
return true;
break;
case Qgis::MeshEditingErrorType::InvalidFace:
case Qgis::MeshEditingErrorType::TooManyVerticesInFace:
case Qgis::MeshEditingErrorType::FlatFace:
case Qgis::MeshEditingErrorType::ManifoldFace:
if ( error.elementIndex != -1 && error.elementIndex < mMesh->faceCount() )
{
mMesh->faces.removeAt( error.elementIndex );
return true;
}
return false;
break;
case Qgis::MeshEditingErrorType::InvalidVertex:
case Qgis::MeshEditingErrorType::UniqueSharedVertex:
{
auto faceIt = mMesh->faces.begin();
while ( faceIt != mMesh->faces.end() )
{
if ( faceIt->contains( error.elementIndex ) )
faceIt = mMesh->faces.erase( faceIt );
else
++faceIt;
}

if ( error.elementIndex >= 0 && error.elementIndex < mMesh->vertexCount() )
{
mMesh->vertices[error.elementIndex] = QgsMeshVertex();
reindex( false );
}
return true;
}
break;
}

return false;
}

void QgsMeshEditor::resetTriangularMesh( QgsTriangularMesh *triangularMesh )
{
mTriangularMesh = triangularMesh;
@@ -986,10 +1062,12 @@ bool QgsMeshEditor::reindex( bool renumbering )
if ( !mTopologicalMesh.renumber() )
return false;

QgsMeshEditingError error = initialize();
QgsMeshEditingError error;
mTopologicalMesh = QgsTopologicalMesh::createTopologicalMesh( mMesh, mMaximumVerticesPerFace, error );
mValidFacesCount = mMesh->faceCount();
mValidVerticesCount = mMesh->vertexCount();
return error.errorType == Qgis::MeshEditingErrorType::NoError;
}

else
return true;
}
17 changes: 16 additions & 1 deletion src/core/mesh/qgsmesheditor.h
Original file line number Diff line number Diff line change
@@ -82,9 +82,24 @@ class CORE_EXPORT QgsMeshEditor : public QObject
*/
QgsMeshDatasetGroup *createZValueDatasetGroup() SIP_TRANSFERBACK;

//! Initialize the mesh editor and return errors if the internal native mesh have topologic errors
//! Initializes the mesh editor and returns first error if the internal native mesh has topological errors
QgsMeshEditingError initialize();

/**
* Initializes the mesh editor. If topological errors occur,tries to fix these errors
* and returns error if there is one that couldn't be fixed
*
* \since QGIS 3.28
*/
QgsMeshEditingError initializeWithErrorsFix();

/**
* Tries to fix the topological \a error in the mesh. Returns FALSE if the fix fails
*
* \since QGIS 3.28
*/
bool fixError( const QgsMeshEditingError &error );

//! Resets the triangular mesh
void resetTriangularMesh( QgsTriangularMesh *triangularMesh ); SIP_SKIP

14 changes: 13 additions & 1 deletion src/core/mesh/qgsmeshlayer.cpp
Original file line number Diff line number Diff line change
@@ -966,6 +966,12 @@ static QString detailsErrorMessage( const QgsMeshEditingError &error )
}

bool QgsMeshLayer::startFrameEditing( const QgsCoordinateTransform &transform )
{
QgsMeshEditingError error;
return startFrameEditing( transform, error, false );
}

bool QgsMeshLayer::startFrameEditing( const QgsCoordinateTransform &transform, QgsMeshEditingError &error, bool fixErrors )
{
if ( !supportsEditing() )
{
@@ -985,7 +991,13 @@ bool QgsMeshLayer::startFrameEditing( const QgsCoordinateTransform &transform )

mMeshEditor = new QgsMeshEditor( this );

const QgsMeshEditingError error = mMeshEditor->initialize();
if ( fixErrors )
{
mRendererCache.reset(); // fixing errors could lead to remove faces/vertices
error = mMeshEditor->initializeWithErrorsFix();
}
else
error = mMeshEditor->initialize();

if ( error.errorType != Qgis::MeshEditingErrorType::NoError )
{
27 changes: 21 additions & 6 deletions src/core/mesh/qgsmeshlayer.h
Original file line number Diff line number Diff line change
@@ -40,6 +40,7 @@ class QgsMesh3dAveragingMethod;
class QgsMeshLayerTemporalProperties;
class QgsMeshDatasetGroupStore;
class QgsMeshEditor;
class QgsMeshEditingError;
class QgsMeshLayerElevationProperties;

/**
@@ -774,15 +775,29 @@ class CORE_EXPORT QgsMeshLayer : public QgsMapLayer, public QgsAbstractProfileSo
qint64 datasetRelativeTimeInMilliseconds( const QgsMeshDatasetIndex &index );

/**
* Starts edition of the mesh frame. Coordinate \a transform used to initialize the triangular mesh if needed.
* This operation will disconnect the mesh layer from the data provider anf removes all existing dataset group
* Starts editing of the mesh frame. Coordinate \a transform used to initialize the triangular mesh if needed.
* This operation will disconnect the mesh layer from the data provider and removes all existing dataset group
*
* \since QGIS 3.22
* \deprecated since QGIS 3.28, use the version with QgsMeshEditingError instead
*/
bool startFrameEditing( const QgsCoordinateTransform &transform );
Q_DECL_DEPRECATED bool startFrameEditing( const QgsCoordinateTransform &transform );

/**
* Commits edition of the mesh frame,
* Starts editing of the mesh frame. Coordinate \a transform used to initialize the triangular mesh if needed.
* This operation will disconnect the mesh layer from the data provider and removes all existing dataset group.
* Returns FALSE if starting fails and the \a error that is the reason (No error, if the mesh is not editable or already in edit mode).
*
* If \a fixErrors is set to TRUE, errors will be attempted to be fixed.
* In that case returns FALSE if there is an error that could not be fixed and the remaining \a error.
*
* \since QGIS 3.28
*/
bool startFrameEditing( const QgsCoordinateTransform &transform, QgsMeshEditingError &error SIP_OUT, bool fixErrors );


/**
* Commits editing of the mesh frame,
* Rebuilds the triangular mesh and its spatial index with \a transform,
* Continue editing with the same mesh editor if \a continueEditing is True
*
@@ -792,7 +807,7 @@ class CORE_EXPORT QgsMeshLayer : public QgsMapLayer, public QgsAbstractProfileSo
bool commitFrameEditing( const QgsCoordinateTransform &transform, bool continueEditing = true );

/**
* Rolls Back edition of the mesh frame.
* Rolls Back editing of the mesh frame.
* Reload mesh from file, rebuilds the triangular mesh and its spatial index with \a transform,
* Continue editing with the same mesh editor if \a continueEditing is TRUE
*
@@ -802,7 +817,7 @@ class CORE_EXPORT QgsMeshLayer : public QgsMapLayer, public QgsAbstractProfileSo
bool rollBackFrameEditing( const QgsCoordinateTransform &transform, bool continueEditing = true );

/**
* Stops edition of the mesh, re-indexes the faces and vertices,
* Stops editing of the mesh, re-indexes the faces and vertices,
* rebuilds the triangular mesh and its spatial index with \a transform,
* clean the undostack
*
5 changes: 5 additions & 0 deletions src/core/mesh/qgstriangularmesh.cpp
Original file line number Diff line number Diff line change
@@ -254,6 +254,11 @@ bool QgsTriangularMesh::update( QgsMesh *nativeMesh, const QgsCoordinateTransfor
return true;
}

bool QgsTriangularMesh::update( QgsMesh *nativeMesh )
{
return update( nativeMesh, mCoordinateTransform );
}

void QgsTriangularMesh::finalizeTriangles()
{
mAverageTriangleSize = 0;
14 changes: 13 additions & 1 deletion src/core/mesh/qgstriangularmesh.h
Original file line number Diff line number Diff line change
@@ -62,7 +62,19 @@ class CORE_EXPORT QgsTriangularMesh // TODO rename to QgsRendererMesh in QGIS 4
* \param transform Transformation from layer CRS to destination (e.g. map) CRS. With invalid transform, it keeps the native mesh CRS
* \returns TRUE if the mesh is effectivly updated, and FALSE if not
*/
bool update( QgsMesh *nativeMesh, const QgsCoordinateTransform &transform = QgsCoordinateTransform() );
bool update( QgsMesh *nativeMesh, const QgsCoordinateTransform &transform );

/**
* Constructs triangular mesh from layer's native mesh using the coordinate transform already set. Populates spatial index.
* \param nativeMesh QgsMesh to access native vertices and faces
*
* \returns TRUE if the mesh is effectivly updated, and FALSE if not
*
* \note if the coordinate transform is not already set, it uses the native mesh CRS
*
* \since QGIS 3.28
*/
bool update( QgsMesh *nativeMesh );

/**
* Returns vertices in map coordinate system
11 changes: 8 additions & 3 deletions tests/src/app/testqgsmaptooleditmesh.cpp
Original file line number Diff line number Diff line change
@@ -79,7 +79,9 @@ void TestQgsMapToolEditMesh::hoverElements()
mCanvas->setDestinationCrs( wgs84Crs );

QgsCoordinateTransform transform( meshLayerSimpleBox->crs(), mCanvas->mapSettings().destinationCrs(), QgsProject::instance() );
QVERIFY( meshLayerSimpleBox->startFrameEditing( transform ) );
QgsMeshEditingError error;
QVERIFY( meshLayerSimpleBox->startFrameEditing( transform, error, false ) );
QVERIFY( error.errorType == Qgis::MeshEditingErrorType::NoError );

QgsRectangle extent( 3.31351393, 47.97489613, 3.31351792, 47.97508220 );
mCanvas->setExtent( extent );
@@ -110,7 +112,9 @@ void TestQgsMapToolEditMesh::editMesh()
QCOMPARE( meshLayerQuadFlower->datasetGroupCount(), 1 );

const QgsCoordinateTransform transform;
meshLayerQuadFlower->startFrameEditing( transform );
QgsMeshEditingError error;
meshLayerQuadFlower->startFrameEditing( transform, error, false );
QVERIFY( error == QgsMeshEditingError() );

mCanvas->setLayers( QList<QgsMapLayer *>() << meshLayerQuadFlower.get() );
const double offsetInMapUnits = 15 * mCanvas->mapSettings().mapUnitsPerPixel();
@@ -138,7 +142,8 @@ void TestQgsMapToolEditMesh::editMesh()

QVERIFY( !meshLayerQuadFlower->meshEditor() );

meshLayerQuadFlower->startFrameEditing( transform );
meshLayerQuadFlower->startFrameEditing( transform, error, false );
QVERIFY( error == QgsMeshEditingError() );

QVERIFY( meshLayerQuadFlower->meshEditor() );

97 changes: 83 additions & 14 deletions tests/src/core/testqgsmesheditor.cpp
Original file line number Diff line number Diff line change
@@ -48,6 +48,7 @@ class TestQgsMeshEditor : public QObject
void cleanup() {} // will be called after every testfunction.

void startStopEditing();
void startEditingWithErrors();
void createTopologicMesh();
void editTopologicMesh();
void badTopologicMesh();
@@ -114,9 +115,10 @@ void TestQgsMeshEditor::startStopEditing()
QCOMPARE( meta.name(), QStringLiteral( "Bed Elevation" ) );

const QgsCoordinateTransform transform;
QgsMeshEditingError error;

QVERIFY( meshLayerQuadTriangle->startFrameEditing( transform ) );
QVERIFY( !meshLayerQuadTriangle->startFrameEditing( transform ) ); //mesh editing is already started
QVERIFY( meshLayerQuadTriangle->startFrameEditing( transform, error, false ) );
QVERIFY( !meshLayerQuadTriangle->startFrameEditing( transform, error, false ) ); //mesh editing is already started

// Edition has started, dataset groups are removed ans replace by a virtual one that represent the Z value of vertices
QCOMPARE( meshLayerQuadTriangle->datasetGroupCount(), 1 );
@@ -185,6 +187,72 @@ void TestQgsMeshEditor::startStopEditing()
QCOMPARE( meshLayerQuadTriangle->meshFaceCount(), 2 );
}

void TestQgsMeshEditor::startEditingWithErrors()
{
const QgsCoordinateTransform transform;

QString uri( mDataDir + QStringLiteral( "/with_flat_face.2dm" ) );
std::unique_ptr<QgsMeshLayer> mesh = std::make_unique<QgsMeshLayer>( uri, QStringLiteral( "With flat face" ), QStringLiteral( "mdal" ) );
QVERIFY( mesh->isValid() );
QCOMPARE( mesh->meshFaceCount(), 3 );
QCOMPARE( mesh->meshVertexCount(), 5 );

QgsMeshEditingError error;
QVERIFY( !mesh->startFrameEditing( transform, error, false ) );
QCOMPARE( error, QgsMeshEditingError( Qgis::MeshEditingErrorType::FlatFace, 2 ) );

QVERIFY( mesh->startFrameEditing( transform, error, true ) );
QCOMPARE( error, QgsMeshEditingError() );

QCOMPARE( mesh->meshFaceCount(), 2 );
QCOMPARE( mesh->meshVertexCount(), 5 );

uri = mDataDir + QStringLiteral( "/with_manifold_face.2dm" );
mesh = std::make_unique<QgsMeshLayer>( uri, QStringLiteral( "With manifold face" ), QStringLiteral( "mdal" ) );
QVERIFY( mesh->isValid() );
QCOMPARE( mesh->meshFaceCount(), 3 );
QCOMPARE( mesh->meshVertexCount(), 5 );

QVERIFY( !mesh->startFrameEditing( transform, error, false ) );
QCOMPARE( error, QgsMeshEditingError( Qgis::MeshEditingErrorType::ManifoldFace, 1 ) );

QVERIFY( mesh->startFrameEditing( transform, error, true ) );
QCOMPARE( error, QgsMeshEditingError() );

QCOMPARE( mesh->meshFaceCount(), 2 );
QCOMPARE( mesh->meshVertexCount(), 5 );

uri = mDataDir + QStringLiteral( "/with_free_vertex_in_mesh.2dm" );
mesh = std::make_unique<QgsMeshLayer>( uri, QStringLiteral( "With free vertex in mesh" ), QStringLiteral( "mdal" ) );
QVERIFY( mesh->isValid() );
QCOMPARE( mesh->meshFaceCount(), 2 );
QCOMPARE( mesh->meshVertexCount(), 6 );

QVERIFY( !mesh->startFrameEditing( transform, error, false ) );
QCOMPARE( error, QgsMeshEditingError( Qgis::MeshEditingErrorType::InvalidVertex, 5 ) );

QVERIFY( mesh->startFrameEditing( transform, error, true ) );
QCOMPARE( error, QgsMeshEditingError() );

QCOMPARE( mesh->meshFaceCount(), 2 );
QCOMPARE( mesh->meshVertexCount(), 5 );

uri = mDataDir + QStringLiteral( "/with_unique_shared_vertex.2dm" );
mesh = std::make_unique<QgsMeshLayer>( uri, QStringLiteral( "With unique shared vertex" ), QStringLiteral( "mdal" ) );
QVERIFY( mesh->isValid() );
QCOMPARE( mesh->meshFaceCount(), 3 );
QCOMPARE( mesh->meshVertexCount(), 7 );

QVERIFY( !mesh->startFrameEditing( transform, error, false ) );
QCOMPARE( error, QgsMeshEditingError( Qgis::MeshEditingErrorType::UniqueSharedVertex, 2 ) );

QVERIFY( mesh->startFrameEditing( transform, error, true ) );
QCOMPARE( error, QgsMeshEditingError() );

QCOMPARE( mesh->meshFaceCount(), 1 );
QCOMPARE( mesh->meshVertexCount(), 6 );
}

static bool checkNeighbors( const QgsTopologicalMesh &mesh, int faceIndex, const QList<int> &expectedNeighbors )
{
const QVector<int> neighbors = mesh.neighborsOfFace( faceIndex );
@@ -736,7 +804,8 @@ void TestQgsMeshEditor::meshEditorSimpleEdition()
const QUndoStack undoStack;

const QgsCoordinateTransform transform;
QVERIFY( meshLayerQuadTriangle->startFrameEditing( transform ) );
QgsMeshEditingError error;
QVERIFY( meshLayerQuadTriangle->startFrameEditing( transform, error, false ) );

QgsMesh mesh;
QgsTriangularMesh triangularMesh;
@@ -751,8 +820,6 @@ void TestQgsMeshEditor::meshEditorSimpleEdition()
QgsPoint( 1.0, 0.0, 0.0 ), // 3
QgsPoint( )} ); // 4

QgsMeshEditingError error;

meshEditor.addVertices( vertices, 0.01 );

for ( int i = 0; i < 5; ++i )
@@ -809,7 +876,8 @@ void TestQgsMeshEditor::meshEditorSimpleEdition()
void TestQgsMeshEditor::faceIntersection()
{
const QgsCoordinateTransform transform;
QVERIFY( meshLayerQuadFlower->startFrameEditing( transform ) );
QgsMeshEditingError error;
QVERIFY( meshLayerQuadFlower->startFrameEditing( transform, error, false ) );

QgsMeshEditor *editor = meshLayerQuadFlower->meshEditor();
QVERIFY( editor );
@@ -1352,7 +1420,8 @@ void TestQgsMeshEditor::meshEditorFromMeshLayer_quadTriangle()
QCOMPARE( meshLayerQuadTriangle->meshFaceCount(), 2 );

const QgsCoordinateTransform transform;
QVERIFY( meshLayerQuadTriangle->startFrameEditing( transform ) );
QgsMeshEditingError error;
QVERIFY( meshLayerQuadTriangle->startFrameEditing( transform, error, false ) );

QgsMeshEditor *editor = meshLayerQuadTriangle->meshEditor();

@@ -1402,7 +1471,7 @@ void TestQgsMeshEditor::meshEditorFromMeshLayer_quadTriangle()
meshLayerQuadTriangle->undoStack()->undo();

// try to add a face that shares only one vertex
QgsMeshEditingError error = editor->addFaces( {{2, 5, 6}} );
error = editor->addFaces( {{2, 5, 6}} );
QVERIFY( error == QgsMeshEditingError( Qgis::MeshEditingErrorType::UniqueSharedVertex, 2 ) );
QCOMPARE( meshLayerQuadTriangle->meshVertexCount(), 7 );
QCOMPARE( meshLayerQuadTriangle->meshFaceCount(), 2 );
@@ -1685,15 +1754,14 @@ void TestQgsMeshEditor::meshEditorFromMeshLayer_quadFlower()
QCOMPARE( meshLayerQuadFlower->meshFaceCount(), 5 );

const QgsCoordinateTransform transform;
QVERIFY( meshLayerQuadFlower->startFrameEditing( transform ) );
QgsMeshEditingError error;
QVERIFY( meshLayerQuadFlower->startFrameEditing( transform, error, false ) );

QgsMeshEditor *editor = meshLayerQuadFlower->meshEditor();
QVERIFY( editor );
editor->mMaximumVerticesPerFace = 5; //for testing

QgsMeshEditingError error;

meshLayerQuadFlower->startFrameEditing( transform );
meshLayerQuadFlower->startFrameEditing( transform, error, false );
editor = meshLayerQuadFlower->meshEditor();
QCOMPARE( editor->addPointsAsVertices( {QgsPoint( 1500, 2800, -10 )}, 10 ), 1 ); // 8
QCOMPARE( meshLayerQuadFlower->meshFaceCount(), 8 );
@@ -2095,7 +2163,8 @@ void TestQgsMeshEditor::transformByExpression()
std::unique_ptr<QgsMeshLayer> layer = std::make_unique<QgsMeshLayer>( mDataDir + "/quad_flower_to_edit.2dm", "mesh", "mdal" );

const QgsCoordinateTransform transform;
layer->startFrameEditing( transform );
QgsMeshEditingError error;
layer->startFrameEditing( transform, error, false );

QgsMeshTransformVerticesByExpression transformVertex;

@@ -2249,7 +2318,7 @@ void TestQgsMeshEditor::forceByLine()

QVERIFY( meshLayer->isValid() );
QgsCoordinateTransform transform;
QVERIFY( meshLayer->startFrameEditing( transform ) );
QVERIFY( meshLayer->startFrameEditing( transform, error, false ) );

QVERIFY( meshLayer->meshEditor() );

9 changes: 9 additions & 0 deletions tests/testdata/mesh/with_flat_face.2dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
MESH2D 1000.000 2000.000 0.000000 200 300 1.000 1.000
ND 1 1000.000 2000.000 20.000
ND 2 2000.000 2000.000 30.000
ND 3 3000.000 2000.000 40.000
ND 4 2000.000 3000.000 50.000
ND 5 1000.000 3000.000 10.000
E4Q 1 1 2 4 5 1
E3T 2 2 3 4 1
E3T 3 1 2 3 1
10 changes: 10 additions & 0 deletions tests/testdata/mesh/with_free_vertex_in_mesh.2dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
MESH2D 1000.000 2000.000 0.000000 200 300 1.000 1.000
ND 1 1000.000 2000.000 20.000
ND 2 2000.000 2000.000 30.000
ND 3 3000.000 2000.000 40.000
ND 4 2000.000 3000.000 50.000
ND 5 1000.000 3000.000 10.000
ND 6 1500.000 2500.000 10.000
E4Q 1 1 2 4 5 1
E3T 3 2 3 4 1

10 changes: 10 additions & 0 deletions tests/testdata/mesh/with_manifold_face.2dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
MESH2D 1000.000 2000.000 0.000000 200 300 1.000 1.000
ND 1 1000.000 2000.000 20.000
ND 2 2000.000 2000.000 30.000
ND 3 3000.000 2000.000 40.000
ND 4 2000.000 3000.000 50.000
ND 5 1000.000 3000.000 10.000
E4Q 1 1 2 4 5 1
E3T 2 2 4 5 1
E3T 3 2 3 4 1

12 changes: 12 additions & 0 deletions tests/testdata/mesh/with_unique_shared_vertex.2dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
MESH2D 1000.000 2000.000 0.000000 200 300 1.000 1.000
ND 1 1000.000 2000.000 20.000
ND 2 2000.000 2000.000 30.000
ND 3 3000.000 2000.000 40.000
ND 4 2000.000 3000.000 50.000
ND 5 1000.000 3000.000 10.000
ND 6 4000.000 3000.000 10.000
ND 7 4000.000 2000.000 10.000
E4Q 1 1 2 4 5 1
E3T 2 2 3 4 1
E3T 3 3 6 7 1