From 67bc6af0d5ebc6db825ab8b6ae22e71073949022 Mon Sep 17 00:00:00 2001 From: Jan Caha Date: Wed, 8 Jan 2025 14:08:11 +0100 Subject: [PATCH] Mesh editing delaunay refinement (#59560) * add function delaunayConditionForEdge() * undo command with delaunay refinement * add delaunay refinement action * add menu to button * sipify * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix merge issues * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * access widget in test to directly change selected value * add tests * fix docstring Co-authored-by: Stefanos Natsis * remove pass by reference Co-authored-by: Stefanos Natsis * pass by reference Co-authored-by: Stefanos Natsis * fix label, add tooltip * avoid brute forcing through faces, use review suggestion instead * add pass by reference * sipify * remove leftover --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Stefanos Natsis --- .../auto_generated/mesh/qgsmesheditor.sip.in | 8 ++ .../auto_generated/mesh/qgsmesheditor.sip.in | 8 ++ src/app/mesh/qgsmaptooleditmeshframe.cpp | 28 +++- src/app/mesh/qgsmaptooleditmeshframe.h | 6 + src/core/mesh/qgsmesheditor.cpp | 121 ++++++++++++++++++ src/core/mesh/qgsmesheditor.h | 30 +++++ src/core/mesh/qgstopologicalmesh.cpp | 39 ++++++ src/core/mesh/qgstopologicalmesh.h | 8 ++ tests/src/app/testqgsmaptooleditmesh.cpp | 103 ++++++++++++++- .../expected_delaunay.2dm | 21 +++ .../expected_not_delaunay.2dm | 21 +++ tests/testdata/mesh/not_delaunay.2dm | 18 +++ 12 files changed, 406 insertions(+), 5 deletions(-) create mode 100644 tests/testdata/control_files/app/expected_edit_delaunay_refinement/expected_delaunay.2dm create mode 100644 tests/testdata/control_files/app/expected_edit_no_delaunay_refinement/expected_not_delaunay.2dm create mode 100644 tests/testdata/mesh/not_delaunay.2dm diff --git a/python/PyQt6/core/auto_generated/mesh/qgsmesheditor.sip.in b/python/PyQt6/core/auto_generated/mesh/qgsmesheditor.sip.in index 6482b89e7de9..c01dbfcb8acc 100644 --- a/python/PyQt6/core/auto_generated/mesh/qgsmesheditor.sip.in +++ b/python/PyQt6/core/auto_generated/mesh/qgsmesheditor.sip.in @@ -260,6 +260,14 @@ Returns the count of valid vertices, that is non void vertices in the mesh int maximumVerticesPerFace() const; %Docstring Returns the maximum count of vertices per face that the mesh can support +%End + + void addVertexWithDelaunayRefinement( const QgsMeshVertex &vertex, const double tolerance ); +%Docstring +Add a vertex in a face with Delaunay refinement of neighboring faces +All neighboring faces sharing a vertex will be refined to satisfy the Delaunay condition + +.. versionadded:: 3.42 %End signals: diff --git a/python/core/auto_generated/mesh/qgsmesheditor.sip.in b/python/core/auto_generated/mesh/qgsmesheditor.sip.in index 6482b89e7de9..c01dbfcb8acc 100644 --- a/python/core/auto_generated/mesh/qgsmesheditor.sip.in +++ b/python/core/auto_generated/mesh/qgsmesheditor.sip.in @@ -260,6 +260,14 @@ Returns the count of valid vertices, that is non void vertices in the mesh int maximumVerticesPerFace() const; %Docstring Returns the maximum count of vertices per face that the mesh can support +%End + + void addVertexWithDelaunayRefinement( const QgsMeshVertex &vertex, const double tolerance ); +%Docstring +Add a vertex in a face with Delaunay refinement of neighboring faces +All neighboring faces sharing a vertex will be refined to satisfy the Delaunay condition + +.. versionadded:: 3.42 %End signals: diff --git a/src/app/mesh/qgsmaptooleditmeshframe.cpp b/src/app/mesh/qgsmaptooleditmeshframe.cpp index 96b90b6bf558..b17180c90a3a 100644 --- a/src/app/mesh/qgsmaptooleditmeshframe.cpp +++ b/src/app/mesh/qgsmaptooleditmeshframe.cpp @@ -238,13 +238,21 @@ QgsMeshEditDigitizingAction::QgsMeshEditDigitizingAction( QObject *parent ) int interpolateFromValue = settings.enumValue( QStringLiteral( "UI/Mesh/zValueFrom" ), PreferMeshThenZWidget ); mComboZValueType->setCurrentIndex( interpolateFromValue ); - gLayout->addWidget( labelZValueType, 2, 0, 1, 3 ); - gLayout->addWidget( mComboZValueType, 2, 3, 1, 1 ); + mCheckBoxRefineNeighboringFaces = new QCheckBox( tr( "Refine neighboring faces when adding vertices" ) ); + mCheckBoxRefineNeighboringFaces->setToolTip( "Flip edges that do not fulfil delaunay rule on triangular faces that share at least one vertex with the face that new vertex was added to." ); + bool refineNeighboringFaces = settings.value( QStringLiteral( "UI/Mesh/refineNeighboringFaces" ) ).toBool(); + mCheckBoxRefineNeighboringFaces->setChecked( refineNeighboringFaces ); + + gLayout->addWidget( labelZValueType, 1, 0, 1, 1 ); + gLayout->addWidget( mComboZValueType, 1, 1, 1, 1 ); + gLayout->addWidget( mCheckBoxRefineNeighboringFaces, 2, 0, 1, 2 ); QWidget *w = new QWidget(); w->setLayout( gLayout ); setDefaultWidget( w ); + + connect( mCheckBoxRefineNeighboringFaces, &QCheckBox::toggled, this, &QgsMeshEditDigitizingAction::updateSettings ); } void QgsMeshEditDigitizingAction::updateSettings() @@ -252,6 +260,7 @@ void QgsMeshEditDigitizingAction::updateSettings() QgsSettings settings; settings.setEnumValue( QStringLiteral( "UI/Mesh/zValueFrom" ), static_cast( mComboZValueType->currentData().toInt() ) ); + settings.setValue( QStringLiteral( "UI/Mesh/refineNeighboringFaces" ), mCheckBoxRefineNeighboringFaces->isChecked() ); } QgsMeshEditDigitizingAction::ZValueSource QgsMeshEditDigitizingAction::zValueSourceType() const @@ -264,6 +273,11 @@ void QgsMeshEditDigitizingAction::setZValueType( QgsMeshEditDigitizingAction::ZV mComboZValueType->setCurrentIndex( mComboZValueType->findData( zValueSource ) ); } +bool QgsMeshEditDigitizingAction::refineNeighboringFaces() const +{ + return mCheckBoxRefineNeighboringFaces->isChecked(); +} + // // QgsMapToolEditMeshFrame // @@ -2798,10 +2812,18 @@ void QgsMapToolEditMeshFrame::addVertex( } const QVector points( 1, QgsMeshVertex( effectivePoint.x(), effectivePoint.y(), zValue ) ); + if ( mCurrentEditor ) { double tolerance = QgsTolerance::vertexSearchRadius( canvas()->mapSettings() ); - mCurrentEditor->addVertices( points, tolerance ); + if ( mWidgetActionDigitizing->refineNeighboringFaces() && mCurrentFaceIndex != -1 ) + { + mCurrentEditor->addVertexWithDelaunayRefinement( points.first(), tolerance ); + } + else + { + mCurrentEditor->addVertices( points, tolerance ); + } } } diff --git a/src/app/mesh/qgsmaptooleditmeshframe.h b/src/app/mesh/qgsmaptooleditmeshframe.h index e20e0c437a5b..792869bf848c 100644 --- a/src/app/mesh/qgsmaptooleditmeshframe.h +++ b/src/app/mesh/qgsmaptooleditmeshframe.h @@ -141,6 +141,9 @@ class QgsMeshEditDigitizingAction : public QWidgetAction //! Returns type of z value obtaining QgsMeshEditDigitizingAction::ZValueSource zValueSourceType() const; + //! Returns if neighboring faces should be refined when adding vertex inside mesh + bool refineNeighboringFaces() const; + void setZValueType( QgsMeshEditDigitizingAction::ZValueSource zValueSource ); private slots: @@ -148,6 +151,9 @@ class QgsMeshEditDigitizingAction : public QWidgetAction private: QComboBox *mComboZValueType = nullptr; + QCheckBox *mCheckBoxRefineNeighboringFaces = nullptr; + + friend class TestQgsMapToolEditMesh; }; class APP_EXPORT QgsMapToolEditMeshFrame : public QgsMapToolAdvancedDigitizing diff --git a/src/core/mesh/qgsmesheditor.cpp b/src/core/mesh/qgsmesheditor.cpp index 770242fa7062..79d520d27519 100644 --- a/src/core/mesh/qgsmesheditor.cpp +++ b/src/core/mesh/qgsmesheditor.cpp @@ -640,6 +640,15 @@ QgsMeshEditingError QgsMeshEditor::removeFaces( const QList &facesToRemove return error; } +void QgsMeshEditor::addVertexWithDelaunayRefinement( const QgsMeshVertex &vertex, const double tolerance ) +{ + int triangleIndex = mTriangularMesh->faceIndexForPoint_v2( vertex ); + if ( triangleIndex == -1 ) + return; + + mUndoStack->push( new QgsMeshLayerUndoCommandAddVertexInFaceWithDelaunayRefinement( this, vertex, tolerance ) ); +} + bool QgsMeshEditor::edgeCanBeFlipped( int vertexIndex1, int vertexIndex2 ) const { return mTopologicalMesh.edgeCanBeFlipped( vertexIndex1, vertexIndex2 ); @@ -1482,3 +1491,115 @@ void QgsMeshLayerUndoCommandAdvancedEditing::redo() mMeshEditor->applyEdit( edit ); } } + +QgsMeshLayerUndoCommandAddVertexInFaceWithDelaunayRefinement::QgsMeshLayerUndoCommandAddVertexInFaceWithDelaunayRefinement( + QgsMeshEditor *meshEditor, + const QgsMeshVertex &vertex, + double tolerance ) + : QgsMeshLayerUndoCommandMeshEdit( meshEditor ) + , mVertex( vertex ) + , mTolerance( tolerance ) +{ + setText( QObject::tr( "Add vertex inside face with Delaunay refinement" ) ); +} + +void QgsMeshLayerUndoCommandAddVertexInFaceWithDelaunayRefinement::redo() +{ + if ( !mVertex.isEmpty() ) + { + QgsMeshEditor::Edit edit; + + mMeshEditor->applyAddVertex( edit, mVertex, mTolerance ); + mEdits.append( edit ); + + QList> sharedEdges = innerEdges( secondNeighboringTriangularFaces() ); + + for ( std::pair edge : sharedEdges ) + { + if ( mMeshEditor->edgeCanBeFlipped( edge.first, edge.second ) && !mMeshEditor->topologicalMesh().delaunayConditionForEdge( edge.first, edge.second ) ) + { + mMeshEditor->applyFlipEdge( edit, edge.first, edge.second ); + mEdits.append( edit ); + } + } + + mVertex = QgsMeshVertex(); + } + else + { + for ( QgsMeshEditor::Edit &edit : mEdits ) + mMeshEditor->applyEdit( edit ); + } +} + +QSet QgsMeshLayerUndoCommandAddVertexInFaceWithDelaunayRefinement::secondNeighboringTriangularFaces() +{ + const int vIndex = mMeshEditor->topologicalMesh().mesh()->vertexCount() - 1; + const QList firstNeighborFaces = mMeshEditor->topologicalMesh().facesAroundVertex( vIndex ); + QSet firstNeighborVertices; + for ( int face : firstNeighborFaces ) + { + const QgsMeshFace meshFace = mMeshEditor->topologicalMesh().mesh()->face( face ); + for ( int vertex : meshFace ) + { + firstNeighborVertices.insert( vertex ); + } + } + + QSet secondNeighboringFaces; + for ( int vertex : firstNeighborVertices ) + { + const QList faces = mMeshEditor->topologicalMesh().facesAroundVertex( vertex ); + for ( int face : faces ) + { + if ( mMeshEditor->topologicalMesh().mesh()->face( face ).count() == 3 ) + secondNeighboringFaces.insert( face ); + } + } + return secondNeighboringFaces; +} + +QList> QgsMeshLayerUndoCommandAddVertexInFaceWithDelaunayRefinement::innerEdges( const QSet &faces ) +{ + // edges and number of their occurrence in triangular faces + QMap, int> edges; + + for ( int faceIndex : faces ) + { + const QgsMeshFace face = mMeshEditor->topologicalMesh().mesh()->face( faceIndex ); + + for ( int i = 0; i < face.size(); i++ ) + { + int next = i + 1; + if ( next == face.size() ) + { + next = 0; + } + + int minIndex = std::min( face.at( i ), face.at( next ) ); + int maxIndex = std::max( face.at( i ), face.at( next ) ); + std::pair edge = std::pair( minIndex, maxIndex ); + + int count = 1; + if ( edges.contains( edge ) ) + { + count = edges.take( edge ); + count++; + } + + edges.insert( edge, count ); + } + } + + QList> sharedEdges; + + for ( auto it = edges.begin(); it != edges.end(); it++ ) + { + if ( it.value() == 2 ) + { + sharedEdges.push_back( it.key() ); + } + } + + return sharedEdges; +} diff --git a/src/core/mesh/qgsmesheditor.h b/src/core/mesh/qgsmesheditor.h index ea0f127101db..72cfd9edd12d 100644 --- a/src/core/mesh/qgsmesheditor.h +++ b/src/core/mesh/qgsmesheditor.h @@ -305,6 +305,14 @@ class CORE_EXPORT QgsMeshEditor : public QObject //! Returns the maximum count of vertices per face that the mesh can support int maximumVerticesPerFace() const; + /** + * Add a vertex in a face with Delaunay refinement of neighboring faces + * All neighboring faces sharing a vertex will be refined to satisfy the Delaunay condition + * + * \since QGIS 3.42 + */ + void addVertexWithDelaunayRefinement( const QgsMeshVertex &vertex, const double tolerance ); + signals: //! Emitted when the mesh is edited void meshEdited(); @@ -363,6 +371,7 @@ class CORE_EXPORT QgsMeshEditor : public QObject friend class QgsMeshLayerUndoCommandFlipEdge; friend class QgsMeshLayerUndoCommandMerge; friend class QgsMeshLayerUndoCommandSplitFaces; + friend class QgsMeshLayerUndoCommandAddVertexInFaceWithDelaunayRefinement; friend class QgsMeshLayerUndoCommandAdvancedEditing; }; @@ -652,7 +661,28 @@ class QgsMeshLayerUndoCommandAdvancedEditing : public QgsMeshLayerUndoCommandMes }; +/** + * \ingroup core + * + * \brief Class for undo/redo command for adding vertex to face with Delaunay Refiment of faces surrounding + * + * \since QGIS 3.42 + */ +class QgsMeshLayerUndoCommandAddVertexInFaceWithDelaunayRefinement: public QgsMeshLayerUndoCommandMeshEdit +{ + public: + + //! Constructor with the associated \a meshEditor and indexes \a vertex and \a tolerance + QgsMeshLayerUndoCommandAddVertexInFaceWithDelaunayRefinement( QgsMeshEditor *meshEditor, const QgsMeshVertex &vertex, double tolerance ); + + void redo() override; + private: + QList> innerEdges( const QSet &faces ); + QSet secondNeighboringTriangularFaces(); + QgsMeshVertex mVertex; + double mTolerance; +}; #endif //SIP_RUN diff --git a/src/core/mesh/qgstopologicalmesh.cpp b/src/core/mesh/qgstopologicalmesh.cpp index a979f54aa0f1..c5fa7f5ff3c5 100644 --- a/src/core/mesh/qgstopologicalmesh.cpp +++ b/src/core/mesh/qgstopologicalmesh.cpp @@ -19,6 +19,7 @@ #include "qgsmesheditor.h" #include "qgsmessagelog.h" #include "qgsgeometryutils.h" +#include "qgscircle.h" #include #include @@ -2584,3 +2585,41 @@ QgsTopologicalMesh::Changes QgsTopologicalMesh::changeXYValue( const QList return changes; } + +bool QgsTopologicalMesh::delaunayConditionForEdge( int vertexIndex1, int vertexIndex2 ) +{ + int faceIndex1; + int faceIndex2; + int oppositeVertexFace1; + int oppositeVertexFace2; + int supposedOppositeVertexFace1; + int supposedoppositeVertexFace2; + + bool result = eitherSideFacesAndVertices( + vertexIndex1, + vertexIndex2, + faceIndex1, + faceIndex2, + oppositeVertexFace1, + supposedoppositeVertexFace2, + supposedOppositeVertexFace1, + oppositeVertexFace2 ); + + if ( ! result ) + return false; + + const QgsMeshFace face1 = mMesh->face( faceIndex1 ); + const QgsMeshFace face2 = mMesh->face( faceIndex2 ); + + QgsCircle circle = QgsCircle::from3Points( mMesh->vertex( face1.at( 0 ) ), + mMesh->vertex( face1.at( 1 ) ), + mMesh->vertex( face1.at( 2 ) ) ); + bool circle1ContainsPoint = circle.contains( mMesh->vertex( supposedoppositeVertexFace2 ) ); + + circle = QgsCircle::from3Points( mMesh->vertex( face2.at( 0 ) ), + mMesh->vertex( face2.at( 1 ) ), + mMesh->vertex( face2.at( 2 ) ) ); + bool circle2ContainsPoint = circle.contains( mMesh->vertex( supposedOppositeVertexFace1 ) ); + + return !( circle1ContainsPoint || circle2ContainsPoint ); +} diff --git a/src/core/mesh/qgstopologicalmesh.h b/src/core/mesh/qgstopologicalmesh.h index d378a15ef7ff..30456313478a 100644 --- a/src/core/mesh/qgstopologicalmesh.h +++ b/src/core/mesh/qgstopologicalmesh.h @@ -232,6 +232,14 @@ class CORE_EXPORT QgsTopologicalMesh */ Changes flipEdge( int vertexIndex1, int vertexIndex2 ); + /** + * Check if Delaunay condition holds for given edge + * returns TRUE if delaunay condition holds FALSE otherwise + * + * \since QGIS 3.42 + */ + bool delaunayConditionForEdge( int vertexIndex1, int vertexIndex2 ); + /** * Returns TRUE if faces separated by vertices with indexes \a vertexIndex1 and \a vertexIndex2 can be merged */ diff --git a/tests/src/app/testqgsmaptooleditmesh.cpp b/tests/src/app/testqgsmaptooleditmesh.cpp index 7a7ea9201a1d..7f1455c774e7 100644 --- a/tests/src/app/testqgsmaptooleditmesh.cpp +++ b/tests/src/app/testqgsmaptooleditmesh.cpp @@ -14,6 +14,8 @@ * * ***************************************************************************/ +#include + #include "qgstest.h" #include "qgisapp.h" #include "testqgsmaptoolutils.h" @@ -25,11 +27,13 @@ #include "qgsterrainprovider.h" #include "qgsmeshtransformcoordinatesdockwidget.h" -class TestQgsMapToolEditMesh : public QObject +class TestQgsMapToolEditMesh : public QgsTest { Q_OBJECT public: - TestQgsMapToolEditMesh() = default; + TestQgsMapToolEditMesh() + : QgsTest( QStringLiteral( "Map Tool Edit Mesh Tests" ), QStringLiteral( "app" ) ) + {} private slots: void initTestCase(); // will be called before the first testfunction is executed. @@ -44,8 +48,11 @@ class TestQgsMapToolEditMesh : public QObject void selectElements(); void testAssignVertexZValueFromTerrainOnCreation(); void testAssignVertexZValueFromTerrainOnButtonClick(); + void testDelaunayRefinement(); private: + static QString read2DMFileContent( const QString &filePath ); + QgisApp *mQgisApp = nullptr; std::unique_ptr meshLayerQuadFlower; QString mDataDir; @@ -730,5 +737,97 @@ void TestQgsMapToolEditMesh::selectElements() QCOMPARE( mEditMeshMapTool->mSelectedVertices.count(), 3 ); } +void TestQgsMapToolEditMesh::testDelaunayRefinement() +{ + QgsCoordinateReferenceSystem crs3857; + crs3857.createFromString( "EPSG:3857" ); + + const QgsCoordinateTransform transform; + QgsMeshEditingError error; + QgsPointXY point; + + QString originalDataPath = QString( "/mesh/not_delaunay.2dm" ); + + // editing with normal setting - without delaunay refinement + mEditMeshMapTool->mWidgetActionDigitizing->mCheckBoxRefineNeighboringFaces->setChecked( false ); + + const QString copyDataPath1 = copyTestData( originalDataPath ); // copy of data to be edited + + std::unique_ptr layer = std::make_unique( copyDataPath1, "not delaunay", "mdal" ); + layer->setCrs( crs3857 ); + QVERIFY( layer->isValid() ); + + layer->startFrameEditing( transform, error, false ); + QVERIFY( error == QgsMeshEditingError() ); + + mCanvas->setLayers( QList() << layer.get() ); + mCanvas->setDestinationCrs( crs3857 ); + mCanvas->setExtent( layer->extent() ); + + QVERIFY( layer->meshEditor() ); + + TestQgsMapToolAdvancedDigitizingUtils tool( mEditMeshMapTool ); + mCanvas->setCurrentLayer( layer.get() ); + mEditMeshMapTool->mActionDigitizing->trigger(); + + point = QgsPointXY( 4.5, 3.5 ); + QCOMPARE( layer->meshEditor()->validFacesCount(), 8 ); + tool.mouseMove( point.x(), point.y() ); + tool.mouseDoubleClick( point.x(), point.y(), Qt::LeftButton ); + QCOMPARE( layer->meshEditor()->validFacesCount(), 10 ); + QCOMPARE( layer->undoStack()->command( 0 )->text(), "Add 1 vertices" ); + + QVERIFY( layer->commitFrameEditing( transform, false ) ); + + QGSCOMPARELONGSTR( "edit_no_delaunay_refinement", "not_delaunay.2dm", TestQgsMapToolEditMesh::read2DMFileContent( copyDataPath1 ).toUtf8() ); + + // editing with delaunay refinement + mEditMeshMapTool->mWidgetActionDigitizing->mCheckBoxRefineNeighboringFaces->setChecked( true ); + + const QString copyDataPath2 = copyTestData( originalDataPath ); // copy of data to be edited + + layer = std::make_unique( copyDataPath2, "not delaunay", "mdal" ); + layer->setCrs( crs3857 ); + QVERIFY( layer->isValid() ); + + layer->startFrameEditing( transform, error, false ); + QVERIFY( error == QgsMeshEditingError() ); + + mCanvas->setLayers( QList() << layer.get() ); + mCanvas->setDestinationCrs( crs3857 ); + mCanvas->setExtent( layer->extent() ); + + QVERIFY( layer->meshEditor() ); + + mCanvas->setCurrentLayer( layer.get() ); + mEditMeshMapTool->mActionDigitizing->trigger(); + + point = QgsPointXY( 4.5, 3.5 ); + QCOMPARE( layer->meshEditor()->validFacesCount(), 8 ); + tool.mouseMove( point.x(), point.y() ); + tool.mouseDoubleClick( point.x(), point.y(), Qt::LeftButton ); + QCOMPARE( layer->meshEditor()->validFacesCount(), 10 ); + QCOMPARE( layer->undoStack()->command( 0 )->text(), "Add vertex inside face with Delaunay refinement" ); + + QVERIFY( layer->commitFrameEditing( transform, false ) ); + + QGSCOMPARELONGSTR( "edit_delaunay_refinement", "delaunay.2dm", TestQgsMapToolEditMesh::read2DMFileContent( copyDataPath2 ).toUtf8() ); +} + +QString TestQgsMapToolEditMesh::read2DMFileContent( const QString &filePath ) +{ + QFile file( filePath ); + if ( !file.open( QIODevice::ReadOnly | QIODevice::Text ) ) + { + return QString(); // Return empty string if file can't be opened + } + + QTextStream in( &file ); + + QString content = in.readAll(); + file.close(); + return content; +} + QGSTEST_MAIN( TestQgsMapToolEditMesh ) #include "testqgsmaptooleditmesh.moc" diff --git a/tests/testdata/control_files/app/expected_edit_delaunay_refinement/expected_delaunay.2dm b/tests/testdata/control_files/app/expected_edit_delaunay_refinement/expected_delaunay.2dm new file mode 100644 index 000000000000..bee99533d8ac --- /dev/null +++ b/tests/testdata/control_files/app/expected_edit_delaunay_refinement/expected_delaunay.2dm @@ -0,0 +1,21 @@ +MESH2D +ND 1 2.50000000e+00 2.00000000e+00 0 +ND 2 3.00000000e+00 7.50000000e+00 0 +ND 3 8.00000000e+00 2.50000000e+00 0 +ND 4 1.50000000e+00 2.50000000e+00 0 +ND 5 1.50000000e+00 0.00000000e+00 0 +ND 6 8.00000000e+00 0.00000000e+00 0 +ND 7 7.50000000e+00 7.50000000e+00 0 +ND 8 1.00000000e+01 0.00000000e+00 0 +ND 9 9.50000000e+00 3.50000000e+00 0 +ND 10 4.49476987e+00 3.49895397e+00 0 +E4Q 1 6 3 1 5 +E3T 2 6 8 3 +E3T 3 7 3 8 +E3T 4 8 9 7 +E3T 5 10 1 3 +E3T 6 10 2 1 +E3T 7 10 7 2 +E3T 8 7 10 3 +E3T 9 4 1 2 +E3T 10 1 4 5 diff --git a/tests/testdata/control_files/app/expected_edit_no_delaunay_refinement/expected_not_delaunay.2dm b/tests/testdata/control_files/app/expected_edit_no_delaunay_refinement/expected_not_delaunay.2dm new file mode 100644 index 000000000000..bfd241f3dc3a --- /dev/null +++ b/tests/testdata/control_files/app/expected_edit_no_delaunay_refinement/expected_not_delaunay.2dm @@ -0,0 +1,21 @@ +MESH2D +ND 1 2.50000000e+00 2.00000000e+00 0 +ND 2 3.00000000e+00 7.50000000e+00 0 +ND 3 8.00000000e+00 2.50000000e+00 0 +ND 4 1.50000000e+00 2.50000000e+00 0 +ND 5 1.50000000e+00 0.00000000e+00 0 +ND 6 8.00000000e+00 0.00000000e+00 0 +ND 7 7.50000000e+00 7.50000000e+00 0 +ND 8 1.00000000e+01 0.00000000e+00 0 +ND 9 9.50000000e+00 3.50000000e+00 0 +ND 10 4.49476987e+00 3.49895397e+00 0 +E3T 1 2 5 1 +E3T 2 2 4 5 +E4Q 3 6 3 1 5 +E3T 4 3 7 2 +E3T 5 6 8 3 +E3T 6 7 3 8 +E3T 7 8 9 7 +E3T 8 10 1 3 +E3T 9 10 3 2 +E3T 10 10 2 1 diff --git a/tests/testdata/mesh/not_delaunay.2dm b/tests/testdata/mesh/not_delaunay.2dm new file mode 100644 index 000000000000..71a95ea43144 --- /dev/null +++ b/tests/testdata/mesh/not_delaunay.2dm @@ -0,0 +1,18 @@ +MESH2D +ND 1 2.5 2 0 +ND 2 3 7.5 0 +ND 3 8 2.5 0 +ND 4 1.5 2.5 0 +ND 5 1.5 0 0 +ND 6 8 0 0 +ND 7 7.5 7.5 0 +ND 8 10 0 0 +ND 9 9.5 3.5 0 +E3T 1 1 3 2 +E3T 2 2 5 1 +E3T 3 2 4 5 +E4Q 4 6 3 1 5 +E3T 5 3 7 2 +E3T 6 6 8 3 +E3T 7 7 3 8 +E3T 8 8 9 7