// SPDX-License-Identifier: LGPL-2.1-or-later /*************************************************************************** * Copyright (c) 2005 Imetric 3D GmbH * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ #ifndef MESH_ELEMENTS_H #define MESH_ELEMENTS_H #include #include #include #include #include #include #include "Definitions.h" // Cannot use namespace Base in constructors of MeshPoint #ifdef _MSC_VER using Base::Vector3f; #endif namespace MeshCore { class MeshHelpEdge; class MeshPoint; /** * Helper class providing an operator for comparison * of two edges. The class holds the point indices of the * underlying edge. */ class MeshExport MeshHelpEdge { public: inline bool operator==(const MeshHelpEdge& rclEdge) const; PointIndex _ulIndex[2]; // point indices }; /** * Structure that holds the facet index with the two corner point * indices of the facet's orientation this edge is attached to. */ class MeshExport MeshIndexEdge { public: FacetIndex _ulFacetIndex; // Facet index unsigned short _ausCorner[2]; // corner point indices of the facet }; /** MeshEdge just a pair of two point indices */ using MeshEdge = std::pair; struct MeshExport EdgeCollapse { PointIndex _fromPoint; PointIndex _toPoint; std::vector _adjacentFrom; // adjacent points to _fromPoint std::vector _adjacentTo; // adjacent points to _toPoint std::vector _removeFacets; std::vector _changeFacets; }; struct MeshExport VertexCollapse { PointIndex _point; std::vector _circumPoints; std::vector _circumFacets; }; /** * The MeshPoint class represents a point in the mesh data structure. The class inherits from * Vector3f and provides some additional information such as flag state and property value. * The flags can be modified by the Set() and Reset() and queried by IsFlag(). * A point can temporary be in an invalid state (e.g during deletion of several points), but * must not be set in general, i.e. always usable within a mesh-internal algorithm. * * Note: The status flag SEGMENT mark a point to be part of certain subset, a segment. * This flag must not be set by any algorithm unless it adds or removes points to a segment. * * Note: The status flag SELECTED mark a point to be selected which is e.g. used in the GUI. * This flag must not be set by any algorithm unless it adds or removes points to the selection. */ class MeshExport MeshPoint: public Base::Vector3f { public: enum TFlagType { INVALID = 1, VISIT = 2, SEGMENT = 4, MARKED = 8, SELECTED = 16, REV = 32, TMP0 = 64, TMP1 = 128 }; /** @name Construction */ //@{ MeshPoint() : _ucFlag(0) , _ulProp(0) {} inline MeshPoint(float x, float y, float z); inline MeshPoint(const Base::Vector3f& rclPt); // explicit bombs inline MeshPoint(const MeshPoint& rclPt) = default; inline MeshPoint(MeshPoint&& rclPt) = default; ~MeshPoint() = default; //@} public: /** @name Flag state * @note All flag methods are const as they do NOT change the actual behaviour of the object */ //@{ void SetFlag(TFlagType tF) const { _ucFlag |= static_cast(tF); } void ResetFlag(TFlagType tF) const { _ucFlag &= ~static_cast(tF); } bool IsFlag(TFlagType tF) const { return (_ucFlag & static_cast(tF)) == static_cast(tF); } void ResetInvalid() const { ResetFlag(INVALID); } void SetInvalid() const { SetFlag(INVALID); } bool IsValid() const { return !IsFlag(INVALID); } void SetProperty(unsigned long uP) const { _ulProp = uP; } //@} // Assignment inline MeshPoint& operator=(const MeshPoint& rclPt) = default; inline MeshPoint& operator=(MeshPoint&& rclPt) = default; // compare operator inline bool operator==(const MeshPoint& rclPt) const; inline bool operator==(const Base::Vector3f& rclV) const; inline bool operator<(const MeshPoint& rclPt) const; public: mutable unsigned char _ucFlag; /**< Flag member */ mutable unsigned long _ulProp; /**< Free usable property */ }; /** * The MeshGeomEdge class is geometric counterpart to MeshEdge that holds the * geometric data points of an edge. */ class MeshExport MeshGeomEdge { public: MeshGeomEdge() = default; /** Checks if the edge is inside the bounding box or intersects with it. */ bool ContainedByOrIntersectBoundingBox(const Base::BoundBox3f& rclBB) const; /** Returns the bounding box of the edge. */ Base::BoundBox3f GetBoundBox() const; /** Checks if the edge intersects with the given bounding box. */ bool IntersectBoundingBox(const Base::BoundBox3f& rclBB) const; /** Calculates the intersection point of the line defined by the base \a rclPt and the direction * \a rclDir with the edge. The intersection must be inside the edge. If there is no * intersection false is returned. */ bool IntersectWithLine( const Base::Vector3f& rclPt, const Base::Vector3f& rclDir, Base::Vector3f& rclRes ) const; /** Calculates the intersection point of an edge with this edge. * The intersection must be inside both edges. If there is no intersection false is returned. */ bool IntersectWithEdge(const MeshGeomEdge& edge, Base::Vector3f& res) const; /** Calculates the intersection point of the plane defined by the base \a rclPt and the * direction \a rclDir with the edge. The intersection must be inside the edge. If there is no * intersection false is returned. */ bool IntersectWithPlane( const Base::Vector3f& rclPt, const Base::Vector3f& rclDir, Base::Vector3f& rclRes ) const; /** * Calculates the projection of a point onto the line defined by the edge. The caller must check * if the projection point is inside the edge. */ void ProjectPointToLine(const Base::Vector3f& rclPoint, Base::Vector3f& rclProj) const; /** * Get the closest points \a rclPnt1 and \a rclPnt2 of the line defined by this edge and the * line defined by \a rclPt and \a rclDir. If the two points are identical then both lines * intersect each other. */ void ClosestPointsToLine( const Base::Vector3f& linePt, const Base::Vector3f& lineDir, Base::Vector3f& rclPnt1, Base::Vector3f& rclPnt2 ) const; /** * Checks if the point is part of the edge. A point is regarded as part * of an edge if the distance is lower than \a fDistance to the projected point * of \a rclPoint on the edge. */ bool IsPointOf(const Base::Vector3f& rclPoint, float fDistance) const; /** * Checks if the projection point of \a point lies on the edge. */ bool IsProjectionPointOf(const Base::Vector3f& point) const; /** * Checks if the two edges are parallel. * \note Parallel edges could be collinear. */ bool IsParallel(const MeshGeomEdge& edge) const; /** * Checks if the two edges are collinear. * \note Collinear edges always are parallel. */ bool IsCollinear(const MeshGeomEdge& edge) const; public: Base::Vector3f _aclPoints[2]; /**< Corner points */ bool _bBorder {false}; /**< Set to true if border edge */ }; /** * The MeshFacet class represent a triangle facet in the mesh data.structure. A facet indexes * three neighbour facets and also three corner points. * This class only keeps topologic information but no geometric information at all. * * Here are the most important conventions concerning the facet's orientation: * \li neighbour or edge number of 0 is defined by corner 0 and 1 * \li neighbour or edge number of 1 is defined by corner 1 and 2 * \li neighbour or edge number of 2 is defined by corner 2 and 0 * \li neighbour index is set to FACET_INDEX_MAX if there is no neighbour facet * * Note: The status flag SEGMENT mark a facet to be part of certain subset, a segment. * This flag must not be set by any algorithm unless it adds or removes facets to a segment. * * Note: The status flag SELECTED mark a facet to be selected which is e.g. used in the GUI. * This flag must not be set by any algorithm unless it adds or removes facets to the selection. */ class MeshFacet { public: enum TFlagType { INVALID = 1, VISIT = 2, SEGMENT = 4, MARKED = 8, SELECTED = 16, REV = 32, TMP0 = 64, TMP1 = 128 }; public: /** @name Construction */ //@{ inline MeshFacet(); inline MeshFacet(const MeshFacet& rclF) = default; inline MeshFacet(MeshFacet&& rclF) = default; inline MeshFacet( PointIndex p1, PointIndex p2, PointIndex p3, FacetIndex n1 = FACET_INDEX_MAX, FacetIndex n2 = FACET_INDEX_MAX, FacetIndex n3 = FACET_INDEX_MAX ); ~MeshFacet() = default; //@} /** @name Flag state * @note All flag methods are const as they do NOT change the actual behaviour of the object */ //@{ void SetFlag(TFlagType tF) const { _ucFlag |= static_cast(tF); } void ResetFlag(TFlagType tF) const { _ucFlag &= ~static_cast(tF); } bool IsFlag(TFlagType tF) const { return (_ucFlag & static_cast(tF)) == static_cast(tF); } void ResetInvalid() const { ResetFlag(INVALID); } void SetProperty(unsigned long uP) const { _ulProp = uP; } /** * Marks a facet as invalid. Should be used only temporary from within an algorithm * (e.g. deletion of several facets) but must not be set permanently. * From outside the data-structure must not have invalid facets. */ void SetInvalid() const { SetFlag(INVALID); } bool IsValid() const { return !IsFlag(INVALID); } //@} // Assignment inline MeshFacet& operator=(const MeshFacet& rclF) = default; inline MeshFacet& operator=(MeshFacet&& rclF) = default; inline void SetVertices(PointIndex, PointIndex, PointIndex); inline void SetNeighbours(FacetIndex, FacetIndex, FacetIndex); /** * Returns the indices of the corner points of the given edge number. */ inline void GetEdge(unsigned short usSide, MeshHelpEdge& rclEdge) const; /** * Returns the indices of the corner points of the given edge number. */ inline std::pair GetEdge(unsigned short usSide) const; /** * Returns the edge-number to the given index of neighbour facet. * If \a ulNIndex is not a neighbour USHRT_MAX is returned. */ inline unsigned short Side(FacetIndex ulNIndex) const; /** * Returns the edge-number defined by two points. If one point is * not a corner point USHRT_MAX is returned. */ inline unsigned short Side(PointIndex ulP0, PointIndex P1) const; /** * Returns the edge-number defined by the shared edge of both facets. If the facets don't * share a common edge USHRT_MAX is returned. */ inline unsigned short Side(const MeshFacet& rFace) const; /** * Returns true if this facet shares the same three points as \a rcFace. * The orientation is not of interest in this case. */ inline bool IsEqual(const MeshFacet& rcFace) const; /** * Replaces the index of the corner point that is equal to \a ulOrig * by \a ulNew. If the facet does not have a corner point with this index * nothing happens. */ inline void Transpose(PointIndex ulOrig, PointIndex ulNew); /** * Decrement the index for each corner point that is higher than \a ulIndex. */ inline void Decrement(PointIndex ulIndex); /** * Checks if the facets references the given point index. */ inline bool HasPoint(PointIndex) const; /** * Replaces the index of the neighbour facet that is equal to \a ulOrig * by \a ulNew. If the facet does not have a neighbourt with this index * nothing happens. */ inline void ReplaceNeighbour(FacetIndex ulOrig, FacetIndex ulNew); /** * Checks if the neighbour exists at the given edge-number. */ bool HasNeighbour(unsigned short usSide) const { return (_aulNeighbours[usSide] != FACET_INDEX_MAX); } /** * Checks if the given index is a neighbour facet. */ bool IsNeighbour(FacetIndex index) const { return Side(index) < 3; } /** Counts the number of edges without neighbour. */ inline unsigned short CountOpenEdges() const; /** Returns true if there is an edge without neighbour, otherwise false. */ inline bool HasOpenEdge() const; /** Returns true if the two facets have the same orientation, false otherwise * Therefore the two facets must be adjacent. */ inline bool HasSameOrientation(const MeshFacet&) const; /** Checks whether the facet is degenerated to a line of point. */ inline bool IsDegenerated() const; /** Flips the orientation of the facet. */ void FlipNormal() { std::swap(_aulPoints[1], _aulPoints[2]); std::swap(_aulNeighbours[0], _aulNeighbours[2]); } public: mutable unsigned char _ucFlag; /**< Flag member. */ mutable unsigned long _ulProp; /**< Free usable property. */ PointIndex _aulPoints[3]; /**< Indices of corner points. */ FacetIndex _aulNeighbours[3]; /**< Indices of neighbour facets. */ }; /** * The MeshGeomFacet class is geometric counterpart to MeshFacet that holds the * geometric data points of a triangle. */ class MeshExport MeshGeomFacet { public: /** @name Construction */ //@{ /// default constructor MeshGeomFacet(); /// Constructor with the corner points MeshGeomFacet(const Base::Vector3f& v1, const Base::Vector3f& v2, const Base::Vector3f& v3); MeshGeomFacet(const MeshGeomFacet&) = default; MeshGeomFacet(MeshGeomFacet&&) = default; /// Destruction ~MeshGeomFacet() = default; //@} MeshGeomFacet& operator=(const MeshGeomFacet&) = default; MeshGeomFacet& operator=(MeshGeomFacet&&) = default; public: /** * Checks if the point is part of the facet. A point is regarded as part * of a facet if the distance is lower \a fDistance and the projected point * in the facet normal direction is inside the triangle. */ bool IsPointOf(const Base::Vector3f& rclPoint, float fDistance) const; /** * Checks if the point is inside or at the border of the facet. The point * must already exactly lie on the plane defined by the facet, which is not * checked. This method is very efficient. */ bool IsPointOf(const Base::Vector3f& P) const; /** Checks whether the given point is inside the facet with tolerance \a fDistance. * This method does actually the same as IsPointOf() but this implementation * is done more effective through comparison of normals. */ bool IsPointOfFace(const Base::Vector3f& rclP, float fDistance) const; /** Calculates the weights \a w1, ..., \a w3 of the corners to get the point \a rclP, i.e. * rclP = w0*v0 + w1*v1 + w2*v2 (v0-v2 are the corners corners). * If w0+w1+w2==1.0 then the point rclP lies on the plane that is spanned by the facet, * otherwise the point doesn't lie on the plane. If the sum of wi is 1 and if each wi is between * [0,1] than the point lies inside the facet or on the border, respectively. * * If the point doesn't lie on the plane false is returned, true otherwise. */ bool Weights(const Base::Vector3f& rclP, float& w0, float& w1, float& w2) const; /** * Calculates the distance of a point to the plane defined by the triangle. */ inline float DistancePlaneToPoint(const Base::Vector3f& rclPoint) const; /** * Calculates the projection of a point onto the plane defined by the triangle. */ void ProjectPointToPlane(const Base::Vector3f& rclPoint, Base::Vector3f& rclProj) const; /** * Calculates the projection of a facet onto the plane defined by the triangle. */ void ProjectFacetToPlane(MeshGeomFacet& rclFacet) const; /** * Checks whether the triangle is degenerated. A triangle is degenerated if its area * is less than an epsilon. */ bool IsDegenerated(float epsilon) const; /** * Checks whether the triangle is deformed. A triangle is deformed if an angle * exceeds a given maximum angle or falls below a given minimum angle. * For performance reasons the cosine of minimum and maximum angle is expected. */ bool IsDeformed(float fCosOfMinAngle, float fCosOfMaxAngle) const; /** * Enlarges the triangle. */ void Enlarge(float fDist); /** * Calculates the facet normal for storing internally. */ inline void CalcNormal() const; /** * Arrange the facet normal so the both vectors have the same orientation. */ inline void ArrangeNormal(const Base::Vector3f& rclN); /** * Adjusts the facet's orientation to its normal. */ inline void AdjustCirculationDirection(); /** Invalidate the normal. It will be recomputed when querying it. */ void NormalInvalid() { _bNormalCalculated = false; } /** Query the flag state of the facet. */ bool IsFlag(MeshFacet::TFlagType tF) const { return (_ucFlag & static_cast(tF)) == static_cast(tF); } /** Set flag state */ void SetFlag(MeshFacet::TFlagType tF) { _ucFlag |= static_cast(tF); } /** Reset flag state */ void ResetFlag(MeshFacet::TFlagType tF) { _ucFlag &= ~static_cast(tF); } /** Calculates the facet's gravity point. */ inline Base::Vector3f GetGravityPoint() const; /** Returns the normal of the facet. */ inline Base::Vector3f GetNormal() const; /** Sets the facet's normal. */ inline void SetNormal(const Base::Vector3f& rclNormal); /** Returns the wrapping bounding box. */ inline Base::BoundBox3f GetBoundBox() const; /** Calculates the perimeter of the facet. */ inline float Perimeter() const; /** Calculates the area of a facet. */ inline float Area() const; /** Calculates the maximum angle of a facet. */ float MaximumAngle() const; /** Calculates the minimum angle of a facet. */ float MinimumAngle() const; /** Checks if the facet is inside the bounding box or intersects with it. */ inline bool ContainedByOrIntersectBoundingBox(const Base::BoundBox3f& rcBB) const; /** Checks if the facet intersects with the given bounding box. */ bool IntersectBoundingBox(const Base::BoundBox3f& rclBB) const; /** This method checks if both facets intersect. */ bool IntersectWithFacet(const MeshGeomFacet& rclFacet) const; /** * Intersect the facet with the other facet * The result is line given by two points (if intersected). * Return is the number of intersections points: 0: no intersection, 1: one intersection point * (rclPt0), 2: two intersections points (rclPt0, rclPt1) */ int IntersectWithFacet(const MeshGeomFacet& facet, Base::Vector3f& rclPt0, Base::Vector3f& rclPt1) const; /** Calculates the shortest distance from the line segment defined by \a rcP1 and \a rcP2 to * this facet. */ float DistanceToLineSegment(const Base::Vector3f& rcP1, const Base::Vector3f& rcP2) const; /** Calculates the shortest distance from the point \a rcPt to the facet. */ float DistanceToPoint(const Base::Vector3f& rcPt) const { Base::Vector3f res; return DistanceToPoint(rcPt, res); } /** Calculates the shortest distance from the point \a rcPt to the facet. \a rclNt is the point * of the facet with shortest distance. */ float DistanceToPoint(const Base::Vector3f& rclPt, Base::Vector3f& rclNt) const; /** Calculates the intersection point of the line defined by the base \a rclPt and the direction * \a rclDir with the facet. The intersection must be inside the facet. If there is no * intersection false is returned. */ bool IntersectWithLine( const Base::Vector3f& rclPt, const Base::Vector3f& rclDir, Base::Vector3f& rclRes ) const; /** Calculates the intersection point of the line defined by the base \a rclPt and the direction * \a rclDir with the facet. The intersection must be inside the facet. If there is no * intersection false is returned. This does actually the same as IntersectWithLine() with one * additionally constraint that the angle between the direction of the line and the normal of * the plane must not exceed \a fMaxAngle. */ bool Foraminate( const Base::Vector3f& P, const Base::Vector3f& dir, Base::Vector3f& I, float fMaxAngle = Mathf::PI ) const; /** Checks if the facet intersects with the plane defined by the base \a rclBase and the normal * \a rclNormal and returns true if two points are found, false otherwise. */ bool IntersectWithPlane( const Base::Vector3f& rclBase, const Base::Vector3f& rclNormal, Base::Vector3f& rclP1, Base::Vector3f& rclP2 ) const; /** * Checks if the facet intersects with the plane defined by the base \a rclBase and the normal * \a rclNormal. */ inline bool IntersectWithPlane(const Base::Vector3f& rclBase, const Base::Vector3f& rclNormal) const; /** Checks if the plane defined by the facet \a rclFacet intersects with the line defined by the * base \a rclBase and the direction \a rclNormal and returns the intersection point \a rclRes * if possible. */ bool IntersectPlaneWithLine( const Base::Vector3f& rclPt, const Base::Vector3f& rclDir, Base::Vector3f& rclRes ) const; /** Calculates the volume of the prism defined by two facets. * \note The two facets must not intersect. */ float VolumeOfPrism(const MeshGeomFacet& rclF) const; /** Subsamples the facet into points with resolution \a fStep. */ void SubSample(float fStep, std::vector& rclPoints) const; /** Calculates the center and radius of the inscribed circle of the facet. */ float CenterOfInscribedCircle(Base::Vector3f& rclCenter) const; /** Calculates the center and radius of the circum circle of the facet. */ float CenterOfCircumCircle(Base::Vector3f& rclCenter) const; /** Returns the edge number of the facet that is nearest to the point \a rclPt. */ unsigned short NearestEdgeToPoint(const Base::Vector3f& rclPt) const; /** Returns the edge number \a side of the facet and the distance to the edge that is nearest to * the point \a rclPt. */ void NearestEdgeToPoint(const Base::Vector3f& rclPt, float& fDistance, unsigned short& side) const; /** Returns the edge for \a side. */ MeshGeomEdge GetEdge(short side) const; /** The center and radius of the circum circle define a sphere in 3D. If the point \a rP is part * of this sphere true is returned, otherwise false. */ bool IsPointOfSphere(const Base::Vector3f& rP) const; /** This is an overloaded member function, provided for convenience. It behaves essentially like * the above function. If one of the facet's points is inside the sphere true is returned, * otherwise false. */ bool IsPointOfSphere(const MeshGeomFacet& rFacet) const; /** The aspect ratio is the longest edge length divided by its height. */ float AspectRatio() const; /** The alternative aspect ration is the ratio of the radius of the circum-circle and twice the * radius of the in-circle. */ float AspectRatio2() const; /** The roundness is in the range between 0.0 (colinear) and 1.0 (equilateral). */ float Roundness() const; /** Apply a transformation on the triangle. */ void Transform(const Base::Matrix4D&); /** * Checks if the two triangles are coplanar. */ bool IsCoplanar(const MeshGeomFacet& facet) const; private: mutable Base::Vector3f _clNormal; /**< Normal of the facet. */ mutable bool _bNormalCalculated; /**< True if the normal is already calculated. */ public: // NOLINTBEGIN Base::Vector3f _aclPoints[3]; /**< Geometric corner points. */ unsigned char _ucFlag; /**< Flag property */ unsigned long _ulProp; /**< Free usable property. */ // NOLINTEND }; using TMeshPointArray = std::vector; /** * Stores all data points of the mesh structure. */ class MeshExport MeshPointArray: public TMeshPointArray { public: // Iterator interface using _TIterator = std::vector::iterator; using _TConstIterator = std::vector::const_iterator; /** @name Construction */ //@{ // constructor MeshPointArray() = default; // constructor explicit MeshPointArray(PointIndex ulSize) : TMeshPointArray(ulSize) {} /// copy-constructor MeshPointArray(const MeshPointArray&); MeshPointArray(MeshPointArray&&); // Destructor ~MeshPointArray() = default; //@} /** @name Flag state * @note All flag methods are const as they do NOT change the actual properties of the object */ //@{ /// Sets the flag for all points void SetFlag(MeshPoint::TFlagType tF) const; /// Resets the flag for all points void ResetFlag(MeshPoint::TFlagType tF) const; /// Sets all points invalid void ResetInvalid() const; /// Sets the property for all points void SetProperty(unsigned long ulVal) const; //@} // Assignment MeshPointArray& operator=(const MeshPointArray& rclPAry); MeshPointArray& operator=(MeshPointArray&& rclPAry); void Transform(const Base::Matrix4D&); /** * Searches for the first point index Two points are equal if the distance is less * than EPSILON. If no such points is found POINT_INDEX_MAX is returned. */ PointIndex Get(const MeshPoint& rclPoint); /** * Searches for the first point index Two points are equal if the distance is less * than EPSILON. If no such points is found the point is added to the array at end * and its index is returned. */ PointIndex GetOrAddIndex(const MeshPoint& rclPoint); }; using TMeshFacetArray = std::vector; /** * Stores all facets of the mesh data-structure. */ class MeshExport MeshFacetArray: public TMeshFacetArray { public: // Iterator interface using _TIterator = std::vector::iterator; using _TConstIterator = std::vector::const_iterator; /** @name Construction */ //@{ /// constructor MeshFacetArray() = default; /// constructor explicit MeshFacetArray(FacetIndex ulSize) : TMeshFacetArray(ulSize) {} /// copy-constructor MeshFacetArray(const MeshFacetArray&); MeshFacetArray(MeshFacetArray&&); /// destructor ~MeshFacetArray() = default; //@} /** @name Flag state * @note All flag methods are const as they do NOT change the actual properties * of the object */ //@{ /// Sets the flag for all facets. void SetFlag(MeshFacet::TFlagType tF) const; /// Resets the flag for all facets. void ResetFlag(MeshFacet::TFlagType tF) const; /// Sets all facets invalid void ResetInvalid() const; /// Sets the property for all facets void SetProperty(unsigned long ulVal) const; //@} // Assignment MeshFacetArray& operator=(const MeshFacetArray& rclFAry); MeshFacetArray& operator=(MeshFacetArray&& rclFAry); /** * Removes the facet from the array the iterator points to. All neighbour * indices of the other facets get adjusted. */ void Erase(_TIterator pIter); /** * Checks and flips the point indices if needed. @see MeshFacet::Transpose(). */ void TransposeIndices(PointIndex ulOrig, PointIndex ulNew); /** * Decrements all point indices that are higher than \a ulIndex. */ void DecrementIndices(PointIndex ulIndex); }; /** * MeshPointModifier is a helper class that allows one to modify the * point array of a mesh kernel but with limited access. */ class MeshExport MeshPointModifier { public: explicit MeshPointModifier(MeshPointArray& points) : rPoints(points) {} ~MeshPointModifier() = default; MeshPointArray& GetPoints() const { return rPoints; } MeshPointModifier(const MeshPointModifier& c) = default; MeshPointModifier(MeshPointModifier&& c) = default; MeshPointModifier& operator=(const MeshPointModifier& c) = delete; MeshPointModifier& operator=(MeshPointModifier&& c) = delete; private: MeshPointArray& rPoints; }; /** * MeshFacetModifier is a helper class that allows one to modify the * facet array of a mesh kernel but with limited access. */ class MeshExport MeshFacetModifier { public: explicit MeshFacetModifier(MeshFacetArray& facets) : rFacets(facets) {} ~MeshFacetModifier() = default; MeshFacetModifier(const MeshFacetModifier& c) = default; MeshFacetModifier(MeshFacetModifier&& c) = default; MeshFacetModifier& operator=(const MeshFacetModifier& c) = delete; MeshFacetModifier& operator=(MeshFacetModifier&& c) = delete; /** * Replaces the index of the corner point of the facet at position \a pos * that is equal to \a old by \a now. If the facet does not have a corner * point with this index nothing happens. */ void Transpose(PointIndex pos, PointIndex old, PointIndex now) { rFacets[pos].Transpose(old, now); } private: MeshFacetArray& rFacets; }; inline MeshPoint::MeshPoint(float x, float y, float z) : Base::Vector3f(x, y, z) , _ucFlag(0) , _ulProp(0) {} inline MeshPoint::MeshPoint(const Base::Vector3f& rclPt) : Base::Vector3f(rclPt) , _ucFlag(0) , _ulProp(0) {} inline bool MeshPoint::operator==(const MeshPoint& rclPt) const { return Base::DistanceP2(*this, rclPt) < MeshDefinitions::_fMinPointDistanceP2; } inline bool MeshPoint::operator==(const Base::Vector3f& rclV) const { return Base::DistanceP2(*this, rclV) < MeshDefinitions::_fMinPointDistanceP2; } inline bool MeshPoint::operator<(const MeshPoint& rclPt) const { if (std::fabs(this->x - rclPt.x) >= MeshDefinitions::_fMinPointDistanceD1) { return this->x < rclPt.x; } if (std::fabs(this->y - rclPt.y) >= MeshDefinitions::_fMinPointDistanceD1) { return this->y < rclPt.y; } if (std::fabs(this->z - rclPt.z) >= MeshDefinitions::_fMinPointDistanceD1) { return this->z < rclPt.z; } return false; // points are considered to be equal } inline float MeshGeomFacet::DistancePlaneToPoint(const Base::Vector3f& rclPoint) const { // internal normal is forced to have length equal to 1 return float(fabs(rclPoint.DistanceToPlane(_aclPoints[0], GetNormal()))); } inline void MeshGeomFacet::CalcNormal() const { _clNormal = (_aclPoints[1] - _aclPoints[0]) % (_aclPoints[2] - _aclPoints[0]); _clNormal.Normalize(); _bNormalCalculated = true; } inline Base::Vector3f MeshGeomFacet::GetNormal() const { if (!_bNormalCalculated) { CalcNormal(); } return _clNormal; } inline void MeshGeomFacet::SetNormal(const Base::Vector3f& rclNormal) { if (rclNormal.Sqr() == 0.0F) { return; } _clNormal = rclNormal; _clNormal.Normalize(); _bNormalCalculated = true; } inline void MeshGeomFacet::ArrangeNormal(const Base::Vector3f& rclN) { // force internal normal to be computed if not done yet if ((rclN * GetNormal()) < 0.0F) { _clNormal = -_clNormal; } } inline Base::Vector3f MeshGeomFacet::GetGravityPoint() const { return (1.0F / 3.0F) * (_aclPoints[0] + _aclPoints[1] + _aclPoints[2]); } inline void MeshGeomFacet::AdjustCirculationDirection() { Base::Vector3f clN = (_aclPoints[1] - _aclPoints[0]) % (_aclPoints[2] - _aclPoints[0]); if ((clN * _clNormal) < 0.0F) { std::swap(_aclPoints[1], _aclPoints[2]); } } inline Base::BoundBox3f MeshGeomFacet::GetBoundBox() const { return {_aclPoints, 3}; } inline float MeshGeomFacet::Perimeter() const { float perimeter = 0.0F; perimeter += Base::Distance(_aclPoints[0], _aclPoints[1]); perimeter += Base::Distance(_aclPoints[1], _aclPoints[2]); perimeter += Base::Distance(_aclPoints[2], _aclPoints[0]); return perimeter; } inline float MeshGeomFacet::Area() const { return ((_aclPoints[1] - _aclPoints[0]) % (_aclPoints[2] - _aclPoints[0])).Length() / 2.0F; } inline bool MeshGeomFacet::ContainedByOrIntersectBoundingBox(const Base::BoundBox3f& rclBB) const { // Test, if all corner points of the facet are on one of the 6 sides of the BB if (!(GetBoundBox() && rclBB)) { return false; } // Test, whether Facet-BB is completely within BB if (rclBB.IsInBox(GetBoundBox())) { return true; } // Test, whether one of the corner points is in BB for (auto pnt : _aclPoints) { if (rclBB.IsInBox(pnt)) { return true; } } // "real" test for cutting return (IntersectBoundingBox(rclBB)); } inline bool MeshGeomFacet::IntersectWithPlane( const Base::Vector3f& rclBase, const Base::Vector3f& rclNormal ) const { bool bD0 = (_aclPoints[0].DistanceToPlane(rclBase, rclNormal) > 0.0F); return !( (bD0 == (_aclPoints[1].DistanceToPlane(rclBase, rclNormal) > 0.0F)) && (bD0 == (_aclPoints[2].DistanceToPlane(rclBase, rclNormal) > 0.0F)) ); } inline MeshFacet::MeshFacet() // NOLINT : _ucFlag(0) , _ulProp(0) { memset(_aulNeighbours, 0xff, sizeof(FacetIndex) * 3); memset(_aulPoints, 0xff, sizeof(PointIndex) * 3); } inline MeshFacet::MeshFacet( PointIndex p1, PointIndex p2, PointIndex p3, FacetIndex n1, FacetIndex n2, FacetIndex n3 ) : _ucFlag(0) , _ulProp(0) , _aulPoints {p1, p2, p3} , _aulNeighbours {n1, n2, n3} {} void MeshFacet::SetVertices(PointIndex p1, PointIndex p2, PointIndex p3) { _aulPoints[0] = p1; _aulPoints[1] = p2; _aulPoints[2] = p3; } void MeshFacet::SetNeighbours(FacetIndex n1, FacetIndex n2, FacetIndex n3) { _aulNeighbours[0] = n1; _aulNeighbours[1] = n2; _aulNeighbours[2] = n3; } inline void MeshFacet::GetEdge(unsigned short usSide, MeshHelpEdge& rclEdge) const { rclEdge._ulIndex[0] = _aulPoints[usSide]; rclEdge._ulIndex[1] = _aulPoints[(usSide + 1) % 3]; } inline std::pair MeshFacet::GetEdge(unsigned short usSide) const { return {_aulPoints[usSide], _aulPoints[(usSide + 1) % 3]}; } inline void MeshFacet::Transpose(PointIndex ulOrig, PointIndex ulNew) { if (_aulPoints[0] == ulOrig) { _aulPoints[0] = ulNew; } else if (_aulPoints[1] == ulOrig) { _aulPoints[1] = ulNew; } else if (_aulPoints[2] == ulOrig) { _aulPoints[2] = ulNew; } } inline void MeshFacet::Decrement(PointIndex ulIndex) { if (_aulPoints[0] > ulIndex) { _aulPoints[0]--; } if (_aulPoints[1] > ulIndex) { _aulPoints[1]--; } if (_aulPoints[2] > ulIndex) { _aulPoints[2]--; } } inline bool MeshFacet::HasPoint(PointIndex ulIndex) const { if (_aulPoints[0] == ulIndex) { return true; } if (_aulPoints[1] == ulIndex) { return true; } if (_aulPoints[2] == ulIndex) { return true; } return false; } inline void MeshFacet::ReplaceNeighbour(FacetIndex ulOrig, FacetIndex ulNew) { if (_aulNeighbours[0] == ulOrig) { _aulNeighbours[0] = ulNew; } else if (_aulNeighbours[1] == ulOrig) { _aulNeighbours[1] = ulNew; } else if (_aulNeighbours[2] == ulOrig) { _aulNeighbours[2] = ulNew; } } inline unsigned short MeshFacet::CountOpenEdges() const { unsigned short ct = 0; for (unsigned short i = 0; i < 3; i++) { if (!HasNeighbour(i)) { ct++; } } return ct; } inline bool MeshFacet::HasOpenEdge() const { return (CountOpenEdges() != 0); } inline bool MeshFacet::HasSameOrientation(const MeshFacet& f) const { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (_aulPoints[i] == f._aulPoints[j]) { if ((_aulPoints[(i + 1) % 3] == f._aulPoints[(j + 1) % 3]) || (_aulPoints[(i + 2) % 3] == f._aulPoints[(j + 2) % 3])) { return false; } } } } return true; } inline bool MeshFacet::IsDegenerated() const { if (_aulPoints[0] == _aulPoints[1]) { return true; } if (_aulPoints[1] == _aulPoints[2]) { return true; } if (_aulPoints[2] == _aulPoints[0]) { return true; } return false; } inline unsigned short MeshFacet::Side(FacetIndex ulNIndex) const { if (_aulNeighbours[0] == ulNIndex) { return 0; } if (_aulNeighbours[1] == ulNIndex) { return 1; } if (_aulNeighbours[2] == ulNIndex) { return 2; } return std::numeric_limits::max(); } inline unsigned short MeshFacet::Side(PointIndex ulP0, PointIndex ulP1) const { if (_aulPoints[0] == ulP0) { if (_aulPoints[1] == ulP1) { return 0; // Edge 0-1 ==> 0 } if (_aulPoints[2] == ulP1) { return 2; // Edge 0-2 ==> 2 } } else if (_aulPoints[1] == ulP0) { if (_aulPoints[0] == ulP1) { return 0; // Edge 1-0 ==> 0 } if (_aulPoints[2] == ulP1) { return 1; // Edge 1-2 ==> 1 } } else if (_aulPoints[2] == ulP0) { if (_aulPoints[0] == ulP1) { return 2; // Edge 2-0 ==> 2 } if (_aulPoints[1] == ulP1) { return 1; // Edge 2-1 ==> 1 } } return std::numeric_limits::max(); } inline unsigned short MeshFacet::Side(const MeshFacet& rFace) const { unsigned short side {}; for (int i = 0; i < 3; i++) { side = Side(rFace._aulPoints[i], rFace._aulPoints[(i + 1) % 3]); if (side != std::numeric_limits::max()) { return side; } } return std::numeric_limits::max(); } inline bool MeshFacet::IsEqual(const MeshFacet& rcFace) const { for (int i = 0; i < 3; i++) { if (this->_aulPoints[0] == rcFace._aulPoints[i]) { if (this->_aulPoints[1] == rcFace._aulPoints[(i + 1) % 3] && this->_aulPoints[2] == rcFace._aulPoints[(i + 2) % 3]) { return true; } if (this->_aulPoints[1] == rcFace._aulPoints[(i + 2) % 3] && this->_aulPoints[2] == rcFace._aulPoints[(i + 1) % 3]) { return true; } } } return false; } /** * Binary function to query the flags for use with generic STL functions. */ template class MeshIsFlag { public: using first_argument_type = TCLASS; using second_argument_type = typename TCLASS::TFlagType; using result_type = bool; bool operator()(const TCLASS& rclElem, typename TCLASS::TFlagType tFlag) const { return rclElem.IsFlag(tFlag); } }; /** * Binary function to query the flags for use with generic STL functions. */ template class MeshIsNotFlag { public: using first_argument_type = TCLASS; using second_argument_type = typename TCLASS::TFlagType; using result_type = bool; bool operator()(const TCLASS& rclElem, typename TCLASS::TFlagType tFlag) const { return !rclElem.IsFlag(tFlag); } }; /** * Binary function to set the flags for use with generic STL functions. */ template class MeshSetFlag { public: using first_argument_type = TCLASS; using second_argument_type = typename TCLASS::TFlagType; using result_type = bool; bool operator()(const TCLASS& rclElem, typename TCLASS::TFlagType tFlag) const { rclElem.SetFlag(tFlag); return true; } }; /** * Binary function to reset the flags for use with generic STL functions. */ template class MeshResetFlag { public: using first_argument_type = TCLASS; using second_argument_type = typename TCLASS::TFlagType; using result_type = bool; bool operator()(const TCLASS& rclElem, typename TCLASS::TFlagType tFlag) const { rclElem.ResetFlag(tFlag); return true; } }; } // namespace MeshCore #endif // MESH_ELEMENTS_H