OpenCASCADE开发指南<九>:OCC 数据结构分析之拓扑数据结构

  数据结构,指的是数据元素之间的相互关系,尤其是数据的逻辑结构。选择数据结构的主要依据是数据的逻辑结构[6]。 因此, 本章将主要描述三种数据的逻辑结构。这三种数据包括:二维几何数据、三维几何数据和拓扑数据。
在这里插入图片描述

1 拓扑数据

  拓扑数据结构定义了参数空间中对象的数据结构, 由拓扑工具箱提供。 该工具箱提供五种服务: 保持 Shape 位置轨迹,Shape、 SubShape 以及它们的拓扑方向和拓扑状态的命名, Shape 和 SubShape 的处理,访问拓扑数据结构,以及使用 Shape 列表和 Shape 图。本节将论述第一、第三和第四种服务。

1.1 保持 Shape 位置轨迹

  可以将 OCC 的局部坐标系可以看作以下两者之一:

(1) 具有一个原点和三个标准正交向量的右手三面体。 这个定义由 gp _Ax2类提供。
(2)由 a+1 决定的转换,它允许我们在局部引用框架和全局引用框架间进行坐标转换。这个定义由 gp_Trsf 类提供。

  在此, 需要认识两个概念(在 TopLoc 包中): 基本引用坐标和复合引用坐标。基本引用坐标是用在右手直角坐标系或者右手线性转换中的坐标; 而复合引用坐标则由几个基本引用坐标组成。

  如果两个引用坐标系是由相同的基本坐标系以相同的顺序组成的, 那么这两个坐标系是等价的。 例如, 有三个基本坐标系: R1, R2, R3 。 由上述三者复合而成的坐标系有:

C1 = R1 * R2
C2 = R2 * R3
C3 = C1 * R3
C4 = R1 * C2

  这里 C3 和 C4 是等价的,因为它们都是由 R1 * R2 * R3 复合而成的。

1.2 Shape 和 SubShape 的处理

1.2.1 概述

  Shape 和 SubShape 的处理由 TopoDS 包提供。TopoDS 包描述了具有以下特征的拓扑数据结构:

(1)对没有拓扑方向也没有位置的抽象 Shape 的引用
(2)通过工具类对数据结构的访问。

  TopoDS 包是基于 TopoDS_Shape 类和那个定义了自己下层 Shape 的类的。这样做有不少好处, 但也有一个缺点: 这些类太通用了, 不同的 Shape 的不一定属于不同的类型。 因此不可能通过检查来防止冲突(如将一个面插入到一条边)。TopoDS 包提供了两套类: 一套由下层 Shape(既没有拓扑方向也没有位置)派生; 另一套则由 TopoDS_Shape(与 TopoAbs 包中列出的标准拓扑 Shape 一致)派生。

1.2.2 TopoDS_Shape 类

  如前面所言, OCC 描述了参数空间对象的数据结构。这些描述用到了定位和限制。 可以由这些术语描述的 Shape 的类型有: 顶点、 面和 Shape。 顶点是依据参数空间位置来定义的,而面和 Shape 则依据空间的限制来定义。

  OCC 的拓扑描述允许我们将由上述术语定义的简单 Shape 组成集。例如,一个边集形成一个线框; 一个面集形成一个壳; 一个实心体集形成一个组合实心体。 也可以将不同类型的 Shape 组成一个复合体, 并为一个 Shape 指定拓扑方向和位置。

  依据 Shape 的复杂程度, 从顶点到组合实心体依次将 Shape 列出来, 这样能方便的知道一个 Shape 是由哪些简单 Shape 组成的。 事实上, 这就是 TopoDS 包的意图。

  一个 Shape 模型是一个可共享的数据结构, 因为它可以被其它 Shape 使用(如一条边可以被一个实心体的多个面使用)。可共享的数据结构是通过引用来处理的。 若一个简单引用不够充分时, 需要增加两个信息: 一个拓扑方向和一个局部坐标引用。拓扑方向 描述被引用的形状如何在一个边界使用。一个局部引用坐标(Location from TopLoc)允许程序员在 Shape 的定义位置之外引用该 Shape。

  TopoDS_TShape 类是所有 Shape 描述的根类, 包含一个 Shape 列表。 如果有必要, 由 TopoDS_TShape 派生的类可携带几何域信息(例如, 一个 TVertex 类型的几何点)。一个 TopoDS_TShape 就是一个 Shape 在自己的引用框架上的描述。该类是通过引用处理的。

  TopoDS_Shape 类描述了 Shape 的引用。它包含一个指向下层抽象 Shape 的引用、 一个拓扑方向和一个引用坐标。 该类是通过数值处理的, 因此不能被共享。描绘下层 Shape 的类从不被直接引用, 而是通过 TopoDS_Shape 类被间接引用。Shape 的特殊信息总是由 TopoDS_TShape 的派生类的派生类添加。 图 4.5 表示了一个由两个面组成(通过一条边连接)的壳。
在这里插入图片描述

  而图 4.6 给出了图 4.5 中的壳的数据结构。在图 4.6 中,壳由下层 Shape TS 描述,面则由下层面 TF1 和 TF2 描述。整个壳包含七条边(TE1TE7)和六个顶点(TV1TV6)。 线框 TW1 引用了 TE1~TE4四条边; TW2 则引用了 TE4~TE7 四条边。顶点被这些边引用: TE1(TV1,TV2),TE2(TV2,TV3), TE3(TV3,TV4), TE4(TV4,TV5), TE5(TV5,TV6), TE6(T6,TV7),TE7(TV7,TV8)。

在这里插入图片描述
  图 4.6 中的数据结构不包含任何中断引用。所有的引用都是从复杂的下层Shape 到简单的下层 Shape 的。 另外, 这个数据结构要尽可能紧凑,并且子对象是能够被不同对象共享的。两个非常相似的对象(也许是同一个对象的两个版本) 可以共享相同的子对象。 该数据结构中, 局部坐标的使用允许那些重复子结构的描述被共享。 当创建一个新的对象版本或者对象需要适应坐标变化时,通常会用到复制这项操作; 紧凑的数据结构有效避免了因复制而带来的信息缺失。

  图 4.7 描绘的数据结构包含一个实心体的两种版本。 其中第二个版本描绘了一系列处于不同位置但相同的孔。 这样的结构不但紧凑, 而且包含了子元素的所有信息。 从 TSh2 到它的下层面 TFa 和 TFb 的三个引用, 都有相应的基于孔心位置的局部坐标系。
在这里插入图片描述
TopoDS_Shape 类的方法有:

(1) IsNull 和 Nullify。 若一个 Shape 不指向任何下层 Shape(既没有拓扑方向也没有位置),那么该 Shape 是空的(Shape 的默认拓扑状态)。
(2) Location、 Move 和 Moved:用来访问 Shape 的局部引用坐标。
(3) Orientation、 Oriented、 Reverse 和 Reversed: 用来访问 Shape 的拓扑方向。
(4) ShapeType:用来访问基于下层 Shape(没有拓扑方向也没有位置)的相等方法。
( 5) IsPartner。 如果两个 Shape 拥有同一个下层 Shape( 没有拓扑方向也没有位置),则称这两个 Shape 是伙伴关系。这两伙伴的拓扑方向和局部引用可以不同。
( 6) IsSame。如果两个 Shape 是伙伴关系,并且它们拥有相同的局部引用(TopLoc_Location), 则称这两个 Shape 是等价的。 例如, 通过一条边连接的两个面,它们有两条相同的边。
( 7)IsEqual。如果两个 Shape 等价并且有同样的拓扑方向, 则称这两个 Shape相等,用“=” 表示相等关系。

1.2.3 TopoDS_Shape 的派生类

  TopoDS_Shape 类及其派生类是处理拓扑对象的自然方式。 Shape 的派生类有 : TopoDS_Vertex 、TopoDS_Edge 、 TopoDS_Wire 、 TopoDS_Face 、 TopoDS_Shell、 TopoDS_Solid、 TopoDS_CompSolid 和 TopoDS_Compound。 尽管它们的名字与 TopoDS_TShape 派生类的名字相似,但彼此的用法相差很大。

  TopoDS_TShape 类是不可见的。它在自己的没有拓扑方向的原局部坐标系中定义了一个类。 TopoDS_Shape 是一个引用; 这个引用包含在 TopoDS_ TShape(具有拓扑方向和局部引用)中。

  TopoDS_TShape 类是推迟类, 而 TopoDS_Shape 类不是。 有了 TopoDS_ Shape类, 就可以在不知道拓扑对象具体类型的情况下处理拓扑对象了。 这是一种通用形式。纯拓扑算法经常用到 TopoDS_Shape 类。

  TopoDS_TShape 类是通过引用处理的;而 TopoDS_Shape 类通过数值处理。TopoDS_Shape 类只是一个具有拓扑方向和局部坐标的引用。共享 TopoDS_Shapes 类是没有意义的。 重要的是共享 TopoDS_TShapes。 形参的赋值和传递并没有将数据结构拷贝, 而只是创建了一些新的 TopoDS_Shape 类( 这些类赋予同一个 TopoDS_TShape 类)。

  尽管 TopoDS_TShape 的派生类用来添加额外信息, TopoDS_Shape 的派生类却不能添加额外的域。 TopoDS_Shape 的派生类仅仅用来将一个引用特殊化,以便通过静态类型来控制对象(由编译器实现) 。例如,对编译器而言, 以TopoDS_Face 类作参数要比以 TopoDS_Shape 类参数更精确。 编译器能够接收的只有 TopoDS_TopoDS 类; 其它的类都是没有意义的。 拓扑数据结构的所有引用都是由 Shape 类和它的继承类(在 TopoDS_TopoDS 类中定义)制造的。

  TopoDS_Shape 的派生类没有构造函数, 否则会因为“同名覆盖” (C++的一个特点)而失去类型控制权。 TopoDS 包提供了这样一些类方法,这些类方法能够通过类型匹配,将 TopoDS_Shape 类的一个对象抛掷给 TopoDS_Shape 派生类的一个对象。

  例 4.1 说明了如何接收一个 TopoDS_Shape 类型的参数,然后又是怎样将它传递给变量 V(如果该参数是一个顶点的话)的,或者如何调用 ProcessEdge 方法(如果该参数是一条边的话)。

例 4.1:

#include <TopoDS_Vertex.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Shape.hxx>
void ProcessEdge(const TopoDS_Edge&);
void Process(const TopoDS_Shape& aShape)
{
if (aShape.Shapetype() == TopAbs_VERTEX)
{
TopoDS_Vertex V;
V = TopoDS::Vertex(aShape);
//该语句是正确的。
TopoDS_Vertex V2 = aShape;
//该语句不能被编译器处理
TopoDS_Vertex V3 = TopoDS::Vertex(aShape);
//正确
}
else if (aShape.ShapeType() == TopAbs_EDGE)
{
ProcessEdge(aShape) ;
//编译器将无法处理该语句。
ProcessEdge(TopoDS::Edge(aShape)) ;
//正确
}
else
{
cout << “Neither a vertex nor an edge ?;
ProcessEdge(TopoDS::Edge(aShape)) ;
//编译没有问题,但是运行的时候将产生一个异常。
}
}

1.3 拓扑数据结构的访问

  TopExp 包提供了一些工具,用来访问由 TopoDS 包描述的数据结构。访问一个拓扑结构意味着找到指定类型的所有子对象,例如,找到实心体的所有面。TopExp 包提供了一个 TopExp_Explorer 类(一个工具, 用来找到指定类型的所有子对象。)和一些包方法。这些包方法有:

(1) MapShapes ,用来收集一个 Shape 的子图对象。
(2) MapShapesAndAncestors, 用来收集子对象和包含子对象的对象; 例如,由一个实心体的所有边组成的集合容器和由一个面(包含某条边) 的所有边组成的集合容器。
(3) FirstVertex、 LastVertex 和 Vertices 这三者用来寻找边的顶点。TopExp_Explorer 是一个工具,用来访问来自 TopoDS 包的拓扑数据结构。

一个访问器与下面三者同时建立:
(1)要访问的 Shape。
(2) 要查找的 Shape 的类型, 例如顶点、 边和 Shape 异常(这是不允许的)。
(3) Shape 要避免的类型,例如壳、边。默认的,这个类型是 SHAPE。 这个默认值意味着访问不受限制。

访问器要访问整个结构, 以找到要求的类型(不包括要避免的类型)。例 4.2说明了如何找到 Shape S 的所有面。

例 4.2:

void test()
{
TopoDS_Shape S;
TopExp_Explorer Ex;
for (Ex.Init(S,TopAbs_FACE); Ex.More(); Ex.Next())
{
ProcessFace(Ex.Current());
}
}

找到不在指定边上的所有顶点,如例 4.3。
例 4.3:

for (Ex.Init(S,TopAbs_VERTEX,TopAbs_EDGE); ...)

先找到一个壳的所有面,然后找到不在壳中的所有面,如例 4.4。
例 4.4:

void test()
{TopExp_Explorer Ex1, Ex2;
TopoDS_Shape S;
for (Ex1.Init(S,TopAbs_SHELL);Ex1.More(); Ex1.Next())
{
//访问所有壳
for (Ex2.Init(Ex1.Current(),TopAbs_FACE);Ex2.More(); Ex2.Next())
{
//访问当前壳的所有面
ProcessFaceinAshell(Ex2.Current());
...
}
}
for(Ex1.Init(S,TopAbs_FACE,TopAbs_SHELL);Ex1.More(); Ex1.Next())
{
//访问不在壳中的所有面。
ProcessFace(Ex1.Current());
}
}

  访问器假定对象只包含相同类型或者下属类型。 例如, 要查找面, 访问器不会在轮廓、边、或顶点上查找。
下面通过两个例子(例 4.5 和例 4.6) 来说明 TopExp 包的使用方法。 其中例4.5 是 TopExp 包中 MapShapes 方法的源代码,说明了在填充图的时候如何使用访问器。

  如果一个对象被多次引用, 那么访问器就可以多次访问该对象。 例如, 实心体的一条边通常被两个面引用。 要保证只对一个对象访问一次, 必须将这些对象置于一个图中,如例 4.5。这就是 MapShapes 方法的意图。

例 4.5:

void TopExp::MapShapes (const TopoDS_Shape& S,
const TopAbs_ShapeEnum T,
TopTools_IndexedMapOfShape& M)
{
TopExp_Explorer Ex(S,T);
while (Ex.More())
{
M.Add(Ex.Value());
Ex.Next();
}
}

  在例 4.6 中,一个对象的所有面和所有边都是依据以下规则画出来的:

(1)面是由具有 FaceIsoColor 颜色的 NbIso isoparametric 直线网格描绘的。
(2)边用一种颜色(标记着有多少个面共享这条边)画出。
( 3) FreeEdgeColor 用来标记不属于面的边,即线框元素。
( 4) BorderEdgeColor 用来标记只属于某一个面的边。
( 5) SharedEdgeColor 用来标记属于多个面的边。
( 6)要显示单条边和单个面可以使用 DrawEdge 和 DrawFaceIso 方法。

例 4.6 要经历的步骤如下:

(1)将边存到图中,并创建一个相应的整数数组(一个元素记录了一条边被几个面共享)。该数组初始值是零。
( 2)访问面,并画出每一个面。
( 3)访问边,并且每访问一条边都相应的在数组中增加面计数器的值。
( 4)形成边图,用相应的颜色(记录着面数)画出每一条边。

例 4.6:

void DrawShape ( const TopoDS_Shape& aShape,
const Standard_Integer nbIsos,
const Color FaceIsocolor,
const Color FreeEdgeColor,
const Color BorderEdgeColor,
const Color SharedEdgeColor)
{
//将边存入图中。
TopTools_IndexedMapOfShape edgemap;
TopExp::MapShapes(aShape,TopAbs_EDGE,edgeMap);
//创建一个数组,数组的初始值为 0。
TColStd_Array1OfInteger faceCount(1,edgeMap.Extent());
faceCount.Init (0);
//访问面。
TopExp_Explorer expFace(aShape,TopAbs_FACE);
while (expFace.More())
{
//画出当前的面。
DrawFaceIsos(TopoDS::Face(expFace.Value()),nbIsos, FaceIsoColor);
//访问该面的所有边。
TopExp_Explorer expEdge(expFace.Value(),TopAbs_EDGE);
while (expEdge.More())
{
//为该边增加面数。
faceCount(edgeMap.Index(expEdge.value()))++;
expEdge.Next();
}expFace.Next();
}
//画出图中所有的边。
Standard_Integer i;
for (i=1;i<=edgemap.Extent();i++)
{
switch (faceCount(i))
{
case 0 :
DrawEdge(TopoDS::Edge(edgeMap(i)),FreeEdgeColor);
break;
case 1 :
DrawEdge(TopoDS::Edge(edgeMap(i)),BorderEdgeColor);
break;
default :
DrawEdge(TopoDS::Edge(edgeMap(i)),SharedEdgeColor);
break;
}
}
}

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-15 11:38:06       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-15 11:38:06       106 阅读
  3. 在Django里面运行非项目文件

    2024-03-15 11:38:06       87 阅读
  4. Python语言-面向对象

    2024-03-15 11:38:06       96 阅读

热门阅读

  1. CF899F Letters Removing 题解

    2024-03-15 11:38:06       40 阅读
  2. android11 申请所有文件访问权限

    2024-03-15 11:38:06       31 阅读
  3. MongoDB 中的锁分析

    2024-03-15 11:38:06       34 阅读
  4. 大数据开发(Spark面试真题-卷六)

    2024-03-15 11:38:06       42 阅读
  5. ms office学习记录:新增考点

    2024-03-15 11:38:06       41 阅读
  6. android读取sd卡上文件中的数据

    2024-03-15 11:38:06       43 阅读
  7. Android studio存储之SharedPreferences

    2024-03-15 11:38:06       45 阅读