欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

深入了解 Open CASCADE 中的 SelectMgr_EntityOwner 和高亮功能

最编程 2024-04-27 10:05:16
...

@版权声明:本文为版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出,
本文链接https://www.jianshu.com/p/4b67596f5ac0
如有问题, 可邮件(yumxuanyi@qq.com)咨询。


关键字:OpenCascade、SelectMgr_EntityOwner、Sensitive Entity

Entity Owner

Entity Owner对象中存储了

  1. SelectMgr_SelectableObject *mySelectable; //表示可选择对象的指针 也就是说EntityOwner连接着该可选对象
  2. 当前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
来创建自己的可交互对象。
我们创建的可交互对象必须重写以下三个方法。

  1. 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;
 }
}
  1. 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. 深入理解高亮显示

  1. 由以上过程可知,当我们使用AutoHighlight时(this->SetAutoHilight(Standard_True)时),表示设置该对象为自动高亮显示,这时就要创建新的EntityOwner, 而EntityOwner又要负责创建一个新的显示对象来覆盖在原对象表面 从而实现的所谓的高亮显示。
  2. 这种过程相当冗余,而且不高效。
  3. OCCT提供了另外一种模式,由我们自己的对象来显示自己 而不是通过EntityOwner.
    此时有两个步骤:
  1. 设置 AutoHilight属性为False
    this->SetAutoHilight(Standard_False);
  2. 重写 IsAutoHilight()、HilightOwnerWithColor()、HilightSelected()、ClearSelected()方法
  1. 在重写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 );
   }
}

推荐阅读