Open CASCADE 教程 – AIS:自定义呈现

文章目录

  • 开始 (Getting Started)
  • 呈现构建器 (Presentation builders)
  • 基元数组 (Primitive arrays)
  • 基元外观 (Primitive aspects)
  • 二次构建器 (Quadric builders)
  • 计算选择 (Computing selection)
  • 突出显示选择所有者 (Highlighting selection owner)
  • 突出显示的方法 (Highlighting approaches)
  • 鼠标点击 (Mouse click)
  • 最终结果 (Final result)

这篇文章来源于 dev.opencascade.org,使用 Microsoft Edge 翻译,原文 Open CASCADE Technology - AIS: Custom Presentation

开始 (Getting Started)

OCCT 为快速应用程序开发提供了一组强大的内置交互对象,但应用程序交互服务(Application Interactive Services) (AIS) 的真正能力和灵活性可以通过子类化和实现自定义呈现来揭示。在本教程中,我们将重点介绍自定义 AIS_InteractiveObject 的开发,并逐步展示基础知识。

让我们从头开始,尝试子类化 AIS_InteractiveObject 对象:

class MyAisObject : public AIS_InteractiveObject
{
  DEFINE_STANDARD_RTTI_INLINE(MyAisObject, AIS_InteractiveObject)
public:
  MyAisObject() {}
public:
  virtual void Compute (const Handle(PrsMgr_PresentationManager)& thePrsMgr,
                        const Handle(Prs3d_Presentation)& thePrs,
                        const Standard_Integer theMode) override {}
  virtual void ComputeSelection (const Handle(SelectMgr_Selection)& theSel,
                                 const Standard_Integer theMode) override {}
  virtual bool AcceptDisplayMode (const Standard_Integer theMode) const override
  { return true; }
};

DEFINE_STANDARD_RTTI_INLINE() 宏将在 OCCT 运行时类型信息(Run-Time Type Information) (RTTI)系统中注册新类。这个步骤是可选的(如果你不打算在应用程序代码中使用像 Standard_Transient::DynamicType() 这样的方法,则可以跳过它),但这是子类化 OCCT 类时的常见做法。

AIS_InteractiveObject 接口仅定义了两个纯虚方法 - ::Compute() 定义对象呈现、 ::ComputeSelection() 定义可选择(可拾取)体(volume)。选择(Selection)和呈现(presentation)是 AIS 中的两个独立机制。呈现渲染是在 OpenGL 或类似的低级图形库的帮助下完成的,而选择(selection)完全不依赖于图形驱动程序。提供这两个方法的空实现足以将对象添加到 AIS_InteractiveContext (::Display()),但显然屏幕上不会显示任何内容。

呈现构建器 (Presentation builders)

为了继续,我们需要定义我们的对象的一些呈现。OCCT 为箭头、形状、方框等常见元素提供了一套呈现构建工具。这些工具可以在 Prs3d、StdPrs 和 DsgPrs 包中找到:

  • Prs3d 为简单的几何元素提供了构建器。
    • Prs3d_Arrow, Prs3d_BndBox, Prs3d_Point, Prs3d_Text, Prs3d_ToolCylinder, Prs3d_ToolDisk, Prs3d_ToolSector, Prs3d_ToolSphere, Prs3d_ToolTorus
  • StdPrs 为解析几何和 B-Rep 形状(TopoDS_Shape)提供了构建器。
    • StdPrs_WFShape, StdPrs_ShadedShape, StdPrs_BRepTextBuilder
  • DsgPrs 提供基准(datums)、尺寸和关系的构建器。

呈现构建器是用于构建 AIS 对象的可重用砖块(bricks)。标准 OCCT 交互对象高度依赖它们,因此你只需几行调用 StdPrs_ShadedShape 即可轻松复制 AIS_Shape 呈现用于显示形状:

void MyAisObject::Compute (const Handle(PrsMgr_PresentationManager)& thePrsMgr,
                           const Handle(Prs3d_Presentation)& thePrs,
                           const Standard_Integer theMode)
{
  TopoDS_Shape aShape = BRepPrimAPI_MakeCylinder (100.0, 100.0);
  StdPrs_ShadedShape::Add (thePrs, aShape, myDrawer);
}
...
Handle(AIS_InteractiveContext) theCtx;
Handle(MyAisObject) aPrs = new MyAisObject();
theCtx->Display (aPrs, true);

在这里插入图片描述

StdPrs_ShadedShape 呈现构建器
 

PrsMgr_PresentableObject::Compute() 方法采用三个参数:

  • Presentation Manager (PrsMgr_PresentationManager) 很少使用的参数,但对于某些高级用例可能是必需的。
  • Presentation (Prs3d_Presentation 或 Graphic3d_Structure) 定义要用呈现元素填充的结构。
  • Display Mode(整数)。指定要计算的显示模式。0 是默认显示模式,如果未被 AIS_InteractiveObject::SetDisplayMode()AIS_InteractiveContext::Display() 覆盖。

对于每种受支持的显示模式,呈现管理器(Presentation Manager) 都会创建一个专用的 Prs3d_Presentation,并将其作为呈现列表 PrsMgr_PresentableObject::Presentations() 存储在对象本身中。最好在 ::Compute() 方法中拒绝不支持的显示模式:

void MyAisObject::Compute (const Handle(PrsMgr_PresentationManager)& thePrsMgr,
                           const Handle(Prs3d_Presentation)& thePrs,
                           const Standard_Integer theMode)
{
  if (theMode != 0) { return; } // reject non-zero display modes
  TopoDS_Shape aShape = BRepPrimAPI_MakeCylinder (100.0, 100.0);
  StdPrs_ShadedShape::Add (thePrs, aShape, myDrawer);
}

但是,这不会阻止应用程序使用另一种显示模式显示对象,如下所示:

Handle(AIS_InteractiveContext) theCtx;
Handle(MyAisObject) aPrs = new MyAisObject();
theCtx->Display (aPrs, 100, -1, true);

上面的代码将在显示模式等于 100 的情况下显示 MyAisObject,在 ::Compute() 修改后屏幕上不会显示任何内容。AIS 仍将创建具有指定显示模式的呈现,但它将是空的 - 方法 ::AcceptDisplayMode() 可以被覆盖以禁止创建空呈现:

bool MyAisObject::AcceptDisplayMode (const Standard_Integer theMode) const
{
  return theMode == 0; // reject non-zero display modes
}

AIS_InteractiveContext::Display() 检查对象是否实际支持请求的显示模式,如果不支持,则使用默认显示模式(0)。StdPrs_ShadedShape 准备形状的着色(三角剖分)呈现,同时 StdPrs_WFShape 创建具有 B-Rep 线边界的线框呈现:

void MyAisObject::Compute (const Handle(PrsMgr_PresentationManager)& thePrsMgr,
                           const Handle(Prs3d_Presentation)& thePrs,
                           const Standard_Integer theMode)
{
  if (!AcceptDisplayMode (theMode)) { return; }
  TopoDS_Shape aShape = BRepPrimAPI_MakeCylinder (100.0, 100.0);
  StdPrs_ShadedShape::Add (thePrs, aShape, myDrawer); // add shading
  StdPrs_WFShape::Add     (thePrs, aShape, myDrawer); // add wireframe
}

在这里插入图片描述

StdPrs_ShadedShape + StdPrs_WFShape 呈现构建器的结果
 

呈现构建器使用 Prs3d_Drawer 对象定义各种属性 - 阴影形状的材料、线框模式下的等值线数、曲面细分质量、线颜色等等。PrsMgr_PresentableObject 使用默认属性定义 myDrawer 属性。StdPrs 使显示拓扑形状变得容易。借助 Prs3d 工具,我们可以显示箭头、框或文本标签等元素。让我们用第二个 display mode 1 来扩展我们的呈现,使用 Prs3d_BndBox 构建器显示边界框:

bool MyAisObject::AcceptDisplayMode (const Standard_Integer theMode) const
{
  return theMode == 0 || theMode == 1;
}
void MyAisObject::Compute (const Handle(PrsMgr_PresentationManager)& thePrsMgr,
                           const Handle(Prs3d_Presentation)& thePrs,
                           const Standard_Integer theMode)
{
  TopoDS_Shape aShape = BRepPrimAPI_MakeCylinder (100.0, 100.0);
  if (theMode == 0)
  {
    StdPrs_ShadedShape::Add (thePrs, aShape, myDrawer);
    StdPrs_WFShape::Add (thePrs, aShape, myDrawer); // add wireframe
  }
  else if (theMode == 1)
  {
    Bnd_Box aBox;
    BRepBndLib::Add (aShape, aBox);
    Prs3d_BndBox::Add (thePrs, aBox, myDrawer);
  }
}

现在,使用 display mode 1 显示一个对象,将显示一个框:

Handle(AIS_InteractiveContext) theCtx;
Handle(MyAisObject) aPrs = new MyAisObject();
theCtx->Display (aPrs, 1, 0, true);

在这里插入图片描述

Prs3d_BndBox 呈现构建器
 

AIS 不允许同时激活多个显示模式,因此这些呈现模式将相互替代。但 AIS 可以使用非活动显示模式来突出显示 - 例如在所选择 AIS_Shape 对象的阴影 (AIS_Shaded) 呈现之上显示线框 (AIS_WireFrame) 呈现。

让我们为我们的交互对象支持的显示模式定义一个专用枚举,并设置第一个 (MyDispMode_Highlight) 显示模式,用于在 PrsMgr_PresentableObject::SetHilightMode() 的帮助下进行突出显示:

class MyAisObject : public AIS_InteractiveObject
{
public:
  enum MyDispMode { MyDispMode_Main = 0, MyDispMode_Highlight = 1 };
...
MyAisObject::MyAisObject()
{
  SetDisplayMode (MyDispMode_Main);      // main (active) display mode
  SetHilightMode (MyDispMode_Highlight); // auxiliary (highlighting) mode
}
...
Handle(AIS_InteractiveContext) theCtx;
Handle(MyAisObject) aPrs = new MyAisObject();
theCtx->Display (aPrs, MyAisObject::MyDispMode_Main, 0, false);
theCtx->HilightWithColor (aPrs, aPrs->HilightAttributes(), false);
theCtx->CurrentViewer()->Redraw();

在这里插入图片描述

按颜色突出显示(左),按其他显示模式突出显示(右)
 

在这个特定的用例中,我们使用了方法 AIS_InteractiveContext::HilightWithColor() 代替 ::SetSelected() - 仅是因为我们的对象还不可选择且 ::SetSelected() 不起作用。高亮呈现以调制过的颜色显示在屏幕上(请参阅上面的左侧屏幕截图)。使用专用显示模式进行突出显示(上面的右侧屏幕截图)允许自定义选定/突出显示状态下的呈现。

基元数组 (Primitive arrays)

Prs3d_Presentation 可以由以下 基元(primitives) 填充:

  • Triangles (三角形)
    • Graphic3d_ArrayOfTriangles
    • Graphic3d_ArrayOfTriangleFans
    • Graphic3d_ArrayOfTriangleStrips
  • Lines (线)
    • Graphic3d_ArrayOfSegments
    • Graphic3d_ArrayOfPolylines
  • Points or Markers (点或标记)
    • Graphic3d_ArrayOfPoints

这个基元的三元组(triplet)是图形硬件能够呈现的,因此它可以以顶点缓冲对象(Vertex Buffer Objects)(VBO)的形式直接传输到低级图形库。每个 基元数组(primitive array) 都由顶点属性数组(位置(position)法线(normal)纹理坐标(texture coordinates)顶点颜色(vertex colors) 等)和可选的 索引数组(array of indices) 组成。后者避免了在属性数组中连接元素(三角形、折线)之间共享的重复顶点。

Graphic3d_ArrayOfPrimitives 和它的子类为填充基元数组提供了一个方便的接口:

  • 构造函数采用多个顶点、边数(索引)和可选顶点属性的位掩码。
  • Graphic3d_ArrayOfPrimitives::AddVertex() 将具有指定属性的顶点追加到数组的末尾(在构造时指定的范围内)。
  • Graphic3d_ArrayOfPrimitives::AddEdges() 追加索引,从 1 开始。每个线段由两条连续边定义,每个三角形由三个连续边定义。

让我们扩展我们的样本并显示由索引线(indexed segments)数组定义的圆柱体截面轮廓(例如,四个顶点的折线):

void MyAisObject::Compute (const Handle(PrsMgr_PresentationManager)& thePrsMgr,
                           const Handle(Prs3d_Presentation)& thePrs,
                           const Standard_Integer theMode)
{
  const double aRadius = 100.0, aHeight = 100.0;
  TopoDS_Shape aShape = BRepPrimAPI_MakeCylinder (aRadius, aHeight);
  if (theMode == MyDispMode_Main)
  {
    StdPrs_ShadedShape::Add (thePrs, aShape, myDrawer);
    //StdPrs_WFShape::Add (thePrs, aShape, myDrawer);
    Handle(Graphic3d_ArrayOfSegments) aSegs = new Graphic3d_ArrayOfSegments (4, 4 * 2, Graphic3d_ArrayFlags_None);
    aSegs->AddVertex (gp_Pnt (0.0, -aRadius, 0.0));
    aSegs->AddVertex (gp_Pnt (0.0, -aRadius, aHeight));
    aSegs->AddVertex (gp_Pnt (0.0,  aRadius, aHeight));
    aSegs->AddVertex (gp_Pnt (0.0,  aRadius, 0.0));
    aSegs->AddEdges (1, 2);
    aSegs->AddEdges (2, 3);
    aSegs->AddEdges (3, 4);
    aSegs->AddEdges (4, 1);
    Handle(Graphic3d_Group) aGroupSegs = thePrs->NewGroup();
    aGroupSegs->SetGroupPrimitivesAspect (myDrawer->WireAspect()->Aspect());
    aGroupSegs->AddPrimitiveArray (aSegs);
  }
  else if (theMode == MyDispMode_Highlight) { ... }
}

在这里插入图片描述

显示 Graphic3d_ArrayOfSegments
 

这个过程非常简单:

  • 使用 Prs3d_Presentation::NewGroup() 创建一个新的 Graphic3d_Group;
  • 使用 Graphic3d_Group::SetGroupPrimitivesAspect() 指定呈现外观;
  • 使用 Graphic3d_Group::AddPrimitiveArray() 创建并添加基元数组。

像 StdPrs_ShadedShape / StdPrs_WFShape 这样的标准呈现构建器在内部执行完全相同的操作 - 形状的镶嵌表示以三角形(阴影)、线段(线框和自由边)和标记(自由形状顶点)的形式添加到呈现中。

单个 Graphic3d_Group 通常只定义一个基元数组,但从技术上讲,可以将更多数组添加到同一组Graphic3d_Group::AddPrimitiveArray() 并具有不同的外观 Graphic3d_Group::SetPrimitivesAspect(),这在高级方案中可能会考虑。

方法 Graphic3d_Group::AddText() 允许向呈现添加文本标签。在内部,文本标签使用从字体创建的纹理图集呈现为纹理三角形数组,但这种复杂的逻辑对用户是隐藏的。

基元外观 (Primitive aspects)

Graphic3d_Aspects 是一个定义基元数组 (Graphic3d_Group::SetGroupPrimitivesAspect()) 的 显示属性(display properties) 的类 - 材质(material)着色模型(shading model)颜色纹理贴图(texture maps)混合模式(blending mode)线宽(line width) 等。

还有一些子类 Graphic3d_AspectFillArea3d (三角形)、Graphic3d_AspectLine3d (线)、Graphic3d_AspectMarker3d (标记) 和 Graphic3d_AspectText3d (文本标签) 被定义为特定基元数组类型的专用化。这些子类的存在是出于历史原因,并且渲染器以完全相同的方式处理。

从技术上讲,可以直接在 ::Compute() 方法中创建临时外观,如下所示:

void MyAisObject::Compute (const Handle(PrsMgr_PresentationManager)& thePrsMgr,
                           const Handle(Prs3d_Presentation)& thePrs,
                           const Standard_Integer theMode)
{
  Handle(Graphic3d_Aspects) anAspects = new Graphic3d_Aspects();
  anAspects->SetShadingModel (Graphic3d_TypeOfShadingModel_Unlit);
  anAspects->SetColor (Quantity_NOC_RED);
  Handle(Graphic3d_Group) aGroup = thePrs->NewGroup();
  aGroup->SetGroupPrimitivesAspect (anAspects);
  ...
}

虽然该代码将按预期工作,但会阻止在不重新计算整个呈现的情况下进一步动态更新呈现外观。相反,最好从 PrsMgr_PresentableObject::myDrawer / ::Attributes() 获取属性或将自定义属性存储为类字段。Prs3d_Drawer 定义一组由 AIS 呈现构建器使用的属性,但自定义构建器也可以使用相同的参数。

它也是类构造函数中首选的预分配属性。这将允许在不重新计算整体呈现的情况下更改属性 - 只需在修改后调用 PrsMgr_PresentableObject::SynchronizeAspects() 即可。我们的自定义对象使用 myDrawer->ShadingAspect()myDrawer->WireAspect() 外观,因此让我们显式初始化它们 - 为线段分配用于阴影的银色材质和绿色:

MyAisObject::MyAisObject()
{
  SetHilightMode (MyDispMode_Highlight);
  myDrawer->SetupOwnShadingAspect();
  myDrawer->ShadingAspect()->SetMaterial (Graphic3d_NameOfMaterial_Silver);
  myDrawer->SetWireAspect (new Prs3d_LineAspect (Quantity_NOC_GREEN, Aspect_TOL_SOLID, 2.0));
}

二次构建器 (Quadric builders)

以前,我们使用 StdPrs_ShadedShape 显示圆柱体几何图形。Prs3d 软件包提供了一种基于 Prs3d_ToolQuadric 接口的显示几何图形(如圆柱体、球体和圆环)的更简单方法。这个接口允许绕过创建简单几何图形的复杂 B-Rep (TopoDS_Shape) 定义,并避免使用通用曲面细分器,如 BRepMesh。

对于少数此类对象,这种差异可以忽略不计,但对于大量此类对象,这种差异可能会变得相当大。有效圆柱体的 B-Rep 定义包括 2 个唯一 TopoDS_Vertex、3 个 TopoDS_Edge、3 个 TopoDS_Wire、3 个 TopoDS_Face、1 个 TopoDS_Shell 和 1 个 TopoDS_Solid。在内部,每个 TopoDS_Edge 还定义了曲线(Geom_Curve 以及二维参数化 Geom2d_Curve),并且每个 TopoDS_Face 定义了分析曲面(Geom_Surface)。在 BRepMesh 的帮助下划分这种几何体比人们想象的要复杂得多。大量的数据结构(内存!)和计算(时间!),用于显示可以通过简单的 for 循环进行三角剖分的几何图形。

Prs3d_ToolQuadric 通过以直接的方式为此类形状创建三角剖分(triangulation)来解决这个问题。让我们尝试在示例中使用 Prs3d_ToolCylinder:

void MyAisObject::Compute (const Handle(PrsMgr_PresentationManager)& thePrsMgr,
                           const Handle(Prs3d_Presentation)& thePrs,
                           const Standard_Integer theMode)
{
  const double aRadius = 100.0, aHeight = 100.0;
  TopoDS_Shape aShape = BRepPrimAPI_MakeCylinder (aRadius, aHeight);
  if (theMode == MyDispMode_Main)
  {
    //StdPrs_ShadedShape::Add (thePrs, aShape, myDrawer); // add shading
    //StdPrs_WFShape::Add (thePrs, aShape, myDrawer); // add wireframe
    Handle(Graphic3d_ArrayOfTriangles) aTris =
     Prs3d_ToolCylinder::Create (aRadius, aRadius, aHeight, 10, 10, gp_Trsf());
    Handle(Graphic3d_Group) aGroupTris = thePrs->NewGroup();
    aGroupTris->SetGroupPrimitivesAspect (myDrawer->ShadingAspect()->Aspect());
    aGroupTris->AddPrimitiveArray (aTris);
    ...
  }
  ...
}

在这里插入图片描述

Prs3d_ToolCylinder(10片)
 

好吧… 这看起来有点前卫。二次构建器使用以下参数创建三角剖分:

  • 几何参数。(如果是圆柱体 - 底部半径、顶部半径和高度)。
  • 沿 U(切片(slices))和 V(堆栈(stacks))参数的细分数。在某些情况下,只有一个参数范围很重要。
  • 要应用的变换 gp_Trsf(原始几何在某个参考坐标系中定义)。

让我们将细分的数量从 10 个增加到 25 个:

Handle(Graphic3d_ArrayOfTriangles) aTris =
  Prs3d_ToolCylinder::Create (aRadius, aRadius, aHeight, 25, 25, gp_Trsf());

在这里插入图片描述

Prs3d_ToolCylinder(25片)
 

现在看起来好多了!请注意,Prs3d_ToolCylinder 根据顶部/底部半径定义,可用于构建圆锥体和圆柱体。

不过有一个问题 - 我们的圆柱体不再有顶部和底部了!为了解决这个问题,我们将使用另一个二次构建器 Prs3d_ToolDisk:

void MyAisObject::Compute (const Handle(PrsMgr_PresentationManager)& thePrsMgr,
                           const Handle(Prs3d_Presentation)& thePrs,
                           const Standard_Integer theMode)
{
  const double aRadius = 100.0, aHeight = 100.0;
  if (theMode == MyDispMode_Main)
  {
    Prs3d_ToolCylinder aCyl (aRadius, aRadius, aHeight, 25, 25);
    Prs3d_ToolDisk aDisk (0.0, aRadius, 25, 1);
    Handle(Graphic3d_ArrayOfTriangles) aTris =
      new Graphic3d_ArrayOfTriangles (aCyl.VerticesNb() + 2 * aDisk.VerticesNb(),
                                      3 * (aCyl.TrianglesNb() + 2 * aDisk.TrianglesNb()),
                                      Graphic3d_ArrayFlags_VertexNormal);
    aCyl .FillArray (aTris, gp_Trsf());
    aDisk.FillArray (aTris, gp_Trsf());
    gp_Trsf aDisk2Trsf;
    aDisk2Trsf.SetTransformation (gp_Ax3 (gp_Pnt (0.0, 0.0, aHeight), -gp::DZ(), gp::DX()), gp::XOY());
    aDisk.FillArray (aTris, aDisk2Trsf);
    Handle(Graphic3d_Group) aGroupTris = thePrs->NewGroup();
    aGroupTris->SetGroupPrimitivesAspect (myDrawer->ShadingAspect()->Aspect());
    aGroupTris->AddPrimitiveArray (aTris);
    aGroupTris->SetClosed (true);
    ...
  }
}

现在我们的圆柱体看起来是实心的了!上面的示例将两个三角剖分合并为一个三角剖分,而不是单独追加每个基元数组。

这看起来是一个微小的差异,但在大型场景中,它可能会对性能产生巨大影响(dramatic impact on performance),因为每个 Graphic3d_ArrayOfPrimitives 都映射到图形驱动程序(OpenGL)级别的专用绘制调用中。

在这里插入图片描述

Prs3d_ToolCylinder + Prs3d_ToolDisk
 

作为练习,让我们尝试在没有 Prs3d_ToolDisk 构建器帮助的情况下计算圆柱盘的三角剖分:

void MyAisObject::Compute (const Handle(PrsMgr_PresentationManager)& thePrsMgr,
                           const Handle(Prs3d_Presentation)& thePrs,
                           const Standard_Integer theMode)
{
  const double aRadius = 100.0, aHeight = 100.0;
  if (theMode == MyDispMode_Main)
  {
    const int aNbSlices = 25;
    Prs3d_ToolCylinder aCyl (aRadius, aRadius, aHeight, aNbSlices, aNbSlices);
    Handle(Graphic3d_ArrayOfTriangles) aTris =
      new Graphic3d_ArrayOfTriangles (aCyl.VerticesNb(),
                                      3 * (aCyl.TrianglesNb()),
                                      Graphic3d_ArrayFlags_VertexNormal);
    aCyl.FillArray (aTris, gp_Trsf());
    Handle(Graphic3d_ArrayOfTriangles) aTris2 =
      new Graphic3d_ArrayOfTriangles (aNbSlices + 1, aNbSlices * 3, Graphic3d_ArrayFlags_VertexNormal);
    aTris2->AddVertex (gp_Pnt (0.0, 0.0, aHeight), -gp::DZ());
    for (int aSliceIter = 0; aSliceIter < aNbSlices; ++aSliceIter)
    {
      double anAngle = M_PI * 2.0 * double(aSliceIter) / double(aNbSlices);
      aTris2->AddVertex (gp_Pnt (Cos (anAngle) * aRadius, Sin (anAngle) * aRadius, aHeight), -gp::DZ());
    }
    for (int aSliceIter = 0; aSliceIter < aNbSlices; ++aSliceIter)
    {
      aTris2->AddEdges (1, aSliceIter + 2, aSliceIter + 1 < aNbSlices ? (aSliceIter + 3) : 2);
    }
    Handle(Graphic3d_Group) aGroupTris = thePrs->NewGroup();
    aGroupTris->SetGroupPrimitivesAspect (myDrawer->ShadingAspect()->Aspect());
    aGroupTris->AddPrimitiveArray (aTris);
    aGroupTris->AddPrimitiveArray (aTris2);
    ...
  }
}

在这里插入图片描述

手动三角剖分圆盘
 

圆盘在这里,但它有一种奇怪的颜色——就像它不受照明影响一样。当顶点法线定义不正确时,就会发生这种情况。在我们的例子中,我们将盘法线定义为 -DZ(参见 Graphic3d_ArrayOfTriangles::AddVertex() 的第二个参数),但法线方向也应该与三角剖分绕组(winding)规则对齐。图形驱动程序使用三角形节点的顺时针顺序定义三角形的正面,并且应该为三角形的正面定义法线 - 例如,在我们的例子中应该是 gp::DZ()。反转顶点法线方向后,圆柱体看起来与使用 Prs3d_ToolDisk 时完全相同。

正面/背面方向可能会根据 Graphic3d_Aspects::SetDistinguish() 标志和 ::FrontMaterial() / ::BackMaterial() 设置使用不同的材料显示。

计算选择 (Computing selection)

在本教程的第一部分,我们通过实现 PrsMgr_PresentableObject::Compute() 接口创建了一个自定义 AIS 对象 MyAisObject 计算呈现(presentation)。在这一部分中,我们将使用交互功能扩展我们的对象,并通过实现 SelectMgr_SelectableObject 接口使其可选择。

让我们做第一步,把一些逻辑放到 ::ComputeSelection() 方法中。此方法应使用定义可选择元素(三角剖分、折线、点及其组合)的 SelectMgr_SensitiveEntity 实体填充 SelectMgr_Selection 参数。Select3D_SensitiveBox 可能是定义可选择体(volume)的最简单方法 - 通过它的边界框:

void MyAisObject::ComputeSelection (const Handle(SelectMgr_Selection)& theSel,
                                    const Standard_Integer theMode)
{
  const double aRadius = 100.0, aHeight = 100.0;
  TopoDS_Shape aShape = BRepPrimAPI_MakeCylinder (aRadius, aHeight);
  Bnd_Box aBox;
  BRepBndLib::Add (aShape, aBox);
  Handle(SelectMgr_EntityOwner) anOwner = new SelectMgr_EntityOwner (this);
  Handle(Select3D_SensitiveBox) aSensBox = new Select3D_SensitiveBox (anOwner, aBox);
  theSel->Add (aSensBox);
}

SelectMgr_EntityOwner 是选择逻辑中的一个关键对象 - 它用作可拾取对象或其部件的标识符。你可以在 AIS_InteractiveContext::DetectedOwner() 这样的方法中看到这个对象,所有者(Owners)存储在选择对象 AIS_Selection 列表中,它被 AIS_InteractiveContext::SetSelected()AIS_InteractiveContext::AddOrRemoveSelected() 这样的方法接收。从选择器的角度来看,AIS_InteractiveObject 只是 SelectMgr_EntityOwner 的一个 drawer。

第 0 种选择模式(0th selection mode)通常定义整个对象的单个 Owner。为了使一个复合对象作为一个整体进行选择,我们根据需要向 Selection 添加引用同一所有者的 SensitiveEntity。乍一看可能令人困惑,SelectMgr_SensitiveEntity 将 SelectMgr_EntityOwner 存储为类字段,而不是以相反的方式存储(SelectMgr_EntityOwner 不存储定义其拾取体(picking volume)的 SelectMgr_SensitiveEntity 列表)。

对于局部选择(对象部件的选择),我们为每个部件创建单独的所有者,并以相同的方式将 SensitiveEntity 添加到选择中。所有者可以存储一个额外的标识符作为类字段,例如 StdSelect_BRepOwner 将 TopoDS_Shape 存储为带有 AIS_Shape 对象的选取子形状的标识符。

就像 StdPrs_ShadedShape 是 TopoDS_Shape 的呈现构建器(presentation builder)一样,StdSelect_BRepSelectionTool 可以看作是形状的标准选择构建器(selection builder):

void MyAisObject::ComputeSelection (const Handle(SelectMgr_Selection)& theSel,
                                    const Standard_Integer theMode)
{
  const double aRadius = 100.0, aHeight = 100.0;
  TopoDS_Shape aShape = BRepPrimAPI_MakeCylinder (aRadius, aHeight);
  Standard_Real aDefl = StdPrs_ToolTriangulatedShape::GetDeflection (aShape, myDrawer);
  StdSelect_BRepSelectionTool::Load (theSel, this, aShape, TopAbs_SHAPE, aDefl,
                                     myDrawer->DeviationAngle(),
                                     myDrawer->IsAutoTriangulation());
}

在内部,StdSelect_BRepSelectionTool 遍历子形状并将 Select3D_SensitiveTriangulation (对于面) 和 Select3D_SensitiveCurve (对于边) 等实体追加到 Selection (theSel) 中。

之前,我们已经使用 Prs3d_ToolCylinder 对圆柱体进行了三角剖分,所以让我们尝试从相同的三角剖分中构建 Select3D_SensitivePrimitiveArray:

void MyAisObject::ComputeSelection (const Handle(SelectMgr_Selection)& theSel,
                                    const Standard_Integer theMode)
{
  const double aRadius = 100.0, aHeight = 100.0;
  Handle(SelectMgr_EntityOwner) anOwner = new SelectMgr_EntityOwner (this);
  Handle(Graphic3d_ArrayOfTriangles) aTris =
    Prs3d_ToolCylinder::Create (aRadius, aRadius, aHeight, 25, 25, gp_Trsf());
  Handle(Select3D_SensitivePrimitiveArray) aSensTri =
    new Select3D_SensitivePrimitiveArray (anOwner);
  aSensTri->InitTriangulation (aTris->Attributes(), aTris->Indices(),
                               TopLoc_Location());
  theSel->Add (aSensTri);
}

选择的计算独立于呈现,因此它们不必相互匹配。但是,呈现和选择之间的不一致可能会让用户感到困惑,因为他将无法选择鼠标光标下清晰显示的对象。例如,当选择(selection)使用使用不同参数(不同数量的细分,或不同的偏转参数)计算的相同几何图形的镶嵌表示时,可能会发生这些问题。

::Compute() 的情况一样,定义一些特定对象支持的选择模式(selection modes)枚举并拒绝不支持的选择模式以避免意外行为是有意义的:

void MyAisObject::ComputeSelection (const Handle(SelectMgr_Selection)& theSel,
                                    const Standard_Integer theMode)
{
  if (theMode != 0) { return; }
  ...
}

与显示模式不同,AIS_InteractiveContext 允许激活选择模式的任意组合。用户应该小心地只激活那些真正有意义并且可以一起工作的模式。

可以在显示对象时指定要激活的选择模式(传递 -1 而不是 0 将显示一个未激活选择的对象):

Handle(AIS_InteractiveContext) theCtx;
Handle(MyAisObject) aPrs = new MyAisObject();
theCtx->Display (aPrs, MyAisObject::MyDispMode_Main, 0, false);

之后,AIS_InteractiveContext::SetSelectionModeActive(),或者它的包装器 AIS_InteractiveContext::Activate()AIS_InteractiveContext::Deactivate(),可以用于逐个启用或禁用所需的选择模式。

突出显示选择所有者 (Highlighting selection owner)

正如上一节所提到的,SelectMgr_EntityOwner 是一个关键对象,可以用作可选择部件的标识符。当然,您可能希望将其子类化,以放置一些特定于应用程序的 id,用于标识所选择部件。但是你可以用 Owner 类做更多的事情,比如定制高亮显示。

让我们从头开始定义一个自定义 Owner 类:

class MyAisOwner : public SelectMgr_EntityOwner
{
  DEFINE_STANDARD_RTTI_INLINE(MyAisOwner, SelectMgr_EntityOwner)
public:
  MyAisOwner (const Handle(MyAisObject)& theObj, int thePriority = 0)
  : SelectMgr_EntityOwner (theObj, thePriority) {}
  virtual void HilightWithColor (const Handle(PrsMgr_PresentationManager)& thePrsMgr,
                                 const Handle(Prs3d_Drawer)& theStyle,
                                 const Standard_Integer theMode) override
  { base_type::HilightWithColor (thePrsMgr, theStyle, theMode); }
  virtual void Unhilight (const Handle(PrsMgr_PresentationManager)& thePrsMgr,
                          const Standard_Integer theMode) override
  { base_type::Unhilight  (thePrsMgr, theMode); }
protected:
  Handle(Prs3d_Presentation) myPrs;
};

SelectMgr_EntityOwner 不定义任何纯虚方法,可以直接实例化,就像上面的 MyAisObject::ComputeSelection() 实现一样。让我们恢复使用专用的显示模式来高亮显示(在 MyAisObject 构造函数中删除 SetHilightMode()),并在 ::ComputeSelection() 中使用我们的新类 MyAisOwner

MyAisObject::MyAisObject()
{
  //SetHilightMode (MyDispMode_Highlight);
  myDrawer->SetupOwnShadingAspect();
  ...
}
void MyAisObject::ComputeSelection (const Handle(SelectMgr_Selection)& theSel,
                                    const Standard_Integer theMode)
{
  const double aRadius = 100.0, aHeight = 100.0;
  Handle(MyAisOwner) anOwner = new MyAisOwner (this);
  ...
}

创建敏感实体和填充 Selection 的进一步逻辑可以保持原样。用 MyAisOwner 替换 SelectMgr_EntityOwner 当前不会改变行为,我们通过颜色调制看到整个对象的高亮。这是因为用于突出显示逻辑的 SelectMgr_EntityOwner 的默认实现如下所示(简化):

void SelectMgr_EntityOwner::HilightWithColor (
  const Handle(PrsMgr_PresentationManager)& thePrsMgr,
  const Handle(Prs3d_Drawer)& theStyle,
  const Standard_Integer theMode)
{
  const Graphic3d_ZLayerId aHiLayer =
      theStyle->ZLayer() != Graphic3d_ZLayerId_UNKNOWN
    ? theStyle->ZLayer()
    : mySelectable->ZLayer();
  thePrsMgr->Color (mySelectable, theStyle, theMode, NULL, aHiLayer);
}

在这里插入图片描述

SelectMgr_EntityOwner::HilightWithColor() 的默认行为。
 

现在,让我们重写 SelectMgr_EntityOwner::HilightWithColor() 方法并显示一个边界框表示:

void MyAisOwner::HilightWithColor (const Handle(PrsMgr_PresentationManager)& thePrsMgr,
                                   const Handle(Prs3d_Drawer)& theStyle,
                                   const Standard_Integer theMode)
{
  if (myPrs.IsNull())
  {
    myPrs = new Prs3d_Presentation (thePrsMgr->StructureManager());
    MyAisObject* anObj = dynamic_cast<MyAisObject*> (mySelectable);
    anObj->Compute (thePrsMgr, myPrs, MyAisObject::MyDispMode_Highlight);
  }
  if (!thePrsMgr->IsImmediateModeOn())
  {
    myPrs->Display();
  }
}

SelectMgr_EntityOwner::HilightWithColor() 不从参数接收要填充的呈现;应该手动创建高亮呈现,甚至显式显示在屏幕上。为了避免代码重复,上面的代码重用了 MyAisObject::Compute() 已经实现了高亮呈现的计算。

在这里插入图片描述

自定义实现的结果 MyAisOwner::HilightWithColor()。
 

所选择对象的视觉结果看起来与我们使用专用高亮模式时完全相同。不过,有一件事坏了——即使清除了选择,高亮仍然显示。要解决这个问题,我们需要实现 SelectMgr_EntityOwner::Unhilight() 并显式隐藏我们的自定义呈现:

void MyAisOwner::Unhilight (const Handle(PrsMgr_PresentationManager)& thePrsMgr,
                            const Standard_Integer theMode)
{
  if (!myPrs.IsNull()) { myPrs->Erase(); }
}

另一个问题是对象不再动态突出显示。为了解决这个问题,我们需要专门处理 PrsMgr_PresentationManager::IsImmediateModeOn()。在此模式开启后,通过 PrsMgr_PresentationManager::AddToImmediateList() 方法在屏幕上显示呈现(鼠标下一次移动时将自动从屏幕上清除):

void MyAisOwner::HilightWithColor (const Handle(PrsMgr_PresentationManager)& thePrsMgr,
                                   const Handle(Prs3d_Drawer)& theStyle,
                                   const Standard_Integer theMode)
{
  if (myPrs.IsNull())
  {
    myPrs = new Prs3d_Presentation (thePrsMgr->StructureManager());
    MyAisObject* anObj = dynamic_cast<MyAisObject*> (mySelectable);
    anObj->Compute (thePrsMgr, myPrs, MyAisObject::MyDispMode_Highlight);
  }
  if (thePrsMgr->IsImmediateModeOn())
  {
    Handle(Prs3d_PresentationShadow) aShadow =
      new Prs3d_PresentationShadow (thePrsMgr->StructureManager(), myPrs);
    aShadow->SetZLayer (Graphic3d_ZLayerId_Top);
    aShadow->Highlight (theStyle);
    thePrsMgr->AddToImmediateList (aShadow);
  }
  else
  {
    myPrs->Display();
  }
}

我们可以创建两个专用的呈现用于动态突出显示,或者在瞬态对象 Prs3d_PresentationShadow 的帮助下重用现有的呈现用于两种情况。

让我们更进一步,让动态高亮更有趣一点 —— 通过在鼠标拾取对象的位置绘制一个表面法线:

void MyAisOwner::HilightWithColor (const Handle(PrsMgr_PresentationManager)& thePrsMgr,
                                   const Handle(Prs3d_Drawer)& theStyle,
                                   const Standard_Integer theMode)
{
  MyAisObject* anObj = dynamic_cast<MyAisObject*> (mySelectable);
  if (thePrsMgr->IsImmediateModeOn())
  {
    Handle(StdSelect_ViewerSelector) aSelector =
      anObj->InteractiveContext()->MainSelector();
    SelectMgr_SortCriterion aPickPnt;
    for (int aPickIter = 1; aPickIter <= aSelector->NbPicked(); ++aPickIter)
    {
      if (aSelector->Picked (aPickIter) == this)
      {
        aPickPnt = aSelector->PickedData (aPickIter);
        break;
      }
    }
    Handle(Prs3d_Presentation) aPrs = mySelectable->GetHilightPresentation (thePrsMgr);
    aPrs->SetZLayer (Graphic3d_ZLayerId_Top);
    aPrs->Clear();
    Handle(Graphic3d_Group) aGroup = aPrs->NewGroup();
    aGroupPnt->SetGroupPrimitivesAspect (theStyle->ArrowAspect()->Aspect());
    gp_Trsf aTrsfInv = mySelectable->LocalTransformation().Inverted();
    gp_Dir  aNorm (aPickPnt.Normal.x(), aPickPnt.Normal.y(), aPickPnt.Normal.z());
    Handle(Graphic3d_ArrayOfTriangles) aTris =
      Prs3d_Arrow::DrawShaded (gp_Ax1(aPickPnt.Point, aNorm).Transformed (aTrsfInv),
        1.0, 15.0,
        3.0, 4.0, 10);
    aGroupPnt->AddPrimitiveArray (aTris);
    thePrsMgr->AddToImmediateList (aPrs);
  }
}

上面的代码没有将新的高亮呈现作为 MyAisOwner 的属性来存储,而是使用 SelectMgr_SelectableObject::GetHilightPresentation() 方法来创建一个直接存储在交互对象中的呈现。

下一个技巧是遍历最后一个拾取结果 StdSelect_ViewerSelector。动态高亮应该在拾取之后立即被调用,因此高亮显示的 Owner 应该总是在拾取结果中被找到。StdSelect_ViewerSelector::Picked() 返回实体,以它们与拾取射线原点(鼠标光标)的距离降序排列;通常情况下,当没有选择过滤器分配给 AIS_InteractiveContext 时,我们的 Owner 应该是这个列表中的第一个。

SelectMgr_SortCriterion 为我们提供了有用的信息,如位于拾取射线上的被检测物体的 3D 点,以及该点的表面法线方向(实际上,它将是拾取三角形的法线),我们使用 Prs3d_Arrow 呈现构建器将其显示为箭头。

在这里插入图片描述

鼠标悬停时曲面法线
 

结果在屏幕截图上看起来很不错,但有交互问题 —— 一旦显示,箭头不再随着鼠标的进一步移动而更新。但是这种行为并不是一个错误 —— AIS 只在选择(picking) Owner 更改时才调用 MyAisOwner::HilightWithColor(),以避免不必要的 Viewer 更新。要覆盖这个行为,我们可以覆盖 SelectMgr_EntityOwner::IsForcedHilight() 选项:

class MyAisOwner : public SelectMgr_EntityOwner
{
...
  virtual bool IsForcedHilight() const override { return true; }
};

这解决了我们特定用例中的问题。请记住,大多数对象不需要在每次鼠标移动时更新高亮显示;在任何地方覆盖此标志将浪费资源,并可能导致性能问题 - 请谨慎使用。

突出显示的方法 (Highlighting approaches)

AIS 提供了另一种处理呈现高亮的方法,它由选项 SelectMgr_SelectableObject::IsAutoHilight() 管理。默认情况下,该选项处于打开状态,并将高亮显示逻辑重定向到上一节中演示的 SelectMgr_EntityOwner::HilightWithColor()。关闭此选项可将高亮逻辑重定向到交互对象本身 SelectMgr_SelectableObject::HilightSelected()

除了将逻辑从 Owner 移动到交互对象之外,这种方法还允许同时处理同一对象中所有选中 Owner 的高亮显示,并共享一个公共呈现而不是每个 Owner 呈现 —— 在大量小的可选择元素(如 MeshVS_Mesh 对象中的网格节点)的情况下提高性能并减少内存占用。

这种场景的进一步优化将是为整个对象使用单个Owner,将所选择元素的列表存储在 Owner 本身中 —— 就像 AIS_PointCloud 对象用于突出显示单个点一样。

我们不会在这里详细描述这些高级技术 —— 我们只总结一下 AIS 中可用的主要高亮显示方法:

  • 突出显示交互对象(主动显示模式)的主要呈现形式,该对象由 PrsMgr_PresentableObject::Compute() 填充,并通过 AIS 逻辑进行颜色调制显示。
    • 例子:AIS_TextLabel。
  • 突出显示交互对象的辅助呈现形式,该对象由 PrsMgr_PresentableObject::Compute() 填充,并通过 AIS 逻辑进行颜色调制显示。
    • 例子:AIS_Shape,以 AIS_Shaded 显示模式显示,并以 AIS_WireFrame 显示模式突出显示(默认行为)。参见 PrsMgr_PresentableObject::SetHilightMode()
  • 突出显示存储在自定义 SelectMgr_EntityOwner 中并由 SelectMgr_EntityOwner::HilightWithColor() 管理的呈现。
    • 例子:StdSelect_BRepOwner 用于选择子形状。
  • 自定义高亮呈现存储在交互对象本身中(参见 SelectMgr_SelectableObject::GetHilightPresentation() / ::GetSelectPresentation() 方法)。
    • SelectMgr_EntityOwner::HilightWithColor() 填充,SelectMgr_SelectableObject::IsAutoHilight() 已打开。
      例子:AIS_PointCloud。
    • SelectMgr_SelectableObject::HilightSelected() 填充,SelectMgr_SelectableObject::IsAutoHilight() 关闭。
      例子:MeshVS_Mesh。
  • 交互对象(主动显示模式)的主要呈现,由 PrsMgr_PresentableObject::Compute() 填充,并在高亮事件上手动更新(重新计算或修改外观)。
    例子:AIS_Manipulator。

选项的数量看起来使人不知所措,但总的来说,最好坚持使用最适合你的方法,并仅在必要时考虑替代方案。

鼠标点击 (Mouse click)

动态突出显示只是 SelectMgr_EntityOwner 可能有用的场景之一。另一个功能是处理鼠标单击 SelectMgr_EntityOwner::HandleMouseClick() 的接口。

该接口对于定义一些用户界面元素(如按钮)很有用,并且很可能您的应用程序将为此目的使用更全面的 GUI 框架,而不是 AIS 。但是让我们来做一些有趣的事情,让我们的对象在每次鼠标点击时改变颜色:

class MyAisOwner : public SelectMgr_EntityOwner
{
...
  virtual bool HandleMouseClick (const Graphic3d_Vec2i& thePoint,
                                 Aspect_VKeyMouse theButton,
                                 Aspect_VKeyFlags theModifiers,
                                 bool theIsDoubleClick) override;
};
bool MyAisOwner::HandleMouseClick (const Graphic3d_Vec2i& thePoint,
                                   Aspect_VKeyMouse theButton,
                                   Aspect_VKeyFlags theModifiers,
                                   bool theIsDoubleClick)
{
  static math_BullardGenerator aRandGen;
  Quantity_Color aRandColor (float(aRandGen.NextInt() % 256) / 255.0f,
                             float(aRandGen.NextInt() % 256) / 255.0f,
                             float(aRandGen.NextInt() % 256) / 255.0f,
                             Quantity_TOC_sRGB);
  mySelectable->Attributes()->ShadingAspect()->SetColor(aRandColor);
  mySelectable->SynchronizeAspects();
  return true;
}

看起来很简单。现在让我们让事情变得更有趣,在每次点击时启动一些简单的对象动画。为了简单起见,我们在示例中使用了几个全局(static)变量 —— 不要在实际的生产代码中这样做。

class MyAisOwner : public SelectMgr_EntityOwner
{
...
  void SetAnimation (const Handle(AIS_Animation)& theAnim)
  { myAnim = theAnim; }
...
  Handle(AIS_Animation) myAnim;
};
bool MyAisOwner::HandleMouseClick (const Graphic3d_Vec2i& thePoint,
                                   Aspect_VKeyMouse theButton,
                                   Aspect_VKeyFlags theModifiers,
                                   bool theIsDoubleClick)
{
  static bool isFirst = true;
  isFirst = !isFirst;
  MyAisObject* anObj = dynamic_cast<MyAisObject*> (mySelectable);
  gp_Trsf aTrsfTo;
  aTrsfTo.SetRotation (gp_Ax1 (gp::Origin(), gp::DX()),
                       isFirst ? M_PI * 0.5 : -M_PI * 0.5);
  gp_Trsf aTrsfFrom = anObj->LocalTransformation();
  Handle(AIS_AnimationObject) anAnim =
    new AIS_AnimationObject ("MyAnim", anObj->InteractiveContext(),
                             anObj, aTrsfFrom, aTrsfTo);
  anAnim->SetOwnDuration (2.0);
  myAnim->Clear();
  myAnim->Add (anAnim);
  myAnim->StartTimer (0.0, 1.0, true);
  return true;
}

动画是一个复杂的话题,值得专门写一篇文章 —— 我们在这里不要太深入。要以不间断的方式执行动画,它应该由一些类处理,如 AIS_ViewController,它负责管理用户输入事件和 3D 查看器更新。要使用它,你需要添加一个自定义对象动画到 AIS_ViewController::ObjectsAnimation() 或添加自定义视图动画到 AIS_ViewController::ViewAnimation()。在应用程序的某个地方,它可能看起来像这样:

Handle(AIS_InteractiveContext) theCtx;
Handle(AIS_ViewController) theViewCtrl;
Handle(MyAisObject) aPrs = new MyAisObject();
aPrs->SetAnimation (theViewCtrl->ObjectsAnimation());
theCtx->Display (aPrs, MyAisObject::MyDispMode_Main, 0, false);

最终结果 (Final result)

最终的示例可以通过从 Draw Harness 插件 QAcommands(TKQADraw toolkit)中调用 QATutorialAisObject 命令来查看:

pload VISUALIZATION QAcommands
vinit View1
QATutorialAisObject p
vfit

如果您在本教程中遇到一些问题,也可以在 src/QADraw/QADraw_Tutorials.cxx 查看该命令的源代码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/616793.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【网站项目】SpringBoot796水产养殖系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

vi\vim编辑器

root用户&#xff08;超级管理员&#xff09; 无论是Windows、MacOS、Linux均采用多用户的管理模式进行权限管理。 在Linux系统中&#xff0c;拥有最大权限的账户名为&#xff1a;root&#xff08;超级管理员&#xff09; root用户拥有最大的系统操作权限&#xff0c;而普通…

改进YOLOv5,YOLOv5+CBAM注意力机制

目录 1. 目标检测模型 2. YOLOv5s 3. YOLOv5s融合注意力机制 4. 修改yolov5.yaml文件 5. ChannelAttentionModule.py 6. 修改yolo.py 1. 目标检测模型 目标检测算法现在已经在实际中广泛应用&#xff0c;其目的是找出图像中感兴趣的对象&#xff0c;并确定对象的类别和位…

牛客NC343 和大于等于K的最短子数组【困难 前缀和 Java/Go】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/3e1fd3d19fb0479d94652d49c7e1ead1 思路 本答案利用前缀和解答&#xff0c;Java&#xff0c;Go答案通过&#xff0c;但是同样的代码用PHP的话有一个测试用例超时 应该还有更优秀的答案&#xff0c;后面找到更优…

如何远程操作服务器中的Python编译器并将运行结果返回到Pycharm

文章目录 一、前期准备1. 检查IDE版本是否支持2. 服务器需要开通SSH服务 二、Pycharm本地链接服务器测试1. 配置服务器python解释器 三、使用内网穿透实现异地链接服务器开发1. 服务器安装Cpolar2. 创建远程连接公网地址 四、使用固定TCP地址远程开发 本文主要介绍如何使用Pych…

分布式与一致性协议之PBFT算法(一)

PBFT算法 概述 前面提到了拜占庭将军问题之后&#xff0c;有人可能会感到困惑:口信消息型拜占庭问题直接在实际项目中是如何落地的呢&#xff1f;事实上&#xff0c;它很难在实际项目中落地&#xff0c;因为口信消息型拜占庭问题之解是一个非常理论化的算法&#xff0c;没有与…

C++类的概念以及用法

目录 面向过程和面向对象初步认识类的引入类的定义类的两种定义方式声明和定义全部放在类体中 声名定义分离 类的作用域成员变量命名规则建议访问限定符 类的封装类的实例化类对象模型类的对象大小的计算扩展 结构体内存对齐规则 感谢各位大佬对我的支持,如果我的文章对你有用,…

《Fundamentals of Power Electronics》——转换器的传递函数

转换器的工程设计过程主要由以下几个主要步骤组成&#xff1a; 1. 定义了规范和其他设计目标。 2. 提出了一种电路。这是一个创造性的过程&#xff0c;利用了工程师的物理洞察力和经验。 3. 对电路进行了建模。组件和系统的其他部分适当建模&#xff0c;通常使用供应商提供的…

祝天下母亲节快乐!虚无!——早读(逆天打工人爬取热门微信文章解读)

练功加精力哦 引言Python 代码第一篇 人民日报【夜读】人与人之间最好的关系&#xff1a;遇事靠谱&#xff0c;懂得感恩第二篇 冯站长之家 三分钟新闻早餐结尾 感恩与善行 是人生旅途中的灯塔 怀感恩之心 行小善之事 它们将指引我们走向光明 引言 今天是母亲节 祝天下的所有母…

三星硬盘格式化后怎么恢复数据

在数字化时代&#xff0c;硬盘作为数据存储的核心部件&#xff0c;承载着我们的重要文件、照片、视频等资料。然而&#xff0c;不慎的格式化操作可能使我们失去宝贵的数据。面对这样的困境&#xff0c;许多用户可能会感到无助和焦虑。本文旨在为三星硬盘用户提供格式化后的数据…

【CMU 15-445】Proj4 Concurrency Control

Concurrency Control 通关记录Task1 TimestampsTask2 Storage Format and Sequential ScanTask3 MVCC ExecutorsTask3.1 Insert ExecutorTask3.2 CommitTask3.3 Update and Delete ExecutorTask3.4 Stop-the-world Garbage Collection Task4 Primary Key IndexTask4.0 Index Sc…

vue3 element plus el-date-picker组件在日期上做标识

1.先看效果图,带红点的就是我要做标识的日期 2.直接把代码拿出来就可以用 (1)html部分 <el-date-pickerv-model"startTime"type"datetime"placeholder"选择开始日期"format"YYYY-MM-DD HH:mm"value-format"YYYY-MM-DD HH:mm…

基于ChatGLM+Langchain离线搭建本地知识库(免费)

目录 简介 服务部署 实现本地知识库 测试 番外 简介 ChatGLM-6B是清华大学发布的一个开源的中英双语对话机器人。基于 General Language Model (GLM) 架构&#xff0c;具有 62 亿参数。结合模型量化技术&#xff0c;用户可以在消费级的显卡上进行本地部署&#xff08;INT…

大模型微调之 在亚马逊AWS上实战LlaMA案例(八)

大模型微调之 在亚马逊AWS上实战LlaMA案例&#xff08;八&#xff09; 微调技术 Llama 等语言模型的大小超过 10 GB 甚至 100 GB。微调如此大的模型需要具有非常高的 CUDA 内存的实例。此外&#xff0c;由于模型的大小&#xff0c;训练这些模型可能会非常慢。因此&#xff0c…

计算机网络(网络原理与应用)之高级交换实验------冗余环路与生成树协议

一、实验目的 (1)了解生成树协议的作用&#xff1b; (2)熟悉生成树协议的配置。 二、应用环境 采用生成树协议可以避免环路。 生成树协议的根本目的是将一个存在物理环路的交换网络变成一个没有环路的逻辑树形网络。IEEE802.ID协议通过在交换机上运行一套复杂的算法STA(sp…

Springboot+Vue项目-基于Java+MySQL的影院订票系统(附源码+演示视频+LW)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &…

英语复习之英语形近词总结(三)

英语形近词总结复习第三部分: 单词释义例句 adorn 英 /əˈdɔːn/ 美 /əˈdɔːrn/ vt.装饰&#xff1b;使生色&#xff1a;n.(Adorn)人名&#xff1b;&#xff08;泰&#xff09;阿隆 1.They wash and wax the cars, go on and on about them—some even adorn them with …

【GESP】2024年03月图形化二级 -- 找因数

找因数 【题目描述】 默认小猫角色和白色背景。 小杨最近刚刚学习了因数的概念&#xff0c;具体来说&#xff0c;如果一个正整数 a a a 可以被另一个正整数 b b b 整除&#xff0c;那么我们就说 b b b 是 a a a 的因数&#xff0c;例如6可以被1、2、3、6整除&#xff0c;…

[BJDCTF2020]ZJCTF,不过如此 1

涉及&#xff1a;php的伪协议、preg_replace函数的漏洞和正则表达式的运用。 解题步骤 <?phperror_reporting(0); $text $_GET["text"]; $file $_GET["file"]; if(isset($text)&&(file_get_contents($text,r)"I have a dream"))…

JeeSite 平台 Spring Boot 3 体验版发布,一个 Java 快速开发平台

引言 是时候为 Spring Boot 3 做准备了&#xff0c;2018年2月 Spring Boot 进入 2.0 时代&#xff0c;距今已经 5 年了。2022 年 11 月 Spring Boot 3.0 正式发布&#xff0c;它将基于 Spring Framework 6.0&#xff0c;并且需要 Java 17 版本&#xff0c;同时它也将是 Jakart…