Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:04:38

0001 // Copyright (c) 2015-2021 OPEN CASCADE SAS
0002 //
0003 // This file is part of Open CASCADE Technology software library.
0004 //
0005 // This library is free software; you can redistribute it and/or modify it under
0006 // the terms of the GNU Lesser General Public License version 2.1 as published
0007 // by the Free Software Foundation, with special exception defined in the file
0008 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
0009 // distribution for complete text of the license and disclaimer of any warranty.
0010 //
0011 // Alternatively, this file may be used under the terms of Open CASCADE
0012 // commercial license or contractual agreement.
0013 
0014 #ifndef _Poly_MergeNodesTool_HeaderFile
0015 #define _Poly_MergeNodesTool_HeaderFile
0016 
0017 #include <NCollection_Map.hxx>
0018 #include <Poly_Triangulation.hxx>
0019 #include <Standard_HashUtils.hxx>
0020 
0021 //! Auxiliary tool for merging triangulation nodes for visualization purposes.
0022 //! Tool tries to merge all nodes within input triangulation, but split the ones on sharp corners at specified angle.
0023 class Poly_MergeNodesTool : public Standard_Transient
0024 {
0025   DEFINE_STANDARD_RTTIEXT(Poly_MergeNodesTool, Standard_Transient)
0026 public:
0027 
0028   //! Merge nodes of existing mesh and return the new mesh.
0029   //! @param[in] theTris triangulation to add
0030   //! @param[in] theTrsf transformation to apply
0031   //! @param[in] theToReverse reverse triangle nodes order
0032   //! @param[in] theSmoothAngle merge angle in radians
0033   //! @param[in] theMergeTolerance linear merge tolerance
0034   //! @param[in] theToForce return merged triangulation even if it's statistics is equal to input one
0035   //! @return merged triangulation or NULL on no result
0036   Standard_EXPORT static Handle(Poly_Triangulation) MergeNodes (const Handle(Poly_Triangulation)& theTris,
0037                                                                 const gp_Trsf& theTrsf,
0038                                                                 const Standard_Boolean theToReverse,
0039                                                                 const double theSmoothAngle,
0040                                                                 const double theMergeTolerance = 0.0,
0041                                                                 const bool   theToForce = true);
0042 
0043 public:
0044 
0045   //! Constructor
0046   //! @param[in] theSmoothAngle smooth angle in radians or 0.0 to disable merging by angle
0047   //! @param[in] theMergeTolerance node merging maximum distance
0048   //! @param[in] theNbFacets estimated number of facets for map preallocation
0049   Standard_EXPORT Poly_MergeNodesTool (const double theSmoothAngle,
0050                                        const double theMergeTolerance = 0.0,
0051                                        const int    theNbFacets = -1);
0052 
0053   //! Return merge tolerance; 0.0 by default (only 3D points with exactly matching coordinates are merged).
0054   double MergeTolerance() const { return myNodeIndexMap.MergeTolerance(); }
0055 
0056   //! Set merge tolerance.
0057   void SetMergeTolerance (double theTolerance) { myNodeIndexMap.SetMergeTolerance (theTolerance); }
0058 
0059   //! Return merge angle in radians; 0.0 by default (normals with non-exact directions are not merged).
0060   double MergeAngle() const { return myNodeIndexMap.MergeAngle(); }
0061 
0062   //! Set merge angle.
0063   void SetMergeAngle (double theAngleRad) { myNodeIndexMap.SetMergeAngle (theAngleRad); }
0064 
0065   //! Return TRUE if nodes with opposite normals should be merged; FALSE by default.
0066   bool ToMergeOpposite() const { return myNodeIndexMap.ToMergeOpposite(); }
0067 
0068   //! Set if nodes with opposite normals should be merged.
0069   void SetMergeOpposite (bool theToMerge) { myNodeIndexMap.SetMergeOpposite (theToMerge); }
0070 
0071   //! Setup unit factor.
0072   void SetUnitFactor (double theUnitFactor) { myUnitFactor = theUnitFactor; }
0073 
0074   //! Return TRUE if degenerate elements should be discarded; TRUE by default.
0075   bool ToDropDegenerative() const { return myToDropDegenerative; }
0076 
0077   //! Set if degenerate elements should be discarded.
0078   void SetDropDegenerative (bool theToDrop) { myToDropDegenerative = theToDrop; }
0079 
0080   //! Return TRUE if equal elements should be filtered; FALSE by default.
0081   bool ToMergeElems() const { return myToMergeElems; }
0082 
0083   //! Set if equal elements should be filtered.
0084   void SetMergeElems (bool theToMerge) { myToMergeElems = theToMerge; }
0085 
0086   //! Compute normal for the mesh element.
0087   NCollection_Vec3<float> computeTriNormal() const
0088   {
0089     const gp_XYZ aVec01 = myPlaces[1] - myPlaces[0];
0090     const gp_XYZ aVec02 = myPlaces[2] - myPlaces[0];
0091     const gp_XYZ aCross = aVec01 ^ aVec02;
0092     NCollection_Vec3<float> aNorm ((float )aCross.X(), (float )aCross.Y(), (float )aCross.Z());
0093     return aNorm.Normalized();
0094   }
0095 
0096 public:
0097 
0098   //! Add another triangulation to created one.
0099   //! @param[in] theTris triangulation to add
0100   //! @param[in] theTrsf transformation to apply
0101   //! @param[in] theToReverse reverse triangle nodes order
0102   Standard_EXPORT virtual void AddTriangulation (const Handle(Poly_Triangulation)& theTris,
0103                                                  const gp_Trsf& theTrsf = gp_Trsf(),
0104                                                  const Standard_Boolean theToReverse = false);
0105 
0106   //! Prepare and return result triangulation (temporary data will be truncated to result size).
0107   Standard_EXPORT Handle(Poly_Triangulation) Result();
0108 
0109 public:
0110 
0111   //! Add new triangle.
0112   //! @param[in] theElemNodes 3 element nodes
0113   void AddTriangle (const gp_XYZ theElemNodes[3])
0114   {
0115     AddElement (theElemNodes, 3);
0116   }
0117 
0118   //! Add new quad.
0119   //! @param[in] theElemNodes 4 element nodes
0120   void AddQuad (const gp_XYZ theElemNodes[4])
0121   {
0122     AddElement (theElemNodes, 4);
0123   }
0124 
0125   //! Add new triangle or quad.
0126   //! @param[in] theElemNodes element nodes
0127   //! @param[in] theNbNodes number of element nodes, should be 3 or 4
0128   Standard_EXPORT void AddElement (const gp_XYZ* theElemNodes,
0129                                    int theNbNodes);
0130 
0131   //! Change node coordinates of element to be pushed.
0132   //! @param[in] theIndex node index within current element, in 0..3 range
0133   gp_XYZ& ChangeElementNode (int theIndex) { return myPlaces[theIndex]; }
0134 
0135   //! Add new triangle or quad with nodes specified by ChangeElementNode().
0136   Standard_EXPORT void PushLastElement (int theNbNodes);
0137 
0138   //! Add new triangle with nodes specified by ChangeElementNode().
0139   void PushLastTriangle() { PushLastElement (3); }
0140 
0141   //! Add new quad with nodes specified by ChangeElementNode().
0142   void PushLastQuad() { PushLastElement (4); }
0143 
0144   //! Return current element node index defined by PushLastElement().
0145   Standard_Integer ElementNodeIndex (int theIndex) const { return myNodeInds[theIndex]; }
0146 
0147   //! Return number of nodes.
0148   int NbNodes() const { return myNbNodes; }
0149 
0150   //! Return number of elements.
0151   int NbElements() const { return myNbElems; }
0152 
0153   //! Return number of discarded degenerate elements.
0154   int NbDegenerativeElems() const { return myNbDegenElems; }
0155 
0156   //! Return number of merged equal elements.
0157   int NbMergedElems() const { return myNbMergedElems; }
0158 
0159   //! Setup output triangulation for modifications.
0160   //! When set to NULL, the tool could be used as a merge map for filling in external mesh structure.
0161   Handle(Poly_Triangulation)& ChangeOutput() { return myPolyData; }
0162 
0163 private:
0164 
0165   //! Push triangle node with normal angle comparison.
0166   void pushNodeCheck (bool& theIsOpposite,
0167                       const int theTriNode)
0168   {
0169     int aNodeIndex = myNbNodes;
0170     const gp_XYZ& aPlace = myPlaces[theTriNode];
0171     const NCollection_Vec3<float> aVec3 ((float )aPlace.X(), (float )aPlace.Y(), (float )aPlace.Z());
0172     if (myNodeIndexMap.Bind (aNodeIndex, theIsOpposite, aVec3, myTriNormal))
0173     {
0174       ++myNbNodes;
0175       if (!myPolyData.IsNull())
0176       {
0177         if (myPolyData->NbNodes() < myNbNodes)
0178         {
0179           myPolyData->ResizeNodes (myNbNodes * 2, true);
0180         }
0181         myPolyData->SetNode (myNbNodes, aPlace * myUnitFactor);
0182       }
0183     }
0184     myNodeInds[theTriNode] = aNodeIndex;
0185   }
0186 
0187   //! Push triangle node without merging vertices.
0188   inline void pushNodeNoMerge (const int theTriNode)
0189   {
0190     int aNodeIndex = myNbNodes;
0191     const gp_XYZ aPlace = myPlaces[theTriNode] * myUnitFactor;
0192 
0193     ++myNbNodes;
0194     if (!myPolyData.IsNull())
0195     {
0196       if (myPolyData->NbNodes() < myNbNodes)
0197       {
0198         myPolyData->ResizeNodes (myNbNodes * 2, true);
0199       }
0200       myPolyData->SetNode (myNbNodes, aPlace);
0201     }
0202 
0203     myNodeInds[theTriNode] = aNodeIndex;
0204   }
0205 
0206 private:
0207 
0208   //! Pair holding Vec3 and Normal to the triangle
0209   struct Vec3AndNormal
0210   {
0211     NCollection_Vec3<float> Pos;  //!< position
0212     NCollection_Vec3<float> Norm; //!< normal to the element
0213 
0214     Vec3AndNormal (const NCollection_Vec3<float>& thePos,
0215                    const NCollection_Vec3<float>& theNorm)
0216     : Pos (thePos), Norm (theNorm) {}
0217   };
0218 
0219   //! Custom map class with key as Node + element normal and value as Node index.
0220   //! NCollection_DataMap is not used, as it requires Hasher to be defined as class template and not class field.
0221   class MergedNodesMap : public NCollection_BaseMap
0222   {
0223   public:
0224     typedef NCollection_Vec3<int64_t> CellVec3i;
0225   public:
0226     //! Main constructor.
0227     Standard_EXPORT MergedNodesMap (const int theNbBuckets);
0228 
0229     //! Return merge angle in radians;
0230     double MergeAngle() const { return myAngle; }
0231 
0232     //! Set merge angle.
0233     void SetMergeAngle (double theAngleRad)
0234     {
0235       myAngle    = (float )theAngleRad;
0236       myAngleCos = (float )Cos (theAngleRad);
0237     }
0238 
0239     //! Return TRUE if merge angle is non-zero.
0240     //! 0 angle means angles should much without a tolerance.
0241     bool HasMergeAngle() const { return myAngle > 0.0f; }
0242 
0243     //! Return TRUE if merge angle comparison can be skipped (angle is close to 90 degrees).
0244     bool ToMergeAnyAngle() const { return myAngleCos <= 0.01f; }
0245 
0246     //! Return TRUE if nodes with opposite normals should be merged; FALSE by default.
0247     bool ToMergeOpposite() const { return myToMergeOpposite; }
0248 
0249     //! Set if nodes with opposite normals should be merged.
0250     void SetMergeOpposite (bool theToMerge) { myToMergeOpposite = theToMerge; }
0251 
0252     //! Return merge tolerance.
0253     double MergeTolerance() const { return myTolerance; }
0254 
0255     //! Set merge tolerance.
0256     Standard_EXPORT void SetMergeTolerance (double theTolerance);
0257 
0258     //! Return TRUE if merge tolerance is non-zero.
0259     bool HasMergeTolerance() const { return myTolerance > 0.0f; }
0260 
0261     //! Bind node to the map or find existing one.
0262     //! @param theIndex [in,out] index of new key to add, or index of existing key, if already bound
0263     //! @param theIsOpposite [out] flag indicating that existing (already bound) node has opposite direction
0264     //! @param thePos   [in] node position to add or find
0265     //! @param theNorm  [in] element normal for equality check
0266     //! @return TRUE if node was not bound already
0267     Standard_EXPORT bool Bind (int&  theIndex,
0268                                bool& theIsOpposite,
0269                                const NCollection_Vec3<float>& thePos,
0270                                const NCollection_Vec3<float>& theNorm);
0271 
0272     //! ReSize the map.
0273     Standard_EXPORT void ReSize (const int theSize);
0274 
0275   private:
0276 
0277     //! Return cell index for specified 3D point and inverted cell size.
0278     CellVec3i vec3ToCell (const NCollection_Vec3<float>& thePnt) const
0279     {
0280       return CellVec3i (thePnt * myInvTol);
0281     }
0282 
0283     //! Hash code for integer vec3.
0284     Standard_EXPORT static size_t vec3iHashCode (const Poly_MergeNodesTool::MergedNodesMap::CellVec3i& theVec,
0285                                                  const int theUpper);
0286 
0287     //! Compute hash code.
0288     Standard_EXPORT size_t hashCode (const NCollection_Vec3<float>& thePos,
0289                                      const NCollection_Vec3<float>& theNorm,
0290                                      const int theUpper) const;
0291 
0292     //! Compute hash code.
0293     size_t hashCode (const Vec3AndNormal& theKey, const int theUpper) const
0294     {
0295       return hashCode (theKey.Pos, theKey.Norm, theUpper);
0296     }
0297 
0298     //! Compare two vectors with inversed tolerance.
0299     Standard_EXPORT bool vec3AreEqual (const NCollection_Vec3<float>& theKey1,
0300                                        const NCollection_Vec3<float>& theKey2) const;
0301 
0302     //! Compare two nodes.
0303     Standard_EXPORT bool isEqual (const Vec3AndNormal& theKey1,
0304                                   const NCollection_Vec3<float>& thePos2,
0305                                   const NCollection_Vec3<float>& theNorm2,
0306                                   bool& theIsOpposite) const;
0307   private:
0308     //! Map node.
0309     class DataMapNode;
0310   private:
0311     float myTolerance;       //!< linear tolerance for comparison
0312     float myInvTol;          //!< inversed linear tolerance for comparison
0313     float myAngle;           //!< angle for comparison
0314     float myAngleCos;        //!< angle cosine for comparison
0315     bool  myToMergeOpposite; //!< merge nodes with opposite normals
0316   };
0317 
0318   //! Hasher for merging equal elements (with pre-sorted indexes).
0319   struct MergedElemHasher
0320   {
0321     size_t operator()(const NCollection_Vec4<int>& theVec) const
0322     {
0323       return opencascade::hashBytes(&theVec[0], 4 * sizeof(int));
0324     }
0325 
0326     bool operator()(const NCollection_Vec4<int>& theKey1, const NCollection_Vec4<int>& theKey2) const
0327     {
0328       return theKey1.IsEqual (theKey2);
0329     }
0330   };
0331 
0332 private:
0333 
0334   Handle(Poly_Triangulation) myPolyData;           //!< output triangulation
0335   MergedNodesMap             myNodeIndexMap;       //!< map of merged nodes
0336   NCollection_Map<NCollection_Vec4<int>, MergedElemHasher>
0337                              myElemMap;            //!< map of elements
0338   NCollection_Vec4<int>      myNodeInds;           //!< current element indexes
0339   NCollection_Vec3<float>    myTriNormal;          //!< current triangle normal
0340   gp_XYZ                     myPlaces[4];          //!< current triangle/quad coordinates to push
0341 
0342   Standard_Real              myUnitFactor;         //!< scale factor to apply
0343   Standard_Integer           myNbNodes;            //!< number of output nodes
0344   Standard_Integer           myNbElems;            //!< number of output elements
0345   Standard_Integer           myNbDegenElems;       //!< number of degenerated elements
0346   Standard_Integer           myNbMergedElems;      //!< number of merged elements
0347   Standard_Boolean           myToDropDegenerative; //!< flag to filter our degenerate elements
0348   Standard_Boolean           myToMergeElems;       //!< flag to merge elements
0349 
0350 };
0351 
0352 #endif // _Poly_MergeNodesTool_HeaderFile