OpenCascade中读取STL文件的相同节点合并器(源码分析)
一、概述
STL文件的格式是没有节点索引的,是以数值来表示节点的,这就导致如果读入的时候不做去重的话,读取后节点会出现重复的情况,不仅浪费内存,而且可能在运行某些算法的时候出现惊人的错误。
STL的面片格式如下
facet normal 5.223066e-02 -1.736872e-01 9.834148e-01
outer loop
vertex 3.002048e+02 9.027633e+01 1.639474e+02
vertex 2.440949e+02 8.656459e+01 1.662719e+02
vertex 2.513544e+02 6.242381e+01 1.616227e+02
endloop
endfacet
二、代码分析
OpenCascade中的RWStl_Reader.cxx实现了对STL文件的读取,并且做到了去掉重复的点,让我们看一下源码
Class MergeNodeTool用于合并相同的点
首先分析一下MergeNodeTool的父类Poly_MergeNodesTool
构造函数:
public:
//! @param[in] theSmoothAngle smooth angle in radians or 0.0 to disable merging by angle
//! @param[in] theMergeTolerance node merging maximum distance
//! @param[in] theNbFacets estimated number of facets for map preallocation
Standard_EXPORT Poly_MergeNodesTool (const double theSmoothAngle,
const double theMergeTolerance = 0.0,
const int theNbFacets = -1);
类成员定义:
private:
Handle(Poly_Triangulation) myPolyData; //!< output triangulation
MergedNodesMap myNodeIndexMap; //!< map of merged nodes
NCollection_Map<NCollection_Vec4<int>, MergedElemHasher>
myElemMap; //!< map of elements
NCollection_Vec4<int> myNodeInds; //!< current element indexes
NCollection_Vec3<float> myTriNormal; //!< current triangle normal
gp_XYZ myPlaces[4]; //!< current triangle/quad coordinates to push
Standard_Real myUnitFactor; //!< scale factor to apply
Standard_Integer myNbNodes; //!< number of output nodes
Standard_Integer myNbElems; //!< number of output elements
Standard_Integer myNbDegenElems; //!< number of degenerated elements
Standard_Integer myNbMergedElems; //!< number of merged elements
Standard_Boolean myToDropDegenerative; //!< flag to filter our degenerate elements
Standard_Boolean myToMergeElems; //!< flag to merge elements
1、如何判断为相同的节点
在vec3AreEqual中使用两种评判标准,如果myInvTol <= 0 则不启用可容忍的误差来判断两点是否相等,仅当点完全一致时为相同顶点,另一种就是加入了可以容忍的误差,当点足够近的时候,也判断为相同的节点(注:代码中的myInvTol = 1.0/可容忍的误差值,避免因为太小而被判断为 == 0.0 导致没有加入可容忍的误差值)
// =======================================================================
inline bool Poly_MergeNodesTool::MergedNodesMap::vec3AreEqual (const NCollection_Vec3<float>& theKey1,
const NCollection_Vec3<float>& theKey2) const
{
if (myInvTol <= 0.0f)
{
//! 如果myInvTol <= 0 则不启用可容忍的误差来判断两点是否相等,仅当点完全一致时为相同.
//bool IsEqual (const NCollection_Vec3& theOther) const
//{
//return v[0] == theOther.v[0]
//&& v[1] == theOther.v[1]
//&& v[2] == theOther.v[2];
//}
//调用了NCollection中的IsEqual方法 对比每一个值来判断是否相等
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;
}
isEqual方法先是调用了之前点是否相等,其次是对两个点的法向量进行了点乘,由于都是单位向量,点乘的结果等于两个法向量夹角的余弦值,如果结果大于我们设定的cos值时,则说明时同一个顶点,应该合并。
当aCosinus <= -myAngleCos时,如果设定了myToMergeOpposite = true(允许合并具有相反方向法线的节点) 则合并。
(注:这玩意在等会的插入三角形的时候会用到,如果节点判断已经出现过了,则会直接返回id,而不会新增一个节点的id)
// =======================================================================
// 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 <=