1
0
mirror of https://git.dev.opencascade.org/repos/occt.git synced 2025-06-20 11:54:07 +03:00
occt/src/Poly/Poly_MergeNodesTool.cxx
dpasukhi 1103eb60af 0033370: Foundation Classes - Moving into STL and Boost functionality
NCollection containers update:
  - NCollection_Array1 - updated functionality
  - NCollection_Array2 - NCollection_Array1 as a wrapper for 2array
  - NCollection_Vector -> NCollection_DynamicArray was renamed and reworked.
TCollection:
  - Use static empty string to avoid allocations on empty string
 NCollection allocators update:
  - NCollection_Allocator - allocator that used Standard::Allocate
  - NCollection_OccAllocator - allocator-wrapper that used OCC BaseAllocator objects
  - NCollection_IncAllocator - rework to increase performance
Standard:
  - Rework functionality to use different allocation libs
  - Implement basic of new way to wrap allocations tools
  - Define 4 ways to allocation (defines in configure stage)
 Additional changes:
  - Hash function uses std::hash functionality
   - size_t as a hash value
  - New HashUtils with Murmur and FVN hash algo for x32 and x64
  - Deprecated _0.cxx and .gxx DE classes reorganized
  - Create own utility for std memory
  - Update Standard_Transient to be more platform-independent
 Math TK changes:
  - math_Vector -> match_BaseVector<>
    - Buffer decreased to cash 32 elements instead of 512
2023-12-04 13:37:09 +00:00

496 lines
16 KiB
C++

// Copyright (c) 2015-2021 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
#include <Poly_MergeNodesTool.hxx>
#include <NCollection_IncAllocator.hxx>
#include <Standard_CStringHasher.hxx>
#include <algorithm>
namespace
{
//! Returns initial number of buckets for the map.
static int initialNbBuckets (int theNbFacets)
{
return theNbFacets > 0
? theNbFacets * 2 // consider ratio 1:2 (NbTriangles:MergedNodes) as expected
: 995329; // default initial value for mesh of unknown size
}
}
IMPLEMENT_STANDARD_RTTIEXT(Poly_MergeNodesTool, Standard_Transient)
//! Map node.
class Poly_MergeNodesTool::MergedNodesMap::DataMapNode : public NCollection_TListNode<int>
{
public:
//! Constructor.
DataMapNode (const NCollection_Vec3<float>& thePos,
const NCollection_Vec3<float>& theNorm,
int theItem, NCollection_ListNode* theNext)
: NCollection_TListNode<int> (theItem, theNext), myKey (thePos, theNorm) {}
//! Key.
const Poly_MergeNodesTool::Vec3AndNormal& Key() const { return myKey; }
//! Static deleter to be passed to BaseMap
static void delNode (NCollection_ListNode* theNode, Handle(NCollection_BaseAllocator)& theAl)
{
((DataMapNode* )theNode)->~DataMapNode();
theAl->Free (theNode);
}
private:
Poly_MergeNodesTool::Vec3AndNormal myKey;
};
// =======================================================================
// function : MergedNodesMap
// purpose :
// =======================================================================
Poly_MergeNodesTool::MergedNodesMap::MergedNodesMap (const int theNbBuckets)
: NCollection_BaseMap (theNbBuckets, true, new NCollection_IncAllocator()),
myTolerance(0.0f),
myInvTol (0.0f),
myAngle (1.0f),
myAngleCos (0.0f),
myToMergeOpposite (false)
{
//
}
// =======================================================================
// function : MergedNodesMap::SetMergeTolerance
// purpose :
// =======================================================================
void Poly_MergeNodesTool::MergedNodesMap::SetMergeTolerance (double theTolerance)
{
myTolerance = (float )theTolerance;
myInvTol = 0.0f;
if (myTolerance > 0.0f)
{
myInvTol = float(1.0 / theTolerance);
}
}
// =======================================================================
// function : MergedNodesMap::hashCode
// purpose :
// =======================================================================
inline size_t Poly_MergeNodesTool::MergedNodesMap::vec3iHashCode (const Poly_MergeNodesTool::MergedNodesMap::CellVec3i& theVec,
const int theUpper)
{
// copied from NCollection_CellFilter
constexpr uint64_t aShiftBits = (CHAR_BIT * sizeof(int64_t)-1) / 3;
uint64_t aHashCode = 0;
aHashCode = (aHashCode << aShiftBits) ^ theVec[0];
aHashCode = (aHashCode << aShiftBits) ^ theVec[1];
aHashCode = (aHashCode << aShiftBits) ^ theVec[2];
return aHashCode % theUpper + 1;
}
// =======================================================================
// function : MergedNodesMap::hashCode
// purpose :
// =======================================================================
inline size_t Poly_MergeNodesTool::MergedNodesMap::hashCode (const NCollection_Vec3<float>& thePos,
const NCollection_Vec3<float>& theNorm,
const int theUpper) const
{
(void )theNorm;
if (myInvTol <= 0.0f)
{
// compute DJB2 hash of a string
const size_t aLength = sizeof(NCollection_Vec3<float>);
unsigned int aHashCode = 0;
const Standard_Character* c = (Standard_CString )&thePos;
for (size_t i = 0; i < aLength; ++i, ++c)
{
aHashCode = ((aHashCode << 5) + aHashCode) ^ (*c);
}
return aHashCode % theUpper + 1;
}
const CellVec3i anIndex = vec3ToCell (thePos);
return vec3iHashCode (anIndex, theUpper);
}
// =======================================================================
// function : MergedNodesMap::vec3AreEqual
// purpose :
// =======================================================================
inline bool Poly_MergeNodesTool::MergedNodesMap::vec3AreEqual (const NCollection_Vec3<float>& theKey1,
const NCollection_Vec3<float>& theKey2) const
{
if (myInvTol <= 0.0f)
{
return theKey1.IsEqual (theKey2);
}
/// tolerance should be smaller than triangle size to avoid artifacts
//const CellVec3i anIndex1 = vec3ToCell (theKey1);
//const CellVec3i anIndex2 = vec3ToCell (theKey2);
//return anIndex1.IsEqual (anIndex2);
float aVal = theKey1.x() - theKey2.x();
if (aVal < 0) { aVal = -aVal; }
if (aVal > myTolerance) { return false; }
aVal = theKey1.y() - theKey2.y();
if (aVal < 0) { aVal = -aVal; }
if (aVal > myTolerance) { return false; }
aVal = theKey1.z() - theKey2.z();
if (aVal < 0) { aVal = -aVal; }
if (aVal > myTolerance) { return false; }
return true;
}
// =======================================================================
// function : MergedNodesMap::isEqual
// purpose :
// =======================================================================
inline bool Poly_MergeNodesTool::MergedNodesMap::isEqual (const Vec3AndNormal& theKey1,
const NCollection_Vec3<float>& thePos2,
const NCollection_Vec3<float>& theNorm2,
bool& theIsOpposite) const
{
if (!vec3AreEqual (theKey1.Pos, thePos2))
{
return false;
}
const float aCosinus = theKey1.Norm.Dot (theNorm2);
if (aCosinus >= myAngleCos)
{
//theIsOpposite = false;
return true;
}
else if (myToMergeOpposite
&& aCosinus <= -myAngleCos)
{
theIsOpposite = true;
return true;
}
return false;
}
// =======================================================================
// function : MergedNodesMap::Bind
// purpose :
// =======================================================================
inline bool Poly_MergeNodesTool::MergedNodesMap::Bind (int& theIndex,
bool& theIsOpposite,
const NCollection_Vec3<float>& thePos,
const NCollection_Vec3<float>& theNorm)
{
if (Resizable())
{
ReSize (Extent());
}
DataMapNode** aData = (DataMapNode** )myData1;
const size_t aHash = hashCode (thePos, theNorm, NbBuckets());
for (DataMapNode* aNodeIter = aData[aHash]; aNodeIter != NULL;
aNodeIter = (DataMapNode* )aNodeIter->Next())
{
if (isEqual (aNodeIter->Key(), thePos, theNorm, theIsOpposite))
{
theIndex = aNodeIter->ChangeValue();
return false;
}
}
if (myInvTol > 0.0f)
{
static const CellVec3i THE_NEIGHBRS[26] =
{
CellVec3i(-1, 0, 0),CellVec3i( 1, 0, 0),CellVec3i( 0,-1, 0),CellVec3i( 0, 1, 0),CellVec3i( 0, 0,-1),CellVec3i( 0, 0, 1),
CellVec3i(-1,-1, 0),CellVec3i( 1,-1, 0),CellVec3i( 1, 1, 0),CellVec3i(-1, 1, 0),
CellVec3i( 0,-1,-1),CellVec3i( 0, 1,-1),CellVec3i( 0, 1, 1),CellVec3i( 0,-1, 1),
CellVec3i(-1, 0,-1),CellVec3i( 1, 0,-1),CellVec3i( 1, 0, 1),CellVec3i(-1, 0, 1),
CellVec3i(-1,-1,-1),CellVec3i( 1,-1,-1),CellVec3i(-1, 1,-1),CellVec3i( 1, 1,-1),CellVec3i(-1,-1, 1),CellVec3i( 1,-1, 1),CellVec3i(-1, 1, 1),CellVec3i(1, 1, 1)
};
const CellVec3i anIndexCnt = vec3ToCell (thePos);
for (int aNeigIter = 0; aNeigIter < 26; ++aNeigIter)
{
const CellVec3i anIndex = anIndexCnt + THE_NEIGHBRS[aNeigIter];
const size_t aHashEx = vec3iHashCode (anIndex, NbBuckets());
for (DataMapNode* aNodeIter = aData[aHashEx]; aNodeIter != NULL;
aNodeIter = (DataMapNode* )aNodeIter->Next())
{
if (isEqual (aNodeIter->Key(), thePos, theNorm, theIsOpposite))
{
theIndex = aNodeIter->ChangeValue();
return false;
}
}
}
}
//theIsOpposite = false;
aData[aHash] = new (this->myAllocator) DataMapNode (thePos, theNorm, theIndex, aData[aHash]);
Increment();
return true;
}
// =======================================================================
// function : MergedNodesMap::ReSize
// purpose :
// =======================================================================
inline void Poly_MergeNodesTool::MergedNodesMap::ReSize (const int theSize)
{
NCollection_ListNode** aNewData = NULL;
NCollection_ListNode** aDummy = NULL;
int aNbNewBuck = 0;
if (BeginResize (theSize, aNbNewBuck, aNewData, aDummy))
{
if (DataMapNode** anOldData = (DataMapNode** )myData1)
{
for (int anOldBuckIter = 0; anOldBuckIter <= NbBuckets(); ++anOldBuckIter)
{
for (DataMapNode* anOldNodeIter = anOldData[anOldBuckIter]; anOldNodeIter != NULL; )
{
const size_t aNewHash = hashCode (anOldNodeIter->Key(), aNbNewBuck);
DataMapNode* aNextNode = (DataMapNode* )anOldNodeIter->Next();
anOldNodeIter->Next() = aNewData[aNewHash];
aNewData[aNewHash] = anOldNodeIter;
anOldNodeIter = aNextNode;
}
}
}
EndResize (theSize, aNbNewBuck, aNewData, aDummy);
}
}
// =======================================================================
// function : Poly_MergeNodesTool
// purpose :
// =======================================================================
Poly_MergeNodesTool::Poly_MergeNodesTool (const double theSmoothAngle,
const double theMergeTolerance,
const int theNbFacets)
: myPolyData (new Poly_Triangulation()),
myNodeIndexMap ((theSmoothAngle > 0.0
|| theMergeTolerance > 0.0)
? initialNbBuckets (theNbFacets)
: 1),
myNodeInds (0, 0, 0, -1),
myTriNormal (0.0f, 0.0f, 1.0f),
myUnitFactor (1.0),
myNbNodes (0),
myNbElems (0),
myNbDegenElems (0),
myNbMergedElems (0),
myToDropDegenerative (true),
myToMergeElems (false)
{
SetMergeAngle (theSmoothAngle);
SetMergeTolerance (theMergeTolerance);
}
// =======================================================================
// function : AddElement
// purpose :
// =======================================================================
void Poly_MergeNodesTool::AddElement (const gp_XYZ* theElemNodes,
int theNbNodes)
{
if (theNbNodes != 3
&& theNbNodes != 4)
{
throw Standard_ProgramError ("Poly_MergeNodesTool::AddElement() - Internal error");
}
myPlaces[0] = theElemNodes[0];
myPlaces[1] = theElemNodes[1];
myPlaces[2] = theElemNodes[2];
if (theNbNodes == 4)
{
myPlaces[3] = theElemNodes[3];
}
PushLastElement (theNbNodes);
}
// =======================================================================
// function : PushLastElement
// purpose :
// =======================================================================
void Poly_MergeNodesTool::PushLastElement (int theNbNodes)
{
if (theNbNodes != 3
&& theNbNodes != 4)
{
throw Standard_ProgramError ("Poly_MergeNodesTool::PushLastElement() - Internal error");
}
bool isOpposite = false;
myNodeInds[3] = -1;
if (myNodeIndexMap.HasMergeAngle()
|| myNodeIndexMap.HasMergeTolerance())
{
if (!myNodeIndexMap.ToMergeAnyAngle())
{
myTriNormal = computeTriNormal();
}
pushNodeCheck (isOpposite, 0);
pushNodeCheck (isOpposite, 1);
pushNodeCheck (isOpposite, 2);
if (theNbNodes == 4)
{
pushNodeCheck (isOpposite, 3);
}
}
else
{
pushNodeNoMerge (0);
pushNodeNoMerge (1);
pushNodeNoMerge (2);
if (theNbNodes == 4)
{
pushNodeNoMerge (3);
}
}
if (myToDropDegenerative)
{
// warning - removing degenerate elements may produce unused nodes
if (myNodeInds[0] == myNodeInds[1]
|| myNodeInds[0] == myNodeInds[2]
|| myNodeInds[1] == myNodeInds[2])
{
if (theNbNodes == 4)
{
//
}
else
{
++myNbDegenElems;
return;
}
}
}
if (myToMergeElems)
{
NCollection_Vec4<int> aSorted = myNodeInds;
std::sort (aSorted.ChangeData(), aSorted.ChangeData() + theNbNodes);
if (!myElemMap.Add (aSorted))
{
++myNbMergedElems;
return;
}
}
++myNbElems;
if (!myPolyData.IsNull())
{
if (myPolyData->NbTriangles() < myNbElems)
{
myPolyData->ResizeTriangles (myNbElems * 2, true);
}
myPolyData->SetTriangle (myNbElems, Poly_Triangle (myNodeInds[0] + 1, myNodeInds[1] + 1, myNodeInds[2] + 1));
if (theNbNodes == 4)
{
++myNbElems;
if (myPolyData->NbTriangles() < myNbElems)
{
myPolyData->ResizeTriangles (myNbElems * 2, true);
}
myPolyData->SetTriangle (myNbElems, Poly_Triangle (myNodeInds[0] + 1, myNodeInds[2] + 1, myNodeInds[3] + 1));
}
}
}
// =======================================================================
// function : AddTriangulation
// purpose :
// =======================================================================
void Poly_MergeNodesTool::AddTriangulation (const Handle(Poly_Triangulation)& theTris,
const gp_Trsf& theTrsf,
const Standard_Boolean theToReverse)
{
if (theTris.IsNull())
{
return;
}
if (!myPolyData.IsNull()
&& myPolyData->NbNodes() == 0)
{
// preallocate optimistically
myPolyData->SetDoublePrecision (theTris->IsDoublePrecision());
myPolyData->ResizeNodes (theTris->NbNodes(), false);
myPolyData->ResizeTriangles (theTris->NbTriangles(), false);
}
for (int anElemIter = 1; anElemIter <= theTris->NbTriangles(); ++anElemIter)
{
Poly_Triangle anElem = theTris->Triangle (anElemIter);
if (theToReverse)
{
anElem = Poly_Triangle (anElem.Value (1), anElem.Value (3), anElem.Value (2));
}
for (int aTriNodeIter = 0; aTriNodeIter < 3; ++aTriNodeIter)
{
const gp_Pnt aNode = theTris->Node (anElem.Value (aTriNodeIter + 1)).Transformed (theTrsf);
myPlaces[aTriNodeIter] = aNode.XYZ();
}
PushLastTriangle();
}
}
// =======================================================================
// function : Result
// purpose :
// =======================================================================
Handle(Poly_Triangulation) Poly_MergeNodesTool::Result()
{
if (myPolyData.IsNull())
{
return Handle(Poly_Triangulation)();
}
// compress data
myPolyData->ResizeNodes (myNbNodes, true);
myPolyData->ResizeTriangles(myNbElems, true);
return myPolyData;
}
// =======================================================================
// function : MergeNodes
// purpose :
// =======================================================================
Handle(Poly_Triangulation) Poly_MergeNodesTool::MergeNodes (const Handle(Poly_Triangulation)& theTris,
const gp_Trsf& theTrsf,
const Standard_Boolean theToReverse,
const double theSmoothAngle,
const double theMergeTolerance,
const bool theToForce)
{
if (theTris.IsNull()
|| theTris->NbNodes() < 3
|| theTris->NbTriangles() < 1)
{
return Handle(Poly_Triangulation)();
}
Poly_MergeNodesTool aMergeTool (theSmoothAngle, theMergeTolerance, theTris->NbTriangles());
aMergeTool.AddTriangulation (theTris, theTrsf, theToReverse);
if (!theToForce
&& aMergeTool.NbNodes() == theTris->NbNodes()
&& aMergeTool.NbElements() == theTris->NbTriangles())
{
return Handle(Poly_Triangulation)();
}
return aMergeTool.Result();
}