MeshVS_Mesh使用说明:
在绘制各种点云数据时,经常需要绘制点云的网格(栅格)。
Open Cascade为绘制网格提供了一个专门的AIS对象:MeshVS_Mesh,在官方的Samples中没有该类的使用示例,在Draw模块中,可以看到该类的一些使用方法,但很少,而且限于display,没有selection的使用。
在项目中,我原来使用的是个继承自AIS_InteractiveObject的自定义网格类进行绘制,拣选则完全是独立的部分,使用opcode进行拣选。之后改为使用MeshVS_Mesh,只是code看起来clean一些,拣选部分不如自己实现的准确,但也可能是自己实现的问题。但总的来说,毕竟是OCC自带的,使用起来还是比自己实现要方便的多。
首先看一下MeshVS_Mesh的member methods:
SetDataSource (const Handle(MeshVS_DataSource )&aDataSource)
MeshVS_DataSource是一个抽象类,需要用户自己实现其中的一些函数,这些函数主要用来在绘制和选择网格时有用户决定其希望显示和选择的数据。 MeshVS_DataSource是一个数据容器。其内部的数据包含两部分,nodes及elements。nodes就是顶点数据,elements是该网格的元素类型,如三角形网格或者是矩形的栅格等。 MeshVS_DataSource需要用户实现nodes及elements数据的索引关系。elements由nodes的索引来表示。
以我的项目为例:我主要为了显示一个三角形网格一实现warpage的效果。假设基本的点云数据保存在类 CWarPageMeasurement 中.
我们首先需要实现一个 MeshVS_DataSource得派生类,假设为BrepMesh_Mesh_DataSource(无特定的含义,只是一开始就用了)。
该类的申明如下:
//! The sample DataSource for working with STLMesh_Mesh <br>
class BrepMesh_Mesh_DataSource : public MeshVS_DataSource {
public:
// Methods PUBLIC
//
//! Constructor <br>
Standard_EXPORT BrepMesh_Mesh_DataSource(CWarPageMeasurement* pMeasurement);
//! Returns geometry information about node ( if IsElement is False ) or element ( IsElement is True ) <br>
//! by co-ordinates. For element this method must return all its nodes co-ordinates in the strict order: X, Y, Z and <br>
//! with nodes order is the same as in wire bounding the face or link. NbNodes is number of nodes of element. <br>
//! It is recommended to return 1 for node. Type is an element type. <br>
Standard_EXPORT Standard_Boolean GetGeom(const Standard_Integer ID,const Standard_Boolean IsElement,TColStd_Array1OfReal& Coords,Standard_Integer& NbNodes,MeshVS_EntityType& Type) const;
//! This method is similar to GetGeom, but returns only element or node type. This method is provided for <br>
//! a fine performance. <br>
Standard_EXPORT Standard_Boolean GetGeomType(const Standard_Integer ID,const Standard_Boolean IsElement,MeshVS_EntityType& Type) const;
//! This method returns by number an address of any entity which represents element or node data structure. <br>
Standard_EXPORT Standard_Address GetAddr(const Standard_Integer ID,const Standard_Boolean IsElement) const;
//! This method returns information about what node this element consist of. <br>
Standard_EXPORT virtual Handle_TColStd_HArray1OfInteger GetNodesByElement(const Standard_Integer ID) const;
Standard_EXPORT virtual Standard_Boolean GetNodesByElement(const Standard_Integer ID,TColStd_Array1OfInteger& NodeIDs,Standard_Integer& NbNodes) const;
//! This method returns map of all nodes the object consist of. <br>
Standard_EXPORT const TColStd_PackedMapOfInteger& GetAllNodes() const;
//! This method returns map of all elements the object consist of. <br>
Standard_EXPORT const TColStd_PackedMapOfInteger& GetAllElements() const;
//! This method calculates normal of face, which is using for correct reflection presentation. <br>
//! There is default method, for advance reflection this method can be redefined. <br>
//Standard_EXPORT virtual Standard_Boolean GetNormal(const Standard_Integer Id,const Standard_Integer Max,Standard_Real& nx,Standard_Real& ny,Standard_Real& nz) const;
//Standard_EXPORT ~BrepMesh_Mesh_DataSource();
virtual Standard_EXPORT Standard_Boolean GetDetectedEntities (const Handle(MeshVS_Mesh)&Prs, const Standard_Real X, const Standard_Real Y, const Standard_Real aTol, Handle(TColStd_HPackedMapOfInteger)&Nodes, Handle(TColStd_HPackedMapOfInteger)&Elements);
virtual Standard_EXPORT Standard_Boolean GetDetectedEntities (const Handle(MeshVS_Mesh)&Prs, const Standard_Real XMin, const Standard_Real YMin, const Standard_Real XMax, const Standard_Real YMax, const Standard_Real aTol, Handle(TColStd_HPackedMapOfInteger)&Nodes, Handle(TColStd_HPackedMapOfInteger)&Elements);
virtual Standard_EXPORT Standard_Boolean GetDetectedEntities (const Handle(MeshVS_Mesh)&Prs, const TColgp_Array1OfPnt2d &Polyline, const Bnd_Box2d &aBox, const Standard_Real aTol, Handle(TColStd_HPackedMapOfInteger)&Nodes, Handle(TColStd_HPackedMapOfInteger)&Elements);
virtual Standard_EXPORT Standard_Boolean GetDetectedEntities (const Handle(MeshVS_Mesh)&Prs, Handle(TColStd_HPackedMapOfInteger)&Nodes, Handle(TColStd_HPackedMapOfInteger)&Elements);
virtual Standard_EXPORT Standard_Boolean IsAdvancedSelectionEnabled () const ;
// Type management
//
Standard_EXPORT const Handle(Standard_Type)& DynamicType() const;
//Standard_EXPORT Standard_Boolean IsKind(const Handle(Standard_Type)&) const;
protected:
// Methods PROTECTED
//
// Fields PROTECTED
//
private:
// Methods PRIVATE
//
// Fields PRIVATE
//
TColStd_PackedMapOfInteger myNodes;
TColStd_PackedMapOfInteger myElements;
Handle_TColStd_HArray2OfInteger myElemNodes;
Handle_TColStd_HArray2OfReal myNodeCoords;
Handle_TColStd_HArray2OfReal myElemNormals;
CWarPageMeasurement* m_pWarpage;
};
上面只是一个实例,你同样可以以其他的方式来保存数据。
myNodes为顶点索引数组, myNodeCoords为顶点坐标二维数组,通过顶点索引来获取坐标值(详见下面示例). myElements为元素索引数组, myElemNodes为组成元素的顶点索引的二维数组。 myElemNormals为elemnts的各element的组成顶点的法线信息,暂不实现。
在构造函数中,我们完成这些信息的填充:
BrepMesh_Mesh_DataSource::BrepMesh_Mesh_DataSource(CWarPageMeasurement* pMeasurement)
{
设顶点数据保存在pMeasurement的顶点数组 pArray中
初始化顶点坐标二维数组,这里设顶点坐标为三位坐标
myNodeCoords = new TColStd_HArray2OfReal(1, pArray->Size(), 1, 3);
for( i = 1; i <= pArray->Size() ; i++ )
{
myNodes.Add( i );
myNodeCoords->SetValue(i, 1, pMeasurement->m_ pArray [i - 1].X());
myNodeCoords->SetValue(i, 2, pMeasurement->m_ pArray [i - 1].Y());
myNodeCoords->SetValue(i, 3, pMeasurement->m_ pArray [i - 1].Z());
}
三角形网格顶点索引列表存储在 pMeasurement的数组 pTriArray中(每三个索引元素构成一个三角形)
myElemNormals = new TColStd_HArray2OfReal(1, pTriArray->Size() /3, 1, 3);
myElemNodes = new TColStd_HArray2OfInteger(1, pTriArray->Size()/3 , 1, 3);
for( i = 1; i <= pTriArray ->Size() /3; i++ )
{
myElements.Add(i);
myElemNodes->SetValue(i, 1, pTriArray [(i-1)*3]);
myElemNodes->SetValue(i, 2, pTriArray [ (i-1)*3 + 1] );
myElemNodes->SetValue(i, 3, pTriArray [ (i-1)*3 ] + 2);
myElemNormals->SetValue(i, 1, 0 );
myElemNormals->SetValue(i, 2, 0 );
myElemNormals->SetValue(i, 3, 0 );
}
}
其他一些函数的实现如下,它们主要是用来根据参数取得相应的数据信息,比较简单,不做详细说明。
//================================================================
// Function : GetGeom
// Purpose :
//================================================================
Standard_Boolean BrepMesh_Mesh_DataSource::GetGeom
( const Standard_Integer ID, const Standard_Boolean IsElement,
TColStd_Array1OfReal& Coords, Standard_Integer& NbNodes,
MeshVS_EntityType& Type ) const
{
if( IsElement )
{
if( ID>=1 && ID<=myElements.Extent() )
{
Type = MeshVS_ET_Face;
NbNodes = 3;
for( Standard_Integer i = 1, k = 1; i <= 3; i++ )
{
Standard_Integer IdxNode = myElemNodes->Value(ID, i);
for(Standard_Integer j = 1; j <= 3; j++, k++ )
Coords(k) = myNodeCoords->Value(IdxNode, j);
}
return Standard_True;
}
else
return Standard_False;
}
else
if( ID>=1 && ID<=myNodes.Extent() )
{
Type = MeshVS_ET_Node;
NbNodes = 1;
Coords( 1 ) = myNodeCoords->Value(ID, 1);
Coords( 2 ) = myNodeCoords->Value(ID, 2);
Coords( 3 ) = myNodeCoords->Value(ID, 3);
return Standard_True;
}
else
return Standard_False;
}
//================================================================
// Function : GetGeomType
// Purpose :
//================================================================
Standard_Boolean BrepMesh_Mesh_DataSource::GetGeomType
( const Standard_Integer,
const Standard_Boolean IsElement,
MeshVS_EntityType& Type ) const
{
if( IsElement )
{
Type = MeshVS_ET_Face;
return Standard_True;
}
else
{
Type = MeshVS_ET_Node;
return Standard_True;
}
}
//================================================================
// Function : GetAddr
// Purpose :
//================================================================
Standard_Address BrepMesh_Mesh_DataSource::GetAddr
( const Standard_Integer ID,const Standard_Boolean IsElement ) const
{
if( IsElement )
{
return (Standard_Address)&( myElemNodes->Value(ID,1) );
}else
{
return (Standard_Address)&(myNodeCoords->Value(ID,1) );
}
return NULL;
}
//================================================================
// Function : GetNodesByElement
// Purpose :
//================================================================
Handle(TColStd_HArray1OfInteger) BrepMesh_Mesh_DataSource::GetNodesByElement
( const Standard_Integer ID ) const
{
if( ID>=1 && ID<=myElements.Extent() )
{
Handle(TColStd_HArray1OfInteger) anArr = new TColStd_HArray1OfInteger (1, 3);
anArr->SetValue (1, myElemNodes->Value(ID, 1 ) );
anArr->SetValue (2, myElemNodes->Value(ID, 2 ) );
anArr->SetValue (3, myElemNodes->Value(ID, 3 ) );
return anArr;
}
return 0;
}
Standard_Boolean BrepMesh_Mesh_DataSource::GetNodesByElement(const Standard_Integer ID,TColStd_Array1OfInteger& NodeIDs,Standard_Integer& NbNodes) const
{
if( ID>=1 && ID<=myElements.Extent() )
{
NodeIDs.SetValue (1, myElemNodes->Value(ID, 1) );
NodeIDs.SetValue (2, myElemNodes->Value(ID, 2) );
NodeIDs.SetValue (3, myElemNodes->Value(ID, 3) );
NbNodes = 3;
return Standard_True;
}
return Standard_False;
}
//================================================================
// Function : GetAllNodes
// Purpose :
//================================================================
const TColStd_PackedMapOfInteger& BrepMesh_Mesh_DataSource::GetAllNodes() const
{
return myNodes;
}
//================================================================
// Function : GetAllElements
// Purpose :
//================================================================
const TColStd_PackedMapOfInteger& BrepMesh_Mesh_DataSource::GetAllElements() const
{
return myElements;
}
下面主要说一下拣选部分虚函数的实现:
对于MeshVS_Mesh,主要有两种拣选方式,一种是普通的拣选方式,有OCC实现,一种是高级拣选方式,可实现单个顶点或元素的拣选,需要用户自己实现DataSource中的一些虚函数。
以点选为例:需要实现纯虚函数GetDetectedEntities (const Handle(MeshVS_Mesh)&Prs, const Standard_Real X, const Standard_Real Y, const Standard_Real aTol, Handle(TColStd_HPackedMapOfInteger)&Nodes, Handle(TColStd_HPackedMapOfInteger)&Elements)
Nodes及Elements为返回值,保存返回的顶点或索引值,供OCC调用。我们需要自己实现判断哪些元素或顶点被选中。
这里可以自己实现相应的算法,也可以为了简便实用OCC提供的函数,以三角形拣选为例,可以使用Select3D_SensitiveTriangle,
Standard_Boolean BrepMesh_Mesh_DataSource::GetDetectedEntities (const Handle(MeshVS_Mesh)&Prs, const Standard_Real X, const Standard_Real Y, const Standard_Real aTol, Handle(TColStd_HPackedMapOfInteger)&Nodes, Handle(TColStd_HPackedMapOfInteger)&Elements)
{
/// implement this function for MeshVS_SensitiveMesh
Handle(SelectMgr_EntityOwner) owner = new SelectMgr_EntityOwner(Prs);
Handle_StdSelect_ViewerSelector3d viewselector3d = new StdSelect_ViewerSelector3d();
CMainFrame* pMainFrm = (CMainFrame*)AfxGetMainWnd();
viewselector3d->InitProj( m_hView);
Select3D_Projector projector = viewselector3d->Projector();
TColStd_MapIteratorOfPackedMapOfInteger anIterN( myElements );
Standard_Real dmin = Precision::Infinite();
Standard_Integer returnIndex = -1;;
//anIterN.Reset();
for( ; anIterN.More(); anIterN.Next() )
{
Standard_Integer index = anIterN.Key();
int p1 = myElemNodes->Value(index,1);
int p2 = myElemNodes->Value(index,2);
int p3 = myElemNodes->Value(index,3);
gp_Pnt pnt1(myNodeCoords->Value(p1,1),myNodeCoords->Value(p1,2), myNodeCoords->Value(p1,3));
gp_Pnt pnt2(myNodeCoords->Value(p2,1),myNodeCoords->Value(p2,2), myNodeCoords->Value(p2,3));
gp_Pnt pnt3(myNodeCoords->Value(p3,1),myNodeCoords->Value(p3,2), myNodeCoords->Value(p3,3));
Handle(Select3D_SensitiveTriangle) sensitiveTri = new Select3D_SensitiveTriangle( owner, pnt1, pnt2, pnt3 );
sensitiveTri->Project(projector);
double dis;
if( sensitiveTri->Matches(X,Y,aTol,dis) )
{
if( dis < dmin )
{
returnIndex = index;
}
}
}
if( returnIndex != -1 )
{
Elements = new TColStd_HPackedMapOfInteger(1);
Elements->ChangeMap().Add(returnIndex);
}
return Standard_True;
}
注意,由于我们使用上述函数并不是在虚函数ComputeSelection中,因此一些数据需要我们自己进行初始化,如
sensitiveTri->Project(projector);
该函数主要是为了计算元素的2D包围盒,这里需要自己调用,至于其他的类或函数时可能出现同样的问题就需要自己查看源码和文档了。
OCC实用2D包围盒进行拣选判断,最好自己计算选中的元素。
为了开启高级拣选模式,函数 IsAdvancedSelectionEnabled 的返回值应为 Standard_True。
如果只是为了选中整体,则不需实现上述的虚函数。