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

Mesh editing experimentation for Lekan #47

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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
799 changes: 702 additions & 97 deletions src/analysis/interpolation/qgsdualedgetriangulation.cpp

Large diffs are not rendered by default.

69 changes: 65 additions & 4 deletions src/analysis/interpolation/qgsdualedgetriangulation.h
Original file line number Diff line number Diff line change
@@ -27,6 +27,7 @@
#include <QBuffer>
#include <QStringList>
#include <QCursor>
#include <QStack>

#include <cfloat>

@@ -55,6 +56,10 @@ class ANALYSIS_EXPORT QgsDualEdgeTriangulation: public QgsTriangulation
~QgsDualEdgeTriangulation() override;
void addLine( const QVector< QgsPoint > &points, QgsInterpolator::SourceType lineType ) override;
int addPoint( const QgsPoint &p ) override;
bool removePoint( int index );
int addLocalPoint( const QgsPoint &p );
int dimension() const;

//! Performs a consistency check, remove this later
void performConsistencyTest() override;
//! Calculates the normal at a point on the surface
@@ -82,9 +87,9 @@ class ANALYSIS_EXPORT QgsDualEdgeTriangulation: public QgsTriangulation
void setForcedCrossBehavior( QgsTriangulation::ForcedCrossBehavior b ) override;
//! Sets an interpolator object
void setTriangleInterpolator( TriangleInterpolator *interpolator ) override;
//! Eliminates the horizontal triangles by swapping or by insertion of new points
//! Eliminates the horizontal triangles by swapping or by insertion of new points, if triangultion is in edit mode, calling this method will end the edit mode
void eliminateHorizontalTriangles() override;
//! Adds points to make the triangles better shaped (algorithm of ruppert)
//! Adds points to make the triangles better shaped (algorithm of ruppert), if triangultion is in edit mode, calling this method will end the edit mode
void ruppertRefinement() override;
//! Returns TRUE, if the point with coordinates x and y is inside the convex hull and FALSE otherwise
bool pointInside( double x, double y ) override;
@@ -95,8 +100,33 @@ class ANALYSIS_EXPORT QgsDualEdgeTriangulation: public QgsTriangulation

bool saveTriangulation( QgsFeatureSink *sink, QgsFeedback *feedback = nullptr ) const override;

//! Returns a QgsMesh from the triagulation. If not in edit mode, calling this method for the first time (after modification) leads to build a new mesh that can be a long task
virtual QgsMesh triangulationToMesh( QgsFeedback *feedback = nullptr ) const override;

/**
* In edits mode, returns the modified mesh and the extent where changed have been made
*
* The returned mesh can have empty vertices and empty faces to keep the indexes consistent with the different mesh
* returned between eiditing operation. New vertices can replace deleted vertices, but new faces are always appended,
* that is the index of new faces are always greater or equal of the count of faces of former meshes.
* By this way, caller of this method can knonw which extent has to be updated, and which faces are new.
*
*/
virtual QgsMesh editedTriangulationToMesh( QSet<int> &changedPointsIndex ) const;

/**
* Starts the edit mode.
* In edit mode, a QgsMesh is cache in memory and this mesh is sync with the triangulation when a adding and removing point, and swap faces.
* Int editing mode, calling triangulationToMesh() after editing not lead to rebuild a new mesh but returns the mesh in cache.
*/
void startEditMode();

/**
* Ends the edit mode.
* The mesh in cache is cleared and must be rebuilt when calling triangulationToMesh() for the first time.
*/
void endEditMode();

private:
//! X-coordinate of the upper right corner of the bounding box
double mXMax = 0;
@@ -120,6 +150,7 @@ class ANALYSIS_EXPORT QgsDualEdgeTriangulation: public QgsTriangulation
QgsTriangulation::ForcedCrossBehavior mForcedCrossBehavior = QgsTriangulation::DeleteFirst;
//! Inserts an edge and makes sure, everything is OK with the storage of the edge. The number of the HalfEdge is returned
unsigned int insertEdge( int dual, int next, int point, bool mbreak, bool forced );

//! Inserts a forced segment between the points with the numbers p1 and p2 into the triangulation and returns the number of a HalfEdge belonging to this forced edge or -100 in case of failure
int insertForcedSegment( int p1, int p2, QgsInterpolator::SourceType segmentType );
//! Security to prevent endless loops in 'baseEdgeOfTriangle'. It there are more iteration then this number, the point will not be inserted
@@ -148,7 +179,7 @@ class ANALYSIS_EXPORT QgsDualEdgeTriangulation: public QgsTriangulation
//! Number of an edge on the outside of the convex hull. It is updated in method 'baseEdgeOfTriangle' to enable insertion of points outside the convex hull
int mEdgeOutside = -1;
//! If an inserted point is exactly on an existing edge, 'baseEdgeOfTriangle' returns -20 and sets the variable 'mEdgeWithPoint'
unsigned int mEdgeWithPoint = 0;
unsigned int mEdgeOnPoint = 0;
//! If an instability occurs in 'baseEdgeOfTriangle', mUnstableEdge is set to the value of the current edge
unsigned int mUnstableEdge = 0;
//! If a point has been inserted twice, its number is stored in this member
@@ -172,7 +203,37 @@ class ANALYSIS_EXPORT QgsDualEdgeTriangulation: public QgsTriangulation

int firstEdgeOutSide();

void removeLastPoint();
bool mIsEditing = false;
mutable QgsMesh mCacheMesh;
mutable QHash<int, int> mHalfEdgeToMeshFace;
mutable QList<int> mRemovedPoints;
mutable QStack<int> mAvailableHalfEdges;
mutable QSet<int> mChangedPoint;

//! Creates a new point and returns its index
int createPoint( const QgsPoint &point );

//! Deletes the point at \a index
void deletePoint( int index );

//! Deletes the hal edge at \a index
void deleteHalfEdge( int index );

//! Checks if the half edge \a index and the next ones form a face, if yes create a mesh face
void closeEdgeForMesh( int index );

//! Updates the face mesh associate with the hafl edge \a edgeIndex, that is, reassociate vertex index for example after a swap
void updateFaceMesh( int edgeIndex );

//! removes tje face mesh associate with the hald edge \a edgeIndex
void removeFaceMesh( int edgeIndex );

//! removes all null points and null halfedge due to edit mode. Clears the mesh in cache.
void purge();

//! Returns a edge inside the convex hull. This edge depends on the previous operation but will surely be in the hull
int edgeInsideConvexHull();



friend class TestQgsInterpolator;
6 changes: 6 additions & 0 deletions src/analysis/mesh/qgsmeshtriangulation.cpp
Original file line number Diff line number Diff line change
@@ -104,6 +104,12 @@ void QgsMeshTriangulation::setCrs( const QgsCoordinateReferenceSystem &crs )
mCrs = crs;
}

QgsRectangle QgsMeshTriangulation::addVertex( const QgsPoint &point )
{
QgsRectangle *changedExtent;
mTriangulation->addPoint( point );
}

void QgsMeshTriangulation::addVerticesFromFeature( const QgsFeature &feature, int valueAttribute, const QgsCoordinateTransform &transform, QgsFeedback *feedback )
{
QgsGeometry geom = feature.geometry();
2 changes: 2 additions & 0 deletions src/analysis/mesh/qgsmeshtriangulation.h
Original file line number Diff line number Diff line change
@@ -78,6 +78,8 @@ class ANALYSIS_EXPORT QgsMeshTriangulation : public QObject
//! Sets the coordinate reference system used for the triangulation
void setCrs( const QgsCoordinateReferenceSystem &crs );

QgsRectangle addVertex( const QgsPoint &point );

private:
#ifdef SIP_RUN
QgsMeshTriangulation( const QgsMeshTriangulation &rhs );
2 changes: 2 additions & 0 deletions src/core/mesh/qgsmeshdataprovider.h
Original file line number Diff line number Diff line change
@@ -168,6 +168,8 @@ class CORE_EXPORT QgsMeshDataSourceInterface SIP_ABSTRACT
* \since QGIS 3.6
*/
virtual void populateMesh( QgsMesh *mesh ) const = 0;

virtual bool updateMesh( QgsMesh *mesh, QList<int> &updatedVerticesIndex ) {}
};

/**
28 changes: 28 additions & 0 deletions src/core/mesh/qgsmeshlayer.cpp
Original file line number Diff line number Diff line change
@@ -296,8 +296,17 @@ void QgsMeshLayer::updateTriangularMesh( const QgsCoordinateTransform &transfor
}

if ( mTriangularMeshes[0].get()->update( mNativeMesh.get(), transform ) )
{
mTriangularMeshes.resize( 1 ); //if the base triangular mesh is effectivly updated, remove simplified meshes
}
else if ( mIsMeshNeedLocalUpdate )
{
mTriangularMeshes.resize( 1 );
mTriangularMeshes.at( 0 )->update( mNativeMesh.get(), mUpdatedVertices );
}

mIsMeshNeedLocalUpdate = false;
mUpdatedVertices.clear();
createSimplifiedMeshes();
}

@@ -693,6 +702,25 @@ void QgsMeshLayer::onDatasetGroupsAdded( const QList<int> &datasetGroupIndexes )
emit rendererChanged();
}

void QgsMeshLayer::onMeshLocalyChanged()
{
if ( !mDataProvider )
return;

if ( !mNativeMesh )
{
fillNativeMesh();
return;
}

QList<int> updatedVertices;
if ( mDataProvider->updateMesh( mNativeMesh.get(), updatedVertices ) )
{
mIsMeshNeedLocalUpdate = true;
mUpdatedVertices = updatedVertices;
}
}

QgsMeshDatasetGroupTreeItem *QgsMeshLayer::datasetGroupTreeRootItem() const
{
return mDatasetGroupStore->datasetGroupTreeItem();
4 changes: 4 additions & 0 deletions src/core/mesh/qgsmeshlayer.h
Original file line number Diff line number Diff line change
@@ -783,6 +783,7 @@ class CORE_EXPORT QgsMeshLayer : public QgsMapLayer

private slots:
void onDatasetGroupsAdded( const QList<int> &datasetGroupIndexes );
void onMeshLocalyChanged();

private:
//! Pointer to data provider derived from the abastract base class QgsMeshDataProvider
@@ -813,6 +814,9 @@ class CORE_EXPORT QgsMeshLayer : public QgsMapLayer
int mStaticScalarDatasetIndex = 0;
int mStaticVectorDatasetIndex = 0;

bool mIsMeshNeedLocalUpdate = false;
QList<int> mUpdatedVertices;

int closestEdge( const QgsPointXY &point, double searchRadius, QgsPointXY &projectedPoint ) const;

//! Returns the exact position in map coordinates of the closest vertex in the search area
34 changes: 34 additions & 0 deletions src/core/mesh/qgsmeshspatialindex.cpp
Original file line number Diff line number Diff line change
@@ -382,3 +382,37 @@ QgsMesh::ElementType QgsMeshSpatialIndex::elementType() const
{
return mElementType;
}

void QgsMeshSpatialIndex::addFace( int faceIndex, const QgsMesh &mesh )
{
SpatialIndex::Region r( faceToRegion( mesh, faceIndex ) );

QMutexLocker locker( &d->mMutex );

// TODO: handle possible exceptions correctly
try
{
d->mRTree->insertData( 0, nullptr, r, faceIndex );
}
catch ( Tools::Exception &e )
{
Q_UNUSED( e )
QgsDebugMsg( QStringLiteral( "Tools::Exception caught: " ).arg( e.what().c_str() ) );
}
catch ( const std::exception &e )
{
Q_UNUSED( e )
QgsDebugMsg( QStringLiteral( "std::exception caught: " ).arg( e.what() ) );
}
catch ( ... )
{
QgsDebugMsg( QStringLiteral( "unknown spatial index exception caught" ) );
}
}

void QgsMeshSpatialIndex::removeFace( int faceIndex, const QgsMesh &mesh )
{
QMutexLocker locker( &d->mMutex );

d->mRTree->deleteData( faceToRegion( mesh, faceIndex ), faceIndex );
}
6 changes: 6 additions & 0 deletions src/core/mesh/qgsmeshspatialindex.h
Original file line number Diff line number Diff line change
@@ -100,9 +100,15 @@ class CORE_EXPORT QgsMeshSpatialIndex
*/
QgsMesh::ElementType elementType() const;

void addFace( int faceIndex, const QgsMesh &mesh );

void removeFace( int faceIndex, const QgsMesh &mesh );

private:
QgsMesh::ElementType mElementType = QgsMesh::ElementType::Face;
QSharedDataPointer<QgsMeshSpatialIndexData> d;

bool addElement( int index, const QgsRectangle &bounds );
};

#endif //QGSMESHSPATIALINDEX_H
Loading