mirror of
https://github.com/Open-Cascade-SAS/OCCT.git
synced 2026-08-08 23:07:28 +08:00
8d2d8650ca
- Refactors `BRepCheck_*` result classes to use an always-present mutex with a parallel-mode guard, and updates the parallel analyzer to use the new locking model. - Makes multiple Foundation-level globals thread-safe via `std::atomic`, adds mutex-based protection for lazy initialization, and introduces `std::call_once` on Windows host initialization. - Converts several TKBool global mutable statics to `thread_local` to prevent cross-thread state corruption.
518 lines
16 KiB
C++
518 lines
16 KiB
C++
// Created on: 1995-03-06
|
|
// Created by: Laurent PAINNOT
|
|
// Copyright (c) 1995-1999 Matra Datavision
|
|
// Copyright (c) 1999-2014 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_Triangulation.hxx>
|
|
|
|
#include <gp_Pnt.hxx>
|
|
#include <OSD_FileSystem.hxx>
|
|
#include <Poly_Triangle.hxx>
|
|
#include <Standard_Dump.hxx>
|
|
#include <Standard_Type.hxx>
|
|
|
|
#include <mutex>
|
|
|
|
IMPLEMENT_STANDARD_RTTIEXT(Poly_Triangulation, Standard_Transient)
|
|
|
|
//=================================================================================================
|
|
|
|
Poly_Triangulation::Poly_Triangulation()
|
|
: myDeflection(0),
|
|
myPurpose(Poly_MeshPurpose_NONE)
|
|
{
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
Poly_Triangulation::Poly_Triangulation(const int theNbNodes,
|
|
const int theNbTriangles,
|
|
const bool theHasUVNodes,
|
|
const bool theHasNormals)
|
|
: myDeflection(0),
|
|
myNodes(theNbNodes),
|
|
myTriangles(1, theNbTriangles),
|
|
myPurpose(Poly_MeshPurpose_NONE)
|
|
{
|
|
if (theHasUVNodes)
|
|
{
|
|
myUVNodes.Resize(theNbNodes, false);
|
|
}
|
|
if (theHasNormals)
|
|
{
|
|
myNormals.Resize(0, theNbNodes - 1, false);
|
|
}
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
Poly_Triangulation::Poly_Triangulation(const NCollection_Array1<gp_Pnt>& theNodes,
|
|
const NCollection_Array1<Poly_Triangle>& theTriangles)
|
|
: myDeflection(0),
|
|
myNodes(theNodes.Length()),
|
|
myTriangles(1, theTriangles.Length()),
|
|
myPurpose(Poly_MeshPurpose_NONE)
|
|
{
|
|
const Poly_ArrayOfNodes aNodeWrapper(theNodes.First(), theNodes.Length());
|
|
myNodes = aNodeWrapper;
|
|
myTriangles = theTriangles;
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
Poly_Triangulation::Poly_Triangulation(const NCollection_Array1<gp_Pnt>& theNodes,
|
|
const NCollection_Array1<gp_Pnt2d>& theUVNodes,
|
|
const NCollection_Array1<Poly_Triangle>& theTriangles)
|
|
: myDeflection(0),
|
|
myNodes(theNodes.Length()),
|
|
myTriangles(1, theTriangles.Length()),
|
|
myUVNodes(theNodes.Length()),
|
|
myPurpose(Poly_MeshPurpose_NONE)
|
|
{
|
|
const Poly_ArrayOfNodes aNodeWrapper(theNodes.First(), theNodes.Length());
|
|
myNodes = aNodeWrapper;
|
|
myTriangles = theTriangles;
|
|
const Poly_ArrayOfUVNodes aUVNodeWrapper(theUVNodes.First(), theUVNodes.Length());
|
|
myUVNodes = aUVNodeWrapper;
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
Poly_Triangulation::~Poly_Triangulation()
|
|
{
|
|
delete myCachedMinMax.load(std::memory_order_acquire);
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
occ::handle<Poly_Triangulation> Poly_Triangulation::Copy() const
|
|
{
|
|
return new Poly_Triangulation(this);
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
Poly_Triangulation::Poly_Triangulation(const occ::handle<Poly_Triangulation>& theTriangulation)
|
|
: myDeflection(theTriangulation->myDeflection),
|
|
myNodes(theTriangulation->myNodes),
|
|
myTriangles(theTriangulation->myTriangles),
|
|
myUVNodes(theTriangulation->myUVNodes),
|
|
myNormals(theTriangulation->myNormals),
|
|
myPurpose(theTriangulation->myPurpose)
|
|
{
|
|
SetCachedMinMax(theTriangulation->CachedMinMax());
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
void Poly_Triangulation::Clear()
|
|
{
|
|
if (!myNodes.IsEmpty())
|
|
{
|
|
Poly_ArrayOfNodes anEmptyNodes;
|
|
anEmptyNodes.SetDoublePrecision(myNodes.IsDoublePrecision());
|
|
myNodes.Move(anEmptyNodes);
|
|
}
|
|
if (!myTriangles.IsEmpty())
|
|
{
|
|
NCollection_Array1<Poly_Triangle> anEmptyTriangles;
|
|
myTriangles.Move(anEmptyTriangles);
|
|
}
|
|
RemoveUVNodes();
|
|
RemoveNormals();
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
void Poly_Triangulation::RemoveUVNodes()
|
|
{
|
|
if (!myUVNodes.IsEmpty())
|
|
{
|
|
Poly_ArrayOfUVNodes anEmpty;
|
|
anEmpty.SetDoublePrecision(myUVNodes.IsDoublePrecision());
|
|
myUVNodes.Move(anEmpty);
|
|
}
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
void Poly_Triangulation::RemoveNormals()
|
|
{
|
|
if (!myNormals.IsEmpty())
|
|
{
|
|
NCollection_Array1<NCollection_Vec3<float>> anEmpty;
|
|
myNormals.Move(anEmpty);
|
|
}
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
occ::handle<NCollection_HArray1<gp_Pnt>> Poly_Triangulation::MapNodeArray() const
|
|
{
|
|
if (myNodes.IsEmpty())
|
|
{
|
|
return occ::handle<NCollection_HArray1<gp_Pnt>>();
|
|
}
|
|
|
|
if (myNodes.IsDoublePrecision())
|
|
{
|
|
// wrap array
|
|
const gp_Pnt* aPntArr = &myNodes.First<gp_Pnt>();
|
|
occ::handle<NCollection_HArray1<gp_Pnt>> anHArray = new NCollection_HArray1<gp_Pnt>();
|
|
NCollection_Array1<gp_Pnt> anArray(*aPntArr, 1, NbNodes());
|
|
anHArray->Move(anArray);
|
|
return anHArray;
|
|
}
|
|
|
|
// deep copy
|
|
occ::handle<NCollection_HArray1<gp_Pnt>> anArray = new NCollection_HArray1<gp_Pnt>(1, NbNodes());
|
|
for (int aNodeIter = 0; aNodeIter < NbNodes(); ++aNodeIter)
|
|
{
|
|
const gp_Pnt aPnt = myNodes.Value(aNodeIter);
|
|
anArray->SetValue(aNodeIter + 1, aPnt);
|
|
}
|
|
return anArray;
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
occ::handle<NCollection_HArray1<Poly_Triangle>> Poly_Triangulation::MapTriangleArray() const
|
|
{
|
|
if (myTriangles.IsEmpty())
|
|
{
|
|
return occ::handle<NCollection_HArray1<Poly_Triangle>>();
|
|
}
|
|
|
|
occ::handle<NCollection_HArray1<Poly_Triangle>> anHArray =
|
|
new NCollection_HArray1<Poly_Triangle>();
|
|
NCollection_Array1<Poly_Triangle> anArray(myTriangles.First(), 1, NbTriangles());
|
|
anHArray->Move(anArray);
|
|
return anHArray;
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
occ::handle<NCollection_HArray1<gp_Pnt2d>> Poly_Triangulation::MapUVNodeArray() const
|
|
{
|
|
if (myUVNodes.IsEmpty())
|
|
{
|
|
return occ::handle<NCollection_HArray1<gp_Pnt2d>>();
|
|
}
|
|
|
|
if (myUVNodes.IsDoublePrecision())
|
|
{
|
|
// wrap array
|
|
const gp_Pnt2d* aPntArr = &myUVNodes.First<gp_Pnt2d>();
|
|
occ::handle<NCollection_HArray1<gp_Pnt2d>> anHArray = new NCollection_HArray1<gp_Pnt2d>();
|
|
NCollection_Array1<gp_Pnt2d> anArray(*aPntArr, 1, NbNodes());
|
|
anHArray->Move(anArray);
|
|
return anHArray;
|
|
}
|
|
|
|
// deep copy
|
|
occ::handle<NCollection_HArray1<gp_Pnt2d>> anArray =
|
|
new NCollection_HArray1<gp_Pnt2d>(1, NbNodes());
|
|
for (int aNodeIter = 0; aNodeIter < NbNodes(); ++aNodeIter)
|
|
{
|
|
const gp_Pnt2d aPnt = myUVNodes.Value(aNodeIter);
|
|
anArray->SetValue(aNodeIter + 1, aPnt);
|
|
}
|
|
return anArray;
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
occ::handle<NCollection_HArray1<float>> Poly_Triangulation::MapNormalArray() const
|
|
{
|
|
if (myNormals.IsEmpty())
|
|
{
|
|
return occ::handle<NCollection_HArray1<float>>();
|
|
}
|
|
|
|
occ::handle<NCollection_HArray1<float>> anHArray = new NCollection_HArray1<float>();
|
|
NCollection_Array1<float> anArray(*myNormals.First().GetData(), 1, 3 * NbNodes());
|
|
anHArray->Move(anArray);
|
|
return anHArray;
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
void Poly_Triangulation::SetNormals(const occ::handle<NCollection_HArray1<float>>& theNormals)
|
|
{
|
|
if (theNormals.IsNull() || theNormals->Length() != 3 * NbNodes())
|
|
{
|
|
throw Standard_DomainError("Poly_Triangulation::SetNormals : wrong length");
|
|
}
|
|
|
|
AddNormals();
|
|
const int anArrayLower = theNormals->Lower();
|
|
for (int aNodeIter = 1; aNodeIter <= NbNodes(); ++aNodeIter)
|
|
{
|
|
int anArrayInd = anArrayLower + (aNodeIter - 1) * 3;
|
|
NCollection_Vec3<float> aNorm(theNormals->Value(anArrayInd + 0),
|
|
theNormals->Value(anArrayInd + 1),
|
|
theNormals->Value(anArrayInd + 2));
|
|
SetNormal(aNodeIter, aNorm);
|
|
}
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
void Poly_Triangulation::SetDoublePrecision(bool theIsDouble)
|
|
{
|
|
myNodes.SetDoublePrecision(theIsDouble);
|
|
myUVNodes.SetDoublePrecision(theIsDouble);
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
void Poly_Triangulation::ResizeNodes(int theNbNodes, bool theToCopyOld)
|
|
{
|
|
myNodes.Resize(theNbNodes, theToCopyOld);
|
|
if (!myUVNodes.IsEmpty())
|
|
{
|
|
myUVNodes.Resize(theNbNodes, theToCopyOld);
|
|
}
|
|
if (!myNormals.IsEmpty())
|
|
{
|
|
myNormals.Resize(0, theNbNodes - 1, theToCopyOld);
|
|
}
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
void Poly_Triangulation::ResizeTriangles(int theNbTriangles, bool theToCopyOld)
|
|
{
|
|
myTriangles.Resize(1, theNbTriangles, theToCopyOld);
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
void Poly_Triangulation::AddUVNodes()
|
|
{
|
|
if (myUVNodes.IsEmpty() || myUVNodes.Size() != myNodes.Size())
|
|
{
|
|
myUVNodes.Resize(myNodes.Size(), false);
|
|
}
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
void Poly_Triangulation::AddNormals()
|
|
{
|
|
if (myNormals.IsEmpty() || myNormals.Size() != myNodes.Size())
|
|
{
|
|
myNormals.Resize(0, myNodes.Size() - 1, false);
|
|
}
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
void Poly_Triangulation::DumpJson(Standard_OStream& theOStream, int) const
|
|
{
|
|
OCCT_DUMP_TRANSIENT_CLASS_BEGIN(theOStream)
|
|
|
|
OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, myDeflection)
|
|
|
|
OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, myNodes.Size())
|
|
if (!myUVNodes.IsEmpty())
|
|
OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, myUVNodes.Size())
|
|
if (!myNormals.IsEmpty())
|
|
OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, myNormals.Size())
|
|
OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, myTriangles.Size())
|
|
OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, myPurpose)
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
const Bnd_Box& Poly_Triangulation::CachedMinMax() const
|
|
{
|
|
static const Bnd_Box anEmptyBox;
|
|
std::shared_lock<std::shared_mutex> aLock(myCachedMinMaxMutex);
|
|
const Bnd_Box* aBox = myCachedMinMax.load(std::memory_order_relaxed);
|
|
return (aBox == nullptr) ? anEmptyBox : *aBox;
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
void Poly_Triangulation::SetCachedMinMax(const Bnd_Box& theBox)
|
|
{
|
|
if (theBox.IsVoid())
|
|
{
|
|
unsetCachedMinMax();
|
|
return;
|
|
}
|
|
std::unique_lock<std::shared_mutex> aLock(myCachedMinMaxMutex);
|
|
Bnd_Box* aBox = myCachedMinMax.load(std::memory_order_relaxed);
|
|
if (aBox == nullptr)
|
|
{
|
|
aBox = new Bnd_Box();
|
|
*aBox = theBox;
|
|
myCachedMinMax.store(aBox, std::memory_order_release);
|
|
}
|
|
else
|
|
{
|
|
*aBox = theBox;
|
|
}
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
void Poly_Triangulation::unsetCachedMinMax()
|
|
{
|
|
std::unique_lock<std::shared_mutex> aLock(myCachedMinMaxMutex);
|
|
Bnd_Box* aBox = myCachedMinMax.load(std::memory_order_relaxed);
|
|
myCachedMinMax.store(nullptr, std::memory_order_release);
|
|
delete aBox;
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
bool Poly_Triangulation::MinMax(Bnd_Box& theBox,
|
|
const gp_Trsf& theTrsf,
|
|
const bool theIsAccurate) const
|
|
{
|
|
Bnd_Box aBox;
|
|
bool aUsedCache = false;
|
|
{
|
|
std::shared_lock<std::shared_mutex> aLock(myCachedMinMaxMutex);
|
|
const Bnd_Box* aCachedBox = myCachedMinMax.load(std::memory_order_relaxed);
|
|
if (aCachedBox != nullptr
|
|
&& (!HasGeometry() || !theIsAccurate || theTrsf.Form() == gp_Identity
|
|
|| theTrsf.Form() == gp_Translation || theTrsf.Form() == gp_PntMirror
|
|
|| theTrsf.Form() == gp_Scale))
|
|
{
|
|
aBox = aCachedBox->Transformed(theTrsf);
|
|
aUsedCache = true;
|
|
}
|
|
}
|
|
if (!aUsedCache)
|
|
{
|
|
aBox = computeBoundingBox(theTrsf);
|
|
}
|
|
if (aBox.IsVoid())
|
|
{
|
|
return false;
|
|
}
|
|
theBox.Add(aBox);
|
|
return true;
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
Bnd_Box Poly_Triangulation::computeBoundingBox(const gp_Trsf& theTrsf) const
|
|
{
|
|
Bnd_Box aBox;
|
|
if (theTrsf.Form() == gp_Identity)
|
|
{
|
|
for (int aNodeIdx = 0; aNodeIdx < NbNodes(); aNodeIdx++)
|
|
{
|
|
aBox.Add(myNodes.Value(aNodeIdx));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int aNodeIdx = 0; aNodeIdx < NbNodes(); aNodeIdx++)
|
|
{
|
|
aBox.Add(myNodes.Value(aNodeIdx).Transformed(theTrsf));
|
|
}
|
|
}
|
|
return aBox;
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
void Poly_Triangulation::ComputeNormals()
|
|
{
|
|
// zero values
|
|
AddNormals();
|
|
myNormals.Init(NCollection_Vec3<float>(0.0f));
|
|
|
|
int anElem[3] = {0, 0, 0};
|
|
for (NCollection_Array1<Poly_Triangle>::Iterator aTriIter(myTriangles); aTriIter.More();
|
|
aTriIter.Next())
|
|
{
|
|
aTriIter.Value().Get(anElem[0], anElem[1], anElem[2]);
|
|
const gp_Pnt aNode0 = myNodes.Value(anElem[0] - 1);
|
|
const gp_Pnt aNode1 = myNodes.Value(anElem[1] - 1);
|
|
const gp_Pnt aNode2 = myNodes.Value(anElem[2] - 1);
|
|
|
|
const gp_XYZ aVec01 = aNode1.XYZ() - aNode0.XYZ();
|
|
const gp_XYZ aVec02 = aNode2.XYZ() - aNode0.XYZ();
|
|
const gp_XYZ aTriNorm = aVec01 ^ aVec02;
|
|
const NCollection_Vec3<float> aNorm3f =
|
|
NCollection_Vec3<float>(float(aTriNorm.X()), float(aTriNorm.Y()), float(aTriNorm.Z()));
|
|
for (int aNodeIter = 0; aNodeIter < 3; ++aNodeIter)
|
|
{
|
|
myNormals.ChangeValue(anElem[aNodeIter] - 1) += aNorm3f;
|
|
}
|
|
}
|
|
|
|
// Normalize all vectors
|
|
for (NCollection_Array1<NCollection_Vec3<float>>::Iterator aNodeIter(myNormals); aNodeIter.More();
|
|
aNodeIter.Next())
|
|
{
|
|
NCollection_Vec3<float>& aNorm3f = aNodeIter.ChangeValue();
|
|
const float aMod = aNorm3f.Modulus();
|
|
aNorm3f = aMod == 0.0f ? NCollection_Vec3<float>(0.0f, 0.0f, 1.0f) : (aNorm3f / aMod);
|
|
}
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
bool Poly_Triangulation::LoadDeferredData(const occ::handle<OSD_FileSystem>& theFileSystem)
|
|
{
|
|
if (!HasDeferredData())
|
|
{
|
|
return false;
|
|
}
|
|
if (!loadDeferredData(theFileSystem, this))
|
|
{
|
|
return false;
|
|
}
|
|
SetMeshPurpose(myPurpose | Poly_MeshPurpose_Loaded);
|
|
return true;
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
occ::handle<Poly_Triangulation> Poly_Triangulation::DetachedLoadDeferredData(
|
|
const occ::handle<OSD_FileSystem>& theFileSystem) const
|
|
{
|
|
if (!HasDeferredData())
|
|
{
|
|
return occ::handle<Poly_Triangulation>();
|
|
}
|
|
occ::handle<Poly_Triangulation> aResult = createNewEntity();
|
|
if (!loadDeferredData(theFileSystem, aResult))
|
|
{
|
|
return occ::handle<Poly_Triangulation>();
|
|
}
|
|
aResult->SetMeshPurpose(aResult->MeshPurpose() | Poly_MeshPurpose_Loaded);
|
|
return aResult;
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
bool Poly_Triangulation::UnloadDeferredData()
|
|
{
|
|
if (HasDeferredData())
|
|
{
|
|
Clear();
|
|
SetMeshPurpose(myPurpose & ~Poly_MeshPurpose_Loaded);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|