/*************************************************************************** qgstopologicalmesh.h - QgsTopologicalMesh --------------------- begin : 18.6.2021 copyright : (C) 2021 by Vincent Cloarec email : vcloarec at gmail dot com *************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef QGSTOPOLOGICALMESH_H #define QGSTOPOLOGICALMESH_H #include #include "qgsmeshdataprovider.h" SIP_NO_FILE class QgsMeshEditingError; class QgsMeshVertexCirculator; /** * \ingroup core * * \brief Class that wraps a QgsMesh to ensure the consistency of the mesh during editing and help to access to elements from other elements * * A topological face need to: * * - be convex * - counter clock wise * - not share an unique vertex with another face * * \since QGIS 3.22 */ class CORE_EXPORT QgsTopologicalMesh { public: using FaceNeighbors = QVector; /** * \ingroup core * * \brief Class that contains independent faces an topological information about this faces * * This class supports unique shared vertex between faces * * \since QGIS 3.22 */ class TopologicalFaces { public: //! Returns faces QVector meshFaces() const {return mFaces;} //! Clears all data contained in the instance. void clear(); //! Returns the face neighborhood of the faces, indexing is local QVector facesNeighborhood() const; private: QVector mFaces; // the faces containing the vertices indexes in the mesh QVector mFacesNeighborhood; // neighborhood of the faces, face indexes are local QMultiHash mVerticesToFace; // map of vertices to incident face, face indexes are local QList mBoundaries; // list of boundary vertices indexes in the mesh friend class QgsTopologicalMesh; friend class QgsMeshVertexCirculator; }; /** * \ingroup core * * \brief Class that contains topological differences between two states of a topological mesh, only accessible from the QgsTopologicalMesh class * * \since QGIS 3.22 */ class Changes { public: //! Returns the face that are added with this changes QVector addedFaces() const; //! Returns the faces that are removed with this changes QVector removedFaces() const; //! Returns the indexes of the faces that are removed with this changes QList removedFaceIndexes() const; //! Returns the added vertices with this changes QVector addedVertices() const; //! Returns the indexes of vertices to remove QList verticesToRemoveIndexes() const; //! Returns the indexes of vertices that have changed coordinates QList changedCoordinatesVerticesIndexes() const; //! Returns the new Z values of vertices that have changed their coordinates QList newVerticesZValues() const; //! Returns the new (X,Y) values of vertices that have changed their coordinates QList newVerticesXYValues() const; //! Returns the old (X,Y) values of vertices that have changed their coordinates QList oldVerticesXYValues() const; //! Returns a list of the native face indexes that have a geometry changed QList nativeFacesIndexesGeometryChanged() const; protected: int mAddedFacesFirstIndex = 0; QList mFaceIndexesToRemove; // the removed faces indexes in the mesh QVector mFacesToAdd; QVector mFacesNeighborhoodToAdd; QVector mFacesToRemove; QVector mFacesNeighborhoodToRemove; QList> mNeighborhoodChanges; // {index of concerned face, neigbor position, previous value, changed value} QVector mVerticesToAdd; QVector mVertexToFaceToAdd; QList mVerticesToRemoveIndexes; QList mRemovedVertices; QList mVerticesToFaceRemoved; QList> mVerticesToFaceChanges; // {index of concerned vertex, previous value, changed value} QList mChangeCoordinateVerticesIndexes; QList mNewZValues; QList mOldZValues; QList mNewXYValues; QList mOldXYValues; QList mNativeFacesIndexesGeometryChanged; //! Clears all changes void clearChanges(); private: int addedFaceIndexInMesh( int internalIndex ) const; int removedFaceIndexInmesh( int internalIndex ) const; friend class QgsTopologicalMesh; }; /** * Creates a topologicaly consistent mesh with \a mesh, this static method modifies \a mesh to be topological consistent * and return a QgsTopologicalMesh instance that contains and handles this mesh (does not take ownership). */ static QgsTopologicalMesh createTopologicalMesh( QgsMesh *mesh, int maxVerticesPerFace, QgsMeshEditingError &error ); //! Creates new topological faces that are not yet included in the mesh static TopologicalFaces createNewTopologicalFaces( const QVector &faces, bool uniqueSharedVertexAllowed, QgsMeshEditingError &error ); //----------- access element methods //! Returns the indexes of neighbor faces of the face with index \a faceIndex QVector neighborsOfFace( int faceIndex ) const; //! Returns the indexes of faces that are around the vertex with index \a vertexIndex QList facesAroundVertex( int vertexIndex ) const; //! Returns a pointer to the wrapped mesh QgsMesh *mesh() const; //! Returns the index of the first face linked, returns -1 if it is a free vertex or out of range index int firstFaceLinked( int vertexIndex ) const; //! Returns whether the vertex is on a boundary bool isVertexOnBoundary( int vertexIndex ) const; //! Returns whether the vertex is a free vertex bool isVertexFree( int vertexIndex ) const; //! Returns a list of vertices are not linked to any faces QList freeVerticesIndexes() const; //! Returns a vertex circulator linked to this mesh around the vertex with index \a vertexIndex QgsMeshVertexCirculator vertexCirculator( int vertexIndex ) const; //----------- editing methods //! Returns whether the faces can be added to the mesh QgsMeshEditingError canFacesBeAdded( const TopologicalFaces &topologicalFaces ) const; /** * Adds faces \a topologicFaces to the topologic mesh. * The method returns a instance of the class QgsTopologicalMesh::Change that can be used to reverse or reapply the operation. */ Changes addFaces( const TopologicalFaces &topologicFaces ); /** * Returns whether faces with index in \a faceIndexes can be removed/ * The method an error object with type QgsMeshEditingError::NoError if the faces can be removed, otherwise returns the corresponding error */ QgsMeshEditingError canFacesBeRemoved( const QList facesIndexes ); /** * Removes faces with index in \a faceIndexes. * The method returns a instance of the class QgsTopologicalMesh::Change that can be used to reverse or reapply the operation. */ Changes removeFaces( const QList facesIndexes ); /** * Returns TRUE if the edge can be flipped (only available for edge shared by two faces with 3 vertices) */ bool edgeCanBeFlipped( int vertexIndex1, int vertexIndex2 ) const; /** * Flips edge (\a vertexIndex1, \a vertexIndex2) * The method returns a instance of the class QgsTopologicalMesh::Change that can be used to reverse or reapply the operation. */ Changes flipEdge( int vertexIndex1, int vertexIndex2 ); /** * Returns TRUE if faces separated by vertices with indexes \a vertexIndex1 and \a vertexIndex2 can be merged */ bool canBeMerged( int vertexIndex1, int vertexIndex2 ) const; /** * Merges faces separated by vertices with indexes \a vertexIndex1 and \a vertexIndex2 * The method returns a instance of the class QgsTopologicalMesh::Change that can be used to reverse or reapply the operation. */ Changes merge( int vertexIndex1, int vertexIndex2 ); /** * Returns TRUE if face with index \a faceIndex can be split */ bool faceCanBeSplit( int faceIndex ) const; /** * Splits face with index \a faceIndex * The method returns a instance of the class QgsTopologicalMesh::Change that can be used to reverse or reapply the operation. */ Changes splitFace( int faceIndex ); /** * Adds a \a vertex in the face with index \a faceIndex. The including face is removed and new faces surrounding the added vertex are added. * The method returns a instance of the class QgsTopologicalMesh::Change that can be used to reverse or reapply the operation. */ Changes addVertexInface( int faceIndex, const QgsMeshVertex &vertex ); /** * Adds a free \a vertex in the face, that is a vertex tha tis not included or linked with any faces. * The method returns a instance of the class QgsTopologicalMesh::Change that can be used to reverse or reapply the operation. */ Changes addFreeVertex( const QgsMeshVertex &vertex ); /** * Removes the vertex with index \a vertexIndex. * If the vertex in linked with faces, the operation leads also to remove the faces. In this case, the hole is filled by a triangulation. * The method returns a instance of the class QgsTopologicalMesh::Change that can be used to reverse or reapply the operation. */ Changes removeVertexFillHole( int vertexIndex ); /** * Removes all the vertices with index in the list \a vertices * If vertices in linked with faces, the operation leads also to remove the faces without filling holes. * The method returns a instance of the class QgsTopologicalMesh::Change that can be used to reverse or reapply the operation. */ Changes removeVertices( const QList &vertices ); /** * Changes the Z values of the vertices with indexes in \a vertices indexes with the values in \a newValues */ Changes changeZValue( const QList &verticesIndexes, const QList &newValues ); /** * Changes the (X,Y) values of the vertices with indexes in \a vertices indexes with the values in \a newValues */ Changes changeXYValue( const QList &verticesIndexes, const QList &newValues ); //! Applies the changes void applyChanges( const Changes &changes ); //! Reverses the changes void reverseChanges( const Changes &changes ); //! Checks the topology of the face and sets it counter clockwise if necessary static QgsMeshEditingError counterClockwiseFaces( QgsMeshFace &face, QgsMesh *mesh ); /** * Reindexes faces and vertices, after this operation, the topological * mesh can't be edited anymore and only the method mesh can be used to access to the raw mesh. */ void reindex(); //! Checks the consistency of the topological mesh and return FALSE if there is a consistency issue QgsMeshEditingError checkConsistency() const; private: //! Creates topological faces from mesh faces static TopologicalFaces createTopologicalFaces( const QVector &faces, QVector *globalVertexToFace, QgsMeshEditingError &error, bool allowUniqueSharedVertex ); //! Returns all faces indexes that are concerned by the face with index in \a faceIndex, that is sharing a least one vertex or one edge QSet concernedFacesBy( const QList faceIndexes ) const; //! References the vertex as a free vertex to be able to access to all free vertices void referenceAsFreeVertex( int vertexIndex ); //! References the vertex as a free vertex void dereferenceAsFreeVertex( int vertexIndex ); /** * Returns faces that are either side of th edge (\a vertexIndex1, \a vertexIndex2) * and neighbor vertices of entry vertex in each faces */ bool eitherSideFacesAndVertices( int vertexIndex1, int vertexIndex2, int &face1, int &face2, int &neighborVertex1InFace1, int &neighborVertex1InFace2, int &neighborVertex2inFace1, int &neighborVertex2inFace2 ) const; //Attributes QgsMesh *mMesh = nullptr; QVector mVertexToFace; QVector mFacesNeighborhood; QSet mFreeVertices; int mMaximumVerticesPerFace = 0; friend class QgsMeshVertexCirculator; }; /** * \ingroup core * * \brief Convenient class that turn around a vertex and provide information about faces and vertices * * \since QGIS 3.22 */ class CORE_EXPORT QgsMeshVertexCirculator { public: //! Constructor with \a topologicalMesh and \a vertexIndex QgsMeshVertexCirculator( const QgsTopologicalMesh &topologicalMesh, int vertexIndex ); /** * Constructor with \a topologicFaces, \a vertexIndex and the index \a faceIndex of the face containing the vertex * \note This circulator only concerns faces that are in the same bloc of the face \a faceIndex. Other faces that could be share only * the vertex \a vertexIndex can't be accessible with this circulator */ QgsMeshVertexCirculator( const QgsTopologicalMesh::TopologicalFaces &topologicalFaces, int faceIndex, int vertexIndex ); /** * Constructor with \a topologicFaces, \a vertexIndex * \note This circulator only concerns faces that are in the same bloc of the first face linked to the vertex \a vertexIndex. * Other faces that could be share only the vertex \a vertexIndex can't be accessible with this circulator */ QgsMeshVertexCirculator( const QgsTopologicalMesh::TopologicalFaces &topologicalFaces, int vertexIndex ); //! Turns counter clockwise around the vertex and returns the new current face, -1 if the circulator pass a boundary or circulator is invalid int turnCounterClockwise() const; //! Turns counter clockwise around the vertex and returns the new current face, -1 if the circulator pass a boundary or circulator is invalid int turnClockwise() const; //! Returns the current face index, -1 if the circulator has passed a boundary or circulator is invalid int currentFaceIndex() const; //! Returns the current face, empty face if the circulator pass a boundary or circulator is invalid QgsMeshFace currentFace() const; //! Sets the circulator on the boundary face turning clockwise, return false is there isn't boundary face bool goBoundaryClockwise() const; //! Sets the circulator on the boundary face turning counter clockwise, return false is there isn't boundary face bool goBoundaryCounterClockwise() const; //! Returns the opposite vertex of the current face and on the edge on the side turning clockwise int oppositeVertexClockwise() const; //! Returns the opposite vertex of the current face and on the edge on the side turning counter clockwise int oppositeVertexCounterClockwise() const; //! Returns whether the vertex circulator is valid bool isValid() const; //! Returns all the faces indexes around the vertex QList facesAround() const; private: const QVector mFaces; const QVector mFacesNeighborhood; const int mVertexIndex = -1; mutable int mCurrentFace = -1; mutable int mLastValidFace = -1; bool mIsValid = false; int positionInCurrentFace() const; }; #endif // QGSTOPOLOGICALMESH_H