@版权声明:本文为版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出,
本文链接https://www.jianshu.com/p/4b67596f5ac0
如有问题, 可邮件(yumxuanyi@qq.com)咨询。
关键字:OpenCascade、SelectMgr_EntityOwner、Sensitive Entity
Entity Owner
Entity Owner对象中存储了
- SelectMgr_SelectableObject *mySelectable; //表示可选择对象的指针 也就是说EntityOwner连接着该可选对象
- 当前Sensitive entity显示的信息。
每一个Entity Owner只负责高亮显示它本身。其实内部只是重新计算了Sensitive entity的显示。
1. 定义CMyEdgeOwner 类
通过继承SelectMgr_EntityOwner类来创建我们自己的CMyEdgeOwner 。
该CMyEdgeOwner 用于高亮显示一个边(线段),比如一个立方体盒子的一条边。
class CMyEdgeOwner ::public SelectMgr_EntityOwner
{
DEFINE_STANDARD_RTTIEXT(CMyEdgeOwner ,SelectMgr_EntityOwner)
public:
//!@ theSelObj为必须传入的可选择对象
//!@ startPoint endPoint为为了重新计算其外观表现闯入的线段的两个端点
Standard_EXPORT CMyEdgeOwner (const Handle(SelectMgr_SelectableObject) &theSelObj,gp_Pnt startPoint,gp_Pnt endPoint,const Standard_Integer aPriority = 2) ;
virtual ~CMyEdgeOwner(void);
protected://需要实现的三个虚函数如下
//用于根据给定的模式计算并进行高亮 主要在该方法中重新计算了其显示外观
Standard_EXPORT virtrual void HilightWithColor(const Handle(PrsMgr_PresentationManager3d) &thePM,const Handle(Prs3d_Drawer)& theStyle,const Standard_Integer theMode =2) Standard_OVERRIDE;
//取消高亮显示
Standard_EXPORT virtrual void Unhilight(const Handle(PrsMgr_PresentationManager) &aPM,const Standard_Integer aMode =0) Standard_OVERRIDE;
//当PrsMgr_PresentationManager aPM根据高亮显示模式成功高亮选择对象后 将返回true
Standard_EXPORT virtrual void IsHilighted(const Handle(PrsMgr_PresentationManager) &aPM,const Standard_Integer aMode =0) Standard_OVERRIDE;
private:
gp_Pnt myStartPoint;//起点
gp_Pnt myEndPoint;//终点
Handle(PrsMgr_PresentableObject) myPresentation;//最终选择后的高亮显示在这里计算
};
DEFINE_STANDARD_HANDLE(CMyBoxEdgeOwner,SelectMgr_EntityOwner)
2. 实现CMyEdgeOwner 类
IMPLEMENT_STANDARD_RTTIEXT(CMyEdgeOwner ,SelectMgr_EntityOwner)
CMyEdgeOwner::CMyEdgeOwner (const Handle(SelectMgr_SelectableObject) &theSelObj,gp_Pnt startPoint,gp_Pnt endPoint,const Standard_Integer aPriority ):SelectMgr_EntityOwner(theSelObj.operator->(),aPriority),myStartPoint(startPoint),myEndPoint(endPoint)
{
myPresentation = new CMyEdgePresentation(startPoint,endPoint);
this->myIsSelected = Standard_False;
this->myFormDecomposition = Standard_True;
}
//用于根据给定的模式计算并进行高亮 主要在该方法中重新计算了其显示外观
void CMyEdgeOwner::HilightWithColor(const Handle(PrsMgr_PresentationManager3d) &thePM,const Handle(Prs3d_Drawer)& theStyle,const Standard_Integer theMode )
{
theStyle->SetColor(Quantity_NOC_YELLOW);//设置高亮显示颜色
myPresentation->SetAttributes(theStyle);
thePM->Color(myPresentation,theStyle,theMode,mySelectable);//这一句用具计算并高亮显示我们的边
}
//取消高亮显示
void CMyEdgeOwner::Unhilight(const Handle(PrsMgr_PresentationManager) &aPM,const Standard_Integer aMode )
{
if(myPresentation.IsNull() || !myFromDecompositon)
{
aPM->UnHighlight(Selectable());
}
else
{
aPM->UnHighlight(myPresentation);
}
}
//当PrsMgr_PresentationManager aPM根据高亮显示模式成功高亮选择对象后 将返回true
void CMyEdgeOwner::IsHilighted(const Handle(PrsMgr_PresentationManager) &aPM,const Standard_Integer aMode )
{
return aPM->IsHighlited(myPresentation,0);
}
3. 定义CMyEdgePresentation 类
通过继承PrsMgr_PresentableObject类来创建我们自己的CMyEdgePresentation 。
该CMyEdgePresentation 用于计算高亮显示的的线段,其实就是计算覆盖到原来的对象上的另外一条线。
class CMyEdgePresentation::public PrsMgr_PresentableObject
{
DEFINE_STANDARD_RTTIEXT(CMyEdgePresentation,PrsMgr_PresentableObject)
public:
//!@ startPoint endPoint为为了重新计算其外观表现闯入的线段的两个端点
Standard_EXPORT CMyEdgePresentation(gp_Pnt startPoint,gp_Pnt endPoint) ;
virtual ~CMyEdgePresentation(void);
protected://需要实现如下虚函数
//用于根据给定的模式计算并进行高亮 主要在该方法中重新计算了其显示外观
Standard_EXPORT virtrual void Compute(const Handle(PrsMgr_PresentationManager3d) &thePM,const Handle(Prs3d_Presentation)& aPresentation,const Standard_Integer theMode =0) Standard_OVERRIDE;
private:
gp_Pnt myStartPoint;//起点
gp_Pnt myEndPoint;//终点
}
4. 实现PrsMgr_PresentableObject 类
IMPLEMENT_STANDARD_RTTIEXT(CMyEdgePresentation,PrsMgr_PresentableObject)
CMyEdgePresentation::CMyEdgePresentation(gp_Pnt startPoint,gp_Pnt endPoint,const Standard_Integer aPriority ):myStartPoint(startPoint),myEndPoint(endPoint)
{
}
//用于根据给定的模式计算并进行高亮 主要在该方法中重新计算了其显示外观
void CMyEdgePresentation:: Compute(const Handle(PrsMgr_PresentationManager3d) &thePM,const Handle(Prs3d_Presentation)& aPresentation,const Standard_Integer theMode )
{
aPresentation->Clear(Standard_True);//清除结构中的所有组,用于创建临时的高亮显示对象
Staandard_Integer maxVertexsCount = 2;//点的数量
Standard_Integer maxEdges = 2;//边的数量
Standard_Boolean hasEdgeColor = Standard_True;
Handle(Graphic3d_ArrayOfSegments) anSegmentsArray = new Graphic3d_ArrayOfSegments(maxVertexsCount ,maxEdges ,hasEdgeColor );
anSegmentsArray ->AddVertex(this->_startPoint);
anSegmentsArray ->AddVertex(this->_endPoint);
anSegmentsArray ->AddEdge(1);
anSegmentsArray ->AddEdge(2);
Handle(Graphic3d_Group) hEdgeGroup = Prs3d_Root::CurrentGroup(aPresentation);//创建一个新的group
hEdgeGroup ->AddPrimitiveArray(anSegmentsArray );//在Group中添加对象
hEdgeGroup->SetGroupPrimitivesAspect(myDrawer->PointAspect()->Aspect());//设置颜色属性
}
5. 理解继承关系
AIS_InteractiveObject 继承于 SelectMgr_SelectableObject
SelectMgr_SelectableObject 继承于 PrsMgr_PresentableObject
也就是说:一个可交互对象既是一个可选择对象也是一个可显示对象。
所有我们可以通过继承于AIS_InteractiveObject或SelectMgr_SelectableObject
来创建自己的可交互对象。
我们创建的可交互对象必须重写以下三个方法。
- void ComputeSelection(const Handle(SelectMgr_Selection) &aSelection,const Standard_Integer aMode);
该方法根据不同的选择模式来创建选择集。一种aMode对于一个Selection.
我们所需要做的就是判断模式,并往aSelection中添加对象
void CMyBox::ComputeSelection(const Handle(SelectMgr_Selection) &aSelection,const Standard_Integer aMode)
{
switch(aMode)
{
case 0 ://0时将选择整个盒子
{
//看文档 由于0模式是选择整个盒子 所以所有的面将指向同一个EntityOwner
//也就是说EntityOwner将负责显示所有的面
Handle(SelectMgr_EntityOwner) anOwner = new SelectMgr_EntityOwner(this.0);
for(Standard_Integer i = 0;i< 6 ;i++)//一个盒子有六个面
{
TColgp_Array1OfPnt facePointsArray(1,4);//一个面由四个点组成
facePointsArray.SetValue(1,facePoints[i][0]);//下面依次添加面的四个点到集合中
facePointsArray.SetValue(2,facePoints[i][1]);//下面依次添加面的四个点到集合中
facePointsArray.SetValue(3,facePoints[i][2]);//下面依次添加面的四个点到集合中
facePointsArray.SetValue(4,facePoints[i][3]);//下面依次添加面的四个点到集合中
Handle(Select3D_SensitiveFace) hSensitiveFace = new Select3D_SensitiveFace(anOwner ,facePointArray,Select3D_TOS_BOUNDARY);
theSelection->Add(hSensitiveFace);
}
}break;
case 1 ://1时将分别选择每个面 这里高亮显示用第二种方式
{
//每个面都对应一个它自己的EntityOwner 这个EntityOwner负责高亮显示它自己。
//也就是创建一个i额临时的面覆盖到原来的面上
//具体实现代码这里不提供该方法的实现
this->SetAutoHilight(Standard_False);//这里设置为FALSE将自己显示自己 见下面 [6. 深入理解高亮显示](深入理解高亮显示)部分
for(Standard_Integer i= 0;i<6;i++)
{
Handle(SelectMgr_EntityOwner) anOwner = new SelectMgr_EntityOwner(this.i+1);//注意第二个参数priority这里用来标记是哪一个面
facePointsArray.SetValue(1,facePoints[i][0]);//下面依次添加面的四个点到集合中
facePointsArray.SetValue(2,facePoints[i][1]);//下面依次添加面的四个点到集合中
facePointsArray.SetValue(3,facePoints[i][2]);//下面依次添加面的四个点到集合中
facePointsArray.SetValue(4,facePoints[i][3]);//下面依次添加面的四个点到集合中
Handle(Select3D_SensitiveFace) hSensitiveFace = new Select3D_SensitiveFace(anOwner ,facePointArray,Select3D_TOS_BOUNDARY);
theSelection->Add(hSensitiveFace);
}
}break;
case 2://2时将选择每条边
{
//共有12条边 在选择边的模式中每一条边都是一个Sensitive Entity 都关联了一个自己的Entity Owner
//每一个EntityOwner负责高亮显示它自己
for(Standard_Integer i = 0;i< 12;i++)
{
gp_Pnt startPoint = this->_edge[i].StartPoint();//第i条边的起点
gp_Pnt endPoint = this->_edge[i].EndPoint();//第i条边的终点
Handle(CMyEdgeOwner) edgeOwner = new CMyEdgeOwner(this,startPoint ,endPoint ,2);
this->SetAutoHilight(Standard_True);//设置为自动高亮显示,
//当设置为Flase时将调用本类中用户重写的HilightOwnerWithColor 负责自己的高亮的显示。
//实例化Sensitive Entity
Handle(Select3D_SensitiveSegment) hSensitiveSegment = new Select3D_SensitiveSegment(edgeOwner ,startPoint ,endPoint );
aSelection->Add(hSensitiveSegment );//将SensitiveEntity添加到选择集
hSensitiveSegment ->BVH();
}
}break;
}
}
- void Compute(const Handle(PrsMgr_PresentationManager3d) & thePrsMgr,const Handle(Prs3d_Presentation) &thePrs,const standard_Integer presentMode = 0);
该方法将根据不同的显示模式presentMode 来显示对象。
所谓显示,就是在structure中中创建一个新的group,再往该gruop中添加不同的Graphic3d基本对象。
void CMyBox::Compute(const Handle(PrsMgr_PresentationManager3d) & thePrsMgr,const Handle(Prs3d_Presentation) &thePrs,const standard_Integer presentMode)
{
switch(presentMode)
{
case 0: //显示整个盒子
{
//代码省略
}break;
case 1://线框模式 显示各个边
{
thePrs->Clear();//清理Graphic3d_Structure中所有的Gruop
thePrs->SetDisplayPriority(1);//设置显示优先级为1
Standard_Integer maxVertexsCount = 8;//一个盒子8个顶点
Standard_Integer maxEdges= 24;//一个盒子12个边 一个边由连个点组成 所有有24个点组成边
Standard_Boolean hasEdgeColor = Standard_True;
//创建爱你边的数组
Handle(Graphic3d_ArrayOfSegments) anSegmentArray = new Graphic3d_ArrayOfSegments(maxVertexsCount,maxEdges,hasEdgeColor);
//下面就是一系列网anSegmentArray 中添加点和边 具体参考文档 有多种方法 为节约点的数量可以通过索引的方式创建边
for(Standard_Integer i=0;i<maxVertexsCount;i++)//总共只有八个点
{
anSegmentArray->AddVertex(vertexs[i],i*20);//由于hasEdgeColor 设置为True所有这里要给颜色 否则就不用给
}
//下面就是通过索引点来添加12个面
//以下只事例 只添加两条边注意 添加后点的索引是从1开始的
anSegmentArray->AddEdge(1); //第一条边
anSegmentArray->AddEdge(2);
anSegmentArray->AddEdge(2); //第二条边
anSegmentArray->AddEdge(6);
...//这里省略其余10条边的设置
//下面设置边的颜色属性
Handle(Graphic3d_AspectLine3d) lineAspece = new Graphic3d_AspectLine3d(Quantity_NOC_WHITE,Aspect_TOL_SOLID,1);
//下面创建一个新的gruop
Handle(Graphic3d_Group) hEdgeGroup = Prs3d_Root::CrurentGroup(thePrs);
//往group中添加对象
hEdgeGroup->AddPrimitiveArray(anSegmentArray);
hEdgeGroup->SetGruopPrimitivesAspect(lineAspect);//设置线的属性
}break;
case 2: //显示所有的顶点
{
thePrs->Clear();//清理Graphic3d_Structure中所有的Gruop
thePrs->SetDisplayPriority(2);//设置显示优先级为2
Standard_Integer maxVertexsCount = 8;//一个盒子8个顶点
Handle(Graphic3d_ArrayOfPoints) anPointsArray = new Graphic3d_ArrayOfPoints(maxVertexsCount);
for(Standard_Integer i=0;i<maxVertexsCount;i++)//总共只有八个点
{
anPointsArray ->AddVertex(vertexs[i]);
}
//下面创建一个新的gruop
Handle(Graphic3d_Group) hGroup2 = Prs3d_Root::CrurentGroup(thePrs);
//往group中添加对象
hGroup2 ->AddPrimitiveArray(anPointsArray );
myDrawer->PointAspect()->Aspect()->SetType(Aspect_TypeOfMarker::Aspect_TOM_BALL);
hEdgeGroup->SetGruopPrimitivesAspect(myDrawer->PointAspect()->Aspect());//设置点的属性
}break;
}
}
6. 深入理解高亮显示
- 由以上过程可知,当我们使用AutoHighlight时(this->SetAutoHilight(Standard_True)时),表示设置该对象为自动高亮显示,这时就要创建新的EntityOwner, 而EntityOwner又要负责创建一个新的显示对象来覆盖在原对象表面 从而实现的所谓的高亮显示。
- 这种过程相当冗余,而且不高效。
- OCCT提供了另外一种模式,由我们自己的对象来显示自己 而不是通过EntityOwner.
此时有两个步骤:
- 设置 AutoHilight属性为False
this->SetAutoHilight(Standard_False);- 重写 IsAutoHilight()、HilightOwnerWithColor()、HilightSelected()、ClearSelected()方法
- 在重写HilightOwnerWithColor时,该函数的参数并没有直接提供Prs3d_Presentatioin。但是可以通过其内置的方法GetHilightPresentation(const Handle(PrsMgr_PresentationManager3d)& thePM)来获取该显示Structure。代码如下:
void CMyBox::HilightOwnerWithColor(onst Handle(PrsMgr_PresentationManager3d)& thePM,const Handle(Prs3d_Drawer)& theStyle,const Handle(SelectMgr_EntityOwner)&theOwner)
{
//获取用于高亮显示的Graphic3d_Structure
Handle(Prs3d_Presentation) highlightPresentation = GetHilightPresentation(thePM);
if(HasPresentation())
{
highlightPresentation->SetTransformPersistence(Presentation()->TransformPersistence());
}
if(theOwner.IsNull())
{
return;
}
highlightPresentation->Clear();//先清除
//下面就是获取Group,往Group中添加基本图像对象了
Handle(Graphic3d_Group) aHilightGroup = Prs3d_Root::CurrentGroup(highlightPresentation );//获取Graphic3d_Group
Handle(Graphic3d_AspectFillArea3d) aTrangleAspect = new Graphic3d_AspectFillArea3d(...)//设置面的填充样式
//由于是绘制面 所以往Gruop中添加Graphic3d_ArrayOfTrangles对象
//对于选择的是哪一个面可以通过theOwner的Priority属性来判断
//theOwner的Priority属性在ComputeSelection中设置
switch(theOwner->Priority())//通过Priority来判断是哪一个面
{
case 1 ://表示前面
{
Handle(Graphic3d_ArrayOfTrangles) myFrontFace = ...//这里表示需要生成面
...//实例化myFrontFace 可以定义为一个字段
aHilightGroup ->AddPrimitiveArray(myFrontFace);
aHilightGroup ->SetGroupPrimitivesAspect(aTrangleAspect );
break;
}
case 2 ://表示后面
...//同上补全
break;
...
case 6://表示底面
...//同上补全
break;
}
highlightPresentation ->SetIsForHighlight(Standard_True);
if(thePM->IsImmediateModeOn())
{
thePM->AddToImmediateList(highlightPresentation );
}
}
网友评论