Visualization Toolkit官方开发文档翻译(9)–第八章高级数据表示

本章探讨了数据表示中的高级主题。主题包括拓扑和几何关系以及单元和数据集的计算方法。

8.1 坐标系

我们将检查三种不同的坐标系:全局坐标系、数据集坐标系和结构化坐标系。图 8-1显示了全局和数据集坐标系之间的关系,并描述了结构化坐标系。

图8-1

图 8-1。本地和全球坐标系。

 

全球坐标系

全局坐标系是笛卡尔三维空间。每个点都表示为沿 x、y 和 z 轴的三元组值 (x,y,z)。这与第 3 章 – 计算机图形学入门中的“坐标系”中描述的系统相同。

全局坐标系始终用于指定数据集的几何形状(即点坐标)和数据属性,例如法线和矢量。我们将使用“位置”一词来表示我们正在使用全局坐标。

数据集坐标系

数据集或局部坐标系基于组合的拓扑和几何坐标。拓扑坐标用于标识特定单元(或可能是子单元),几何坐标用于标识单元内的特定位置。它们一起唯一地指定了数据集中的一个位置。在这里,我们将使用“位置”一词来指代本地或数据集坐标。

拓扑坐标是一个“id”:一个唯一的非负整数,指的是数据集点或单元格。对于复合单元格,我们使用额外的“子 ID”来指代组成复合单元格的特定主单元格。子 ID 也是唯一且非负的。id 和 sub-id 一起选择一个特定的主单元格。

要指定主单元格内的位置,我们使用几何坐标。这些几何坐标或参数坐标是“自然”坐标或对细胞的特定拓扑和尺寸而言规范的坐标。

我们可以通过参考示例来最好地解释局部坐标。如果我们考虑中所示的折线单元格类型图 8-2,我们可以通过指示 1) 折线单元 ID,2) 主单元(即线)子 ID 和 3)线的参数坐标来指定点的位置。因为线是一维的,所以自然或参数坐标是基于一维参数 r 的。那么沿直线的任意一点由直线的两个端点的线性组合给出xixi+1

屏幕截图 2022-11-20 150326

其中参数坐标rr被限制在(0,1)在这个等式中,我们假设 sub-id 等于i.

参数坐标的数量对应于单元格的拓扑维数。三维细胞将由三个参数坐标表征t ). 对于拓扑阶数小于三的单元格,我们将忽略最后一个− n )参数坐标,其中nn是细胞的拓扑顺序。为了方便和一致,我们还将每个参数坐标的范围限制在(0,1).

每种细胞类型都有自己的参数坐标系。在本章的后面,我们将详细描述参数坐标系。但首先我们将检查另一个坐标系,结构化坐标系

结构坐标系

许多数据集类型都是结构化的。这包括图像数据和结构化网格。由于它们固有的结构,它们有自己的自然坐标系。该坐标系基于– – ki−j−k我们在第 5 章的“图像数据”中提到的索引方案。

结构化坐标系是描述结构化数据集组件的自然方式。通过固定一些指标,并允许其他指标在有限范围内变化,我们可以指定点、线、面和体积。例如,通过固定一世i指数=一世0i=i0,并允许jjkk索引范围在它们的最小值和最大值之间,我们指定一个表面。如果我们固定三个指标,我们指定一个点,如果我们固定两个指标,我们指定一条线,如果我们允许三个指标变化,我们指定一个体积(或子体积)。结构化坐标系通常用于指定感兴趣区域(或 ROI)。感兴趣区域是我们想要可视化或操作的区域。

数据集坐标系的点和单元格id与结构化坐标系之间存在简单的关系。在给定索引的情况下获取点 ID pid(ip,jp,kp)(nx,ny,nz)我们用

屏幕截图 2022-11-20 150337

0一世pnX,0jpn,0kpn◂,▸◂≤⋯▸0≤ip≤nx,◂≤⋯▸0≤jp≤ny,◂≤⋯▸0≤kp≤nz. (我们可以使用这个 id 来索引点数组或点属性数据。)这个等式隐含地假设了拓扑空间中点的排序。沿线点一世i轴变化最快,其次是jj然后是 $$k 轴。小区 ID 存在类似的关系

屏幕截图 2022-11-20 150347

这里我们已经考虑到沿每个拓扑轴的单元格比点数少一个。

8.2 插值函数

计算机可视化处理离散数据。数据要么在有限数量的点上提供,要么通过在有限数量的点上对连续数据进行采样来创建。但是我们通常需要这些离散点位置以外的位置的信息。这可能用于渲染或在算法执行期间对数据进行子采样。我们需要使用插值函数将数据从已知点插值到某个中间点。

插值函数将单元格点处的值与单元格内部相关联。因此,我们假设信息是在单元格点定义的,并且我们必须从这些点进行插值。我们可以将结果表示为每个单元格点的数据值的加权平均值。

图8-2

图 8-2。插值是局部插值函数的线性组合。插值函数按单元格点处的数据值缩放。

 

一般形式

从单元格点插入数据p一世pi到一点pp在单元格内部,我们需要三个信息:

  1. 每个单元格点的数据值,

  2. 点的参数坐标pp在细胞内,和

  3. 包括插值函数的单元格类型。

鉴于此信息,插值函数是单元格点处数据值的线性组合

屏幕截图 2022-11-20 150357

 

d是内部单元格位置的数据值(r,s,t)t )did一世是数据值在◂◽˙▸it⁢hith 细胞点,和Wi一世是一个重量◂◽˙▸it⁢hith 细胞点。插值权重是参数坐标的函数◂=▸Wi=W⁡(r,s,t)一世Wt ). 另外,因为我们想要d=did=d一世当内部点与单元格点重合时,我们可以对权重施加额外的约束

屏幕截图 2022-11-20 150405

 

我们还需要插值数据值dd不小于最小值did一世并且不大于最大值did一世. 因此权重也应该满足

屏幕截图 2022-11-20 150414

 

插值函数具有特征形状。他们达到了最大值一世=1Wi=1在小区点pip一世,并且在所有其他点都为零。检查公式 8-1,我们得出图 8-2并看到每个插值函数都具有尖顶“帽子”的形状,并且该插值是这些帽子函数的线性组合,由每个点的数据值缩放。

公式 8-4 是单元插值的一般形式。它用于将在单元格点处定义的任何数据值插入到单元格内的任何其他点。我们只需要定义具体的插值函数一世Wi对于每种细胞类型。

具体表格

每种细胞类型都有自己的插值函数。权重一世Wi是参数坐标的函数rrs, 和t. 在本节中,我们将为每种原代细胞类型定义参数坐标系和插值函数。复合单元使用其组成主单元的插值函数和参数坐标。主单元格和复合单元格之间坐标系规范的唯一区别是复合单元格使用额外的子 ID 来指定特定的主单元格。

顶点。顶点单元不需要参数坐标或插值函数,因为它们是零维的。单一加权函数为0=1.W0=1.

线。 图 8-3显示线的参数坐标系和插值函数。使用单个参数坐标描述线rr.

图8-3

图 8-3。直线的参数化坐标系和插值函数。

 

像素。 图 8-4显示了像素单元类型的参数坐标系和插值函数。使用两个参数坐标描述像素s ). 请注意,像素边缘被限制为平行于全局坐标轴。这些通常称为双线性插值函数。

图8-4

图 8-4。像素的参数化坐标系和插值函数。

 

四边形。 图 8-5显示了四边形单元格类型的参数坐标系和插值函数。使用两个参数坐标描述四边形s ).

图8-5

图 8-5。四边形的参数坐标系和插值函数

 

三角形。 图 8-6显示了三角形单元格类型的参数坐标系和插值函数。使用两个参数坐标表征三角形s ).

图8-6

图 8-6。三角形的参数化坐标系和插值函数。

 

多边形。 图 8-7显示多边形单元类型的参数坐标系和插值函数。使用两个参数坐标表征多边形s ). 参数坐标系是通过创建一个沿多边形第一条边定向的矩形来定义的。矩形还必须限制多边形。

多边形提出了一个特殊的问题,因为我们不知道有多少顶点定义了多边形。因此,不可能按照我们之前看到的函数的方式创建通用插值函数。相反,我们使用基于每个多边形顶点的加权距离平方的函数。

加权距离平方插值函数在实践中运行良好。然而,在某些极少数情况下,拓扑上远离多边形内部的点会对多边形内部产生不当影响(图 8-8). 这些情况只有在多边形是凹的并且自身环绕时才会发生。

图 8-7

图 8-7。多边形的参数化坐标系和插值函数。

 

图8-8

图 8-8。基于距离的插值函数的潜在问题。

 

四面体。 图 8-9显示了四面体单元类型的参数坐标系和插值函数。使用三个参数坐标描述四面体t ).

图 8-9

图 8-9。四面体的参数化坐标系和插值函数。

 

体素。 图 8-10显示了体素单元类型的参数坐标系和插值函数。使用三个参数坐标描述体素t ). 请注意,体素边缘被限制为平行于全局坐标轴。这些通常被称为三线性插值函数。

图8-10

图 8-10。体素的参数化坐标系和插值函数。

 

六面体。 图 8-11显示了六面体单元类型的参数坐标系和插值函数。使用三个参数坐标描述六面体t ).

图8-11

图 8-11。六面体的参数化坐标系和插值函数。

 

楔。 图 8-12显示了楔形单元类型的参数坐标系和插值函数。使用三个参数坐标描述楔形t ).

图8-12

图 8-12。楔形的参数化坐标系和插值函数。

 

金字塔。 图 8-13显示了金字塔单元类型的参数坐标系和插值函数。使用三个参数坐标描述金字塔t ).

图8-13

图 8-13。金字塔的参数化坐标系和插值函数。

 

五角棱镜。 图 8-14显示了五棱柱单元类型的参数坐标系和插值函数。使用三个参数坐标描述五角棱镜t ).

图8-14

图 8-14五棱柱的参数化坐标系和插值函数。

 

六棱柱。 图 8-15显示了六棱柱单元类型的参数坐标系和插值函数。使用三个参数坐标描述六角棱柱t ).

图 8-15

图 8-15。六棱柱的参数化坐标系和插值函数。

 

二次边。 图 8-16显示了二次边缘单元类型的参数坐标系和插值函数。使用单参数坐标描述二次边r.

图8-16

图 8-16。二次边的参数化坐标系和插值函数。

 

二次三角形。 图 8-17显示二次三角形单元格类型的参数坐标系和插值函数。使用两个参数坐标描述二次三角形s ).

图8-17

图 8-17二次三角形的参数坐标系和插值函数。

 

二次四边形。 图 8-18显示了二次四边形单元格类型的参数坐标系和插值函数。使用两个参数坐标 (r,s) 描述二次四边形。请注意,由于插值函数最容易在区间 (-1,1) 中表示,因此坐标偏移将执行到在此范围内定义的 $(\xi, \eta) 坐标。此外,符号X一世ξiηi一世介绍。这些是参数坐标◂◽˙▸it⁢h一世H观点。

图8-18

图 8-18。二次四边形的参数坐标系和插值函数。在 VTK 中,参数坐标 (r,s) 在 (0,1) 之间运行,因此坐标系转变为范围在 (-1,1) 的 (ξ, η) 参数系统。请注意,ξ i、η i和 ζ i指的是第 i点的参数坐标。

 

二次四面体。 图 8-19显示了二次四面体单元类型的参数坐标系和插值函数。使用三个参数坐标描述二次四面体t ).

图8-19

图 8-19。二次四面体的参数坐标系和插值函数。在 VTK 中,参数坐标 (r,s,t) 在 (0,1) 之间运行,因此坐标系转变为范围从 (-1,1) 的 (ξ, η 和 ζ) 参数系统。

 

二次六面体。 图 8-20显示了二次六面体单元类型的参数坐标系和插值函数。使用三个参数坐标描述二次六面体t )(r,s,t). 请注意,由于插值函数最容易在区间 (-1,1) 中表示,因此对(ξ,η,ζ)x,小时,)在此范围内定义的坐标。此外,符号◂,▸ξi,◂⋅▸ηi⁢a⁢n⁢d⁢ζiX一世,一世一个ndG一世介绍。这些是参数坐标◂◽˙▸it⁢h一世H观点。

图8-20

图 8-20。二次六面体的参数坐标系和插值函数。在 VTK 中,参数坐标 (r,s,t) 在 (0,1) 之间运行,因此坐标系转变为范围从 (-1,1) 的 (ξ, η 和 ζ) 参数系统。请注意,ξ i、η i和 ζ i指的是第 ix 点的参数坐标。

 

二次楔形。 图 8-21显示二次楔形单元类型的参数坐标系和插值函数。使用三参数坐标描述二次楔形t )(r,s,t).

图8-21

图 8-21。二次楔形的参数坐标系和插值函数。

 

二次金字塔。 图 8-22显示了二次金字塔单元类型的参数坐标系和插值函数。使用三个参数坐标描述二次金字塔t )(r,s,t). 请注意,由于插值函数最容易在区间 (-1,1) 中表示,因此对(ξ,η,ζ)x,小时,)在此范围内定义的坐标系。此外,符号ξiX一世, \eta_i$ 和ζiG一世被引入,这些是参数坐标◂◽˙▸it⁢h一世H观点。(感谢航空航天结构中心 http://www.colorado.edu/engineering/CAS实现了形函数和导数。)

图8-22

图 8-22。二次金字塔的参数化坐标系和插值函数。在 VTK 中,参数坐标 (r,s,t) 在 (0,1) 之间运行,因此坐标系转变为范围在 (-1,1) 的 (ξ,η,ζ) 参数系统。请注意,ξ i、η i和 ζ i指的是第 ix 点的参数坐标。

.

 

8.3 细胞镶嵌

正如第 5 章 – 数据表示中简要介绍的那样,非线性单元通常用于各种数值技术,例如有限元方法。虽然一些可视化系统直接支持非线性单元,但通常只支持二次公式,偶尔支持三次公式(例如,VTK 支持二次单元)。这仅代表目前在数值包中可用的公式的一小部分,并忽略了无限的潜在细胞公式。为了解决这个重要问题,可视化系统可能会提供一个适配器框架(参见图 8-23) 使用户能够将自己的仿真系统连接到可视化系统[Schroeder06]. 这样的框架需要编写从可视化数据集和单元基类派生的适配器类(在图中这些被标记为 GenericDataSet 和 GenericAdaptorCell)。这些适配器就像翻译器一样,将数据和方法调用与可视化系统和数字系统所期望的形式相互转换。与任何其他数据对象一样,此类适配器单元和数据集可以直接由可视化算法处理。然而,处理此类一般数据对象是一个难题,因为迄今为止科学文献中描述的大多数可视化算法都采用细胞几何形状是线性的基本假设。去除这个假设可能需要在算法中引入显着的复杂性,或者甚至可能需要一个新的算法。例如,行进立方体等值线算法假设单元格是正交直线六面体;如果没有这个假设,就需要在参数化和全局坐标系之间进行复杂的转换,即使在高度弯曲的非线性单元中,也可能在没有广泛的拓扑和几何检查的情况下生成退化或自相交等值线。因此,适配器框架通常包括将非线性单元细分为熟悉的线性单元的方法,然后可以很容易地通过传统的可视化算法进行处理。在下一节中,我们简要描述了一种简单的方法,用于细分高阶非线性单元以生成线性单元。如果没有这个假设,就需要在参数化和全局坐标系之间进行复杂的转换,即使在高度弯曲的非线性单元中,也可能在没有广泛的拓扑和几何检查的情况下生成退化或自相交等值线。因此,适配器框架通常包括将非线性单元细分为熟悉的线性单元的方法,然后可以很容易地通过传统的可视化算法进行处理。在下一节中,我们简要描述了一种简单的方法,用于细分高阶非线性单元以生成线性单元。如果没有这个假设,就需要在参数化和全局坐标系之间进行复杂的转换,即使在高度弯曲的非线性单元中,也可能在没有广泛的拓扑和几何检查的情况下生成退化或自相交等值线。因此,适配器框架通常包括将非线性单元细分为熟悉的线性单元的方法,然后可以很容易地通过传统的可视化算法进行处理。在下一节中,我们简要描述了一种简单的方法,用于细分高阶非线性单元以生成线性单元。因此,适配器框架通常包括将非线性单元细分为熟悉的线性单元的方法,然后可以很容易地通过传统的可视化算法进行处理。在下一节中,我们简要描述了一种简单的方法,用于细分高阶非线性单元以生成线性单元。因此,适配器框架通常包括将非线性单元细分为熟悉的线性单元的方法,然后可以很容易地通过传统的可视化算法进行处理。在下一节中,我们简要描述了一种简单的方法,用于细分高阶非线性单元以生成线性单元。

图8-23

图 8-23。细胞适配器框架。

 

基本方法

基本方法是动态细分 GenericDataSet 的单元格,然后对生成的线性细分进行操作。如伪代码所示,典型的算法如下所示:

for each cell c to be processed
  {
  if cell c meets selection criteria
    {
    linearDataSet = TessellateCell(c)
    for each linear cell cl in linearDataSet
      {
      OperateOn(cl)
      }
    }
 }

重要的是不要一次细分整个数据集,因为这可能会对内存资源产生过多的需求,而且许多算法只访问数据集单元格的一个子集。因此上面的伪代码指的是一个选择标准,它根据算法的性质而变化。例如,等值线算法可能会检查单元格的标量值是否跨越当前等值线值。

虽然可以使用许多细分算法,但基于边缘细分的算法特别简单。该算法背后的想法很简单:每个单元格边缘e通过错误度量进行评估E如果有任何错误测量,可以标记为细分ϵiε一世超过相应的错误阈值 $\epsilon◂⋅▸e⁢p⁢s⁢i⁢l⁢o⁢n⁢ip一世n一世

屏幕截图 2022-11-20 150439

基于单元拓扑和需要细分的特定边缘,使用模板来细分单元。这个过程递归地继续,直到在所有边上都满足误差度量。该算法的一个优点是可以独立地细分单元格。这是因为边缘细分是一种或多种误差测量的函数,它只考虑沿边缘的信息,不需要考虑单元信息。因此,不需要跨单元格边界的通信,并且该算法非常适合并行处理和在遍历期间访问单元格时进行动态镶嵌。

单元格细分的一些模板显示在图 8-24. 请注意,在某些情况下,必须根据要选择的对角线进行曲面细分(例如,虚线图 8-24(b)). 在 2D 中,可以任意选择,但在 3D 中,选择必须与单元格的面邻居一致。为了保持算法的简单性,包括避免细胞间通信,采用简单的平局规则来选择 3D 细胞表面的对角线。这些规则包括使用最短对角线(在全局坐标系中测量),或使用基于选择具有最小点 ID 的对角线的拓扑决策器。(当几何距离度量不确定时,拓扑决策器是必要的。)

图8-24

图 8-24。三角形细分表中的三种情况。实心圆表示边缘被标记为细分。

 

错误措施

上述算法是自适应的,因为边缘分割是由局部网格属性和/或其与视图位置的关系控制的。由于目标是确保曲面细分的质量与可视化的特定要求一致,因此我们期望经过调整的曲面细分与具有相同数量单纯形的固定细分相比具有更好的质量,或者具有更少的单纯形同等质量的镶嵌。

我们的设计允许定义多个错误度量。如公式 8-7 所示,误差度量由多个误差度量组成,每个误差度量根据线性近似评估边缘的局部属性,并将该度量与用户指定的阈值进行比较。如果任何测量超过阈值,则边缘被细分。这些误差度量可以评估几何属性、解决方案属性的近似值或与当前视图相关的误差,以及其他可能性。基于几何或属性的误差度量与视图无关,并且网格只需要一个初始细分。

以下段落描述了在实践中发现有用的几种错误度量。由于 tessellator 旨在处理错误度量列表,因此可以直接添加新度量(通过从 GenericSubdivisionErrorMetric 类派生)和/或将其与现有错误度量相结合。

图8-25

图 8-25。几何和属性误差测量的定义。

 

  • 基于对象的几何误差测量。参考图 8-25(左),该误差度量为垂直距离,dd, 从边缘中心点CC到通过单元格边缘顶点的直线(一个AB). 注意dd是在世界坐标中计算的,但是CC通过在边缘的参数中心进行评估来计算。使用垂直距离而不是之间的距离CC4D⁢4因为如果CC位于(一个(A⁢B)但并不重合D错误不为零,导致许多无用的边缘细分。

  • 基于对象的平面度误差测量。这个误差测量是角度一个α通过实中点 C 的弦 (AC) 和 (CB) 之间。随着夹角接近 180边缘变平。阈值是边缘被视为平坦的角度。

  • 基于属性的错误度量。参考图 8-25(右),这个误差测量是之间的距离一个一世ai属性在中点的线性插值和该属性在边缘中点的实际值一个am.

  • 基于图像的几何误差测量。该误差度量是线与线之间的距离(以像素为单位)(一个(A⁢B)在图像空间中投影到中点CC也投影在图像空间中。因为计算涉及通过当前相机矩阵的投影,所以此误差测量与视图相关。因此,在远离相机的网格部分中,曲面细分可能很粗糙。请注意,此方法的缺点之一是每次相机相对于网格重新定位时都可能需要曲面细分。

高级方法

细心的读者会注意到,前面描述的细分方案可能无法捕捉到高阶基的所有特征。例如,想象一个跨三角形的标量函数,其中函数的峰值出现在三角形的中心,并且跨边缘的变化为零。前面描述的边缘细分算法不会捕获峰值,因此等值线等算法会产生不准确的结果。线性等高线算法要求满足以下条件才能产生拓扑正确的结果。

  • 每个网格边最多与特定值的等值线相交一次,

  • 没有等值线在不与面的至少两条边相交的情况下与网格面相交,并且

  • 没有等值线完全包含在单个元素中。

根据定义,这些条件与临界点直接相关,因为开放域上的可微函数的极值必然是临界点。线性网格假定标量场的所有极值都出现在元素顶点处,但通常在使用高阶基时情况并非如此,极值可以在单元内部找到。

为了解决这个问题,必须对基进行预三角测量。预三角剖分必须识别单元内部、面或边缘上的所有关键点,然后将这些点插入三角剖分中。例如,可以首先执行基于高阶单元顶点的初始三角剖分,然后使用诸如 Delaunay 三角剖分或等效方法(参见第 9 章中的“三角剖分技术” )插入到三角剖分中。然后可以在预三角测量之后进行前面介绍的标准的基于边缘的算法。

8.4 坐标变换

坐标变换是一种常见的可视化操作。这可能是从数据集坐标到全局坐标的转换,或者是从全局坐标到数据集坐标的转换。

数据集到全局坐标

数据集坐标和全局坐标之间的转换很简单。我们首先使用小区 ID 和子 ID 识别主小区。然后使用公式 8-4 的插值函数从参数坐标生成全局坐标。给定单元格点pi=pi(xi,yi,zi)全局坐标p简直是

屏幕截图 2022-11-20 151419

其中插值权重Wi 在参数坐标处被评估(r0,s0,t0).

在此处介绍的公式中,我们对数据和单元格几何结构使用了相同阶的插值函数。(我们所说的顺序是指插值多项式的多项式次数。)这称为等参数插值。可以对几何和数据使用不同的插值函数。当用于几何的插值函数的阶数大于用于数据的插值函数的阶数时,使用超参数插值。次参数当几何插值函数的阶数小于用于数据的插值函数阶数时,将使用插值法。使用不同的插值函数通常用于数值分析技术,例如有限元法。我们将始终使用等参数插值进行可视化应用。

全局到数据集坐标

与数据集到全局转换相比,全局到数据集坐标转换的成本很高。有两个原因。首先,我们必须识别特定的细胞C一世Ci包含全局点pp. 其次,我们必须求解方程 8-4 的参数坐标pp.

识别细胞C一世Ci意味着进行某种形式的搜索。一种简单但效率低下的方法是访问数据集中的每个单元格并确定是否pp位于任何细胞内。如果是这样,那么我们就找到了正确的单元格并停止搜索。否则,我们检查列表中的下一个单元格。

这种简单的技术对于大数据来说速度不够快。相反,我们使用加速搜索技术。这些基于空间组织结构,例如八叉树或三维哈希表。这个想法如下:我们创建了许多“桶”或数据占位符,可以通过它们在全局空间中的位置进行访问。在每个桶内,我们标记部分或完全位于桶内的所有点或单元格。然后,为了找到包含点 p 的特定单元格,我们找到包含 p 的桶,并获得与桶关联的所有单元格。然后,我们对这个缩写的单元格列表进行内部/外部评估,以找到包含 p 的单个单元格。(有关更详细的描述,请参阅第 8 章中的“搜索” 。)

全局到数据集坐标转换代价高昂的第二个原因是我们必须求解 p 的参数坐标的插值函数。有时我们可以通过分析来解决这个问题,但在其他情况下,我们必须使用数值技术求解参数坐标。

考虑一条线的插值函数(图 8-2). 我们可以精确求解这个方程并发现

屏幕截图 2022-11-20 150451

对于插值函数是参数坐标的线性组合的任何单元格,都存在类似的关系。这包括顶点、直线、三角形和四面体。四边形和六面体插值函数是非线性的,因为它们是参数坐标的线性表达式的乘积。因此,我们必须求助于数值技术来计算全局到数据集的坐标转换。像素和体素的插值函数也是非线性的,但由于它们相对于 x、y 和 z 坐标轴的特殊方向,我们可以精确地求解它们。(我们将在第 8 章的“图像数据特殊技术”中更深入地处理像素和体素类型。)

为了求解参数坐标的插值函数,我们必须使用非线性技术来求解方程组。一种简单有效的技术是牛顿法[Conte72]

要使用牛顿法,我们首先为已知的全局坐标定义三个函数pyz)在插值函数方面在 Wi=Wi(r,s,t)

屏幕截图 2022-11-20 150459

然后,使用泰勒级数近似展开函数,

屏幕截图 2022-11-20 150510

我们可以开发一个迭代程序来求解参数坐标。这产生了一般形式


屏幕截图 2022-11-20 150517

幸运的是,牛顿法以二次方式收敛(如果它收敛的话)并且我们在此处介绍的插值函数表现良好。实际上,公式 8-12 在几次迭代后收敛。

8.5 计算导数

插值函数使我们能够计算单元内任意位置的数据值。它们还允许我们计算数据值的变化率或导数。例如,给定单元点的位移,我们可以计算单元应变和应力,或者给定压力值,我们可以计算指定位置的压力梯度。

图8-26

图 8-26。计算一维线单元中的导数。

 

为了介绍这个过程,我们将从研究最简单的情况开始:计算一维直线中的导数 (图 8-26). 使用几何参数,我们可以根据下式计算 r 参数空间中的导数

屏幕截图 2022-11-20 150525

在哪里一世si是点处的数据值一世i. 在局部坐标系中Xx′,这平行于rr坐标系(也就是说,它位于沿⃗ t→或者X1X0)◂−▸x1→−x0→), 导数是

屏幕截图 2022-11-20 150532

在哪里l是线的长度。

推导公式 8-14 的另一种方法是使用插值函数图 8-3以及衍生品的链式法则。连锁法则

屏幕截图 2022-11-20 150539

允许我们计算导数ddX◂/▸dd⁢x′使用

屏幕截图 2022-11-20 150546

使用插值函数,我们可以计算Xx′关于衍生品rr作为

屏幕截图 2022-11-20 150554

结合方程 8-16 和方程 8-13 的 s 导数,得到方程 8-14。

剩下最后一步。中的衍生物X⃗ x→坐标系必须转换为全局坐标系− y– zx−y−z系统。我们可以通过创建一个单位向量来做到这一点⃗ v→作为

屏幕截图 2022-11-20 150600

在哪里X0x0→X1x1→是直线两端点的位置。那么导数中的Xxy, 和z可以通过沿轴取点积来计算方向。

屏幕截图 2022-11-20 150608

总结这个过程,导数是在本地计算的– – tr−s−t使用单元插值的参数空间。然后将它们转换为本地X◂−▸x′−y′−z′笛卡尔系统。然后,如果X◂−▸x′−y′−z′系统不符合全球− y– zx−y−z坐标系,需要另一个变换来生成结果。

我们可以将此过程概括为三个维度。从偏导数的链式法则

屏幕截图 2022-11-20 150615

或重新排列后

屏幕截图 2022-11-20 150623

3×33×3矩阵J称为雅可比矩阵,它将参数坐标导数与全局坐标导数联系起来。我们可以将公式 8-21 重写为更紧凑的形式

屏幕截图 2022-11-20 150630

并通过取雅可比矩阵的逆来求解全局导数

屏幕截图 2022-11-20 150638

只要参数坐标系和全局坐标系之间存在一一对应关系,雅可比行列式的逆就始终存在。这意味着对于任何t )坐标,对应的只有一个yz)协调。这适用于此处介绍的任何参数坐标系,只要避免细胞自相交或细胞自身折叠等病理情况。(单元格折叠的一个例子是当四边形变成非凸面时。)

在我们的一维示例中,沿直线的导数是常数。然而,其他插值函数(例如,图 8-5) 可能会产生非常量导数。这里,雅可比行列式是单元中位置的函数,必须在特定的位置进行评估t )坐标值。

8.6 拓扑运算

许多可视化算法需要有关单元或数据集拓扑的信息。提供此类信息的操作称为拓扑操作。这些操作的示例包括获取单元格的拓扑维度,或访问共享公共边或面的相邻单元格。我们可能会使用这些操作来决定是渲染一个单元(例如,只渲染一维线)还是通过流场传播粒子(例如,穿过公共边界的单元格)。

在继续之前,我们需要从拓扑中定义一些术语。流形拓扑描述了一个围绕拓扑连接的点的区域。也就是说,该点周围的区域在拓扑上相当于一个小“圆盘”(二维)或“球”(三维)。非流形的拓扑称为非流形。流形和非流形几何的示例显示在图 8-27.

我们可以使用一些简单的规则来决定用单元近似的表面或区域是流形的还是非流形的。在二维中,如果二维单元的每条边都恰好被另一个单元使用,则该表面是局部流形的。在三维空间中,如果一个三维单元的每个面都恰好被另一个单元使用,则该区域是局部流形的。

我们还会在某些场合使用术语单纯形。维数为 n 的单纯形是由一组 n+1 个独立点定义的凸区域。顶点、直线、三角形和四面体分别是维数为 0、1、2 和 3 的单纯形,如下所示图 8-28.

屏幕截图 2022-11-20 150647

 

图8-27

图 8-27。流形和非流形表面拓扑。如果一个顶点周围的局部邻域在拓扑上是一个二维圆盘(即,一个小圆盘可以放置在表面上而不会撕裂或重叠),那么该表面在该顶点处是流形的。

 

图8-28

图 8-28。三维和更低维度的单纯形。

 

细胞操作

单元操作返回有关单元拓扑的信息。通常,我们想知道单元的拓扑顺序或单元边界的拓扑。

给定一个单元格C一世Ci拓扑维数为 d 的单元格(隐含地)由拓扑阶数为 d-1 及更低的边界单元格组成。例如,一个四面体由四个二维三角形、六个一维边和四个零维顶点组成。单元操作返回有关特定拓扑维度的边界单元数的信息,以及定义每个边界单元的点的有序列表。

另一个有用的单元操作返回最近的边界单元 d-1 给定单元的参数坐标。与将拓扑与几何联系起来的参数坐标系相比,此操作将几何与单元格的拓扑联系起来。最近边界单元操作是通过将每个单元划分为不同的区域来实现的,如中所示图 8-29. 要确定最近的边界单元,我们只需要识别该点所在的参数区域,然后返回适当的边界单元。

另一个有用的单元操作是将单元分解为单纯形。每个单元格都可以分解为一组单纯形。通过这样做,并通过对单纯形分解而不是细胞本身进行操作,我们可以创建独立于细胞类型的算法。例如,如果我们想要对不同细胞类型的两个数据集进行交集,如果没有单纯形分解,我们将不得不创建方法来对每个可能的细胞组合进行交集。通过单纯形分解,我们可以创建一个仅对有限的单纯形集进行运算的交集运算。这种方法的显着优点是,当新的单元格添加到可视化系统时,只需实现单元格对象(包括其单纯形分解方法),无需修改其他对象。

图8-29

图 8-29。四边形单元格的最近边界单元格操作。

 

数据集操作

数据集操作返回有关数据集拓扑的信息或有关单元邻接的拓扑信息。典型的操作包括确定单元格的邻居或返回使用特定点的所有单元格的列表。

我们可以通过继续第 5 章中“单元格类型”的讨论来形式化邻接操作。邻接方法用于获取有关单元格邻居的信息。特定小区的邻居C一世Ci只是一个共享一个或多个共同点的单元格C一世Ci. 顶点邻居是共享一个或多个顶点的邻居。边邻居是共享一条或多条边的邻居。面邻居是共享顶点的单元格,这些顶点定义了单元格的一个面。请注意,面邻居也是边邻居,边邻居也是顶点邻居。

邻接运算符是简单的集合运算。对于特定的细胞C一世Ci由点和点列表定义P⃗ =(p1,p2,...,pn)◂=▸P→=(◂,▸p1→,p2→,…,pn→)PPP⊂P, 在哪里PP通常对应于定义边界单元格的点C一世Ci; 的邻居C一世Ci是邻接集 A(C, P)。

邻接集只是每个点的使用集的交集,不包括单元格C一世Ci.

屏幕截图 2022-11-20 150700

和一个点列表 P⃗ =(p⃗ 1,p⃗ 2,...,p⃗ n)P⃗ P, 在哪里PP通常对应于定义边界单元格的点C一世Ci; 的邻居C一世Ci是邻接集一个(C⃗ ,P⃗ )A⁡(◂,▸C→,P→). 邻接集只是每个点的使用集的交集,不包括单元格Ci

邻接集代表各种有用的信息。例如,在由多面体表示的流形对象中,每个多边形的每条边都必须恰好有一个边邻居。没有邻居的边是边界边;具有多个边邻居的边表示非流形拓扑。由三维单元(例如,非结构化网格)组成的数据集在拓扑上是一致的,前提是对于每个单元,每个面都恰好有一个面邻居。没有邻居的面在数据集的边界上。多于一个面的邻居意味着邻居是自相交的(在 3D 空间中)。

屏幕截图 2022-11-20 150709

8.7 搜索

搜索是查找包含指定点 p 的单元格或定位 p 周围区域中的单元格或点的操作。需要此操作的算法包括流线生成,我们需要在其中找到单元格内的起始位置;探测,其中某个点的数据值是从包含的单元格中插入的;或碰撞检测,其中必须评估特定区域中的单元格是否相交。有时(例如,图像数据集),由于数据的规律性,搜索是一种简单的操作。然而,在结构化程度较低的数据中,搜索操作更为复杂。

要找到包含 p 的单元格,我们可以使用以下简单搜索过程。遍历数据集中的所有单元格,找到包含 p 的单元格(如果有的话)。为了确定一个单元格是否包含一个点,单元格插值函数针对参数坐标 (r,s,t) 进行评估。如果这些坐标位于单元格内,则pp位于细胞中。这里的基本假设是单元不重叠,因此最多一个单元包含给定点 p。为了确定位于 p 周围区域的单元格或点,我们可以遍历单元格或点以查看它们是否位于 p 周围的区域内。例如,我们可以选择将区域定义为以 p 为中心的球体。然后,如果组成一个单元的一个或多个点位于球体中,则该点或单元被认为位于 p 周围的区域中。

除了最小的数据集之外,这些简单的过程对于所有数据集都是不可接受的,因为它们的顺序为 O(n),其中 n 是单元格或点的数量。为了提高搜索性能,我们需要引入补充数据结构来支持空间搜索。这样的结构是众所周知的,包括 MIP 映射、八叉树、kd 树和二叉球体树(参见本章末尾的“参考文献注释” )。

这些空间搜索结构背后的基本思想是将搜索空间细分为更小的部分或桶。每个桶都包含位于其中的点或单元格的列表。桶以结构化方式组织,因此可以对任何桶进行恒定或对数时间访问。例如,如果我们将 2D 欧几里得空间的一部分分配到 n x m 桶的网格中,则pp在一个特定的桶中可以用两次减法和两次除法来确定:一个恒定的时间访问。同样,位置pp在非均匀细分的八叉树中,以对数时间确定,因为需要递归插入八分圆子项。找到桶后,搜索将限于其中包含的点或单元格。在设计合理的空间搜索结构中,桶中点或单元的数量是单元总数的一小部分,并且小于固定值。因此,在桶内搜索的时间可以由固定常数限制。结果是引入空间搜索结构将搜索时间减少到最大 O(log n),或者更好的是 O(n)。

在应用空间搜索结构时,我们有两种选择。我们可能会在搜索结构中插入点,或者我们可能会插入单元格,具体取决于应用程序。两种方法各有利弊。将单元格插入桶中并不是一项微不足道的操作。一般来说,细胞的方向和形状是任意的,不会完全适合一个桶。因此,单元通常跨越多个桶。要可靠地确定一个单元格是否在桶中,需要进行几何相交测试,这是一项代价高昂的操作。另一种方法是使用单元格的边界框来决定单元格属于哪个桶。我们只需要将边界框与桶相交即可确定单元格是否属于桶中。不幸的是,尽管这个操作通常很快,

图8-30

图 8-30。使用搜索结构(包含点)来查找单元格。(a) 点与适当的桶相关联。点 $p$ 用于索引到桶中,并找到最近的点 pi。使用 pi 的单元格针对包含 p 的单元格进行评估。(b) 有时最近的点 pi 不被包含 p 的单元格使用。

 

将点插入搜索结构更容易,因为点可以唯一地放入桶中。插入点还允许我们搜索点和单元格。可以通过使用找到单元格pp索引到适当的桶中。最近点p一世pipp然后位于。使用拓扑邻接运算符检索使用点 pi 的单元格,然后我们可以在这些单元格中搜索包含的单元格pp. 但是,必须谨慎使用此过程,因为最近的点可能不会被包含的单元格使用pp (图 8-30).

8.8 单元格/行交叉

一个重要的几何操作是线与单元格的交集。此操作可用于从渲染窗口交互式地选择一个单元格,执行光线投射以进行渲染,或以几何方式查询数据。

可视化工具包中,每个单元格都必须能够与一条线相交。图 8-31总结了 VTK 支持的九种线性原代细胞类型的这些操作。(复合单元的交集是通过依次与每个原始单元相交来实现的。)请注意,与高阶单元相交的过程是相同的。

0D、1D 和 2D 单元格的线/单元格交集遵循标准方法。与 3D 单元格的交集很困难。这是因为这些单元的表面是参数化描述的,不一定是平面的。例如,要使一条线与四面体相交,我们可以将线与四面体的四个三角形面相交。然而,六面体可能有非平面的面。因此,我们不能将直线与六个四边形平面相交。相反,我们使用线/面交点作为初始猜测,并将交点投影到单元格的表面上。这会产生一个近似的结果,但对于大多数应用来说已经足够准确了。

图8-31

图 8-31。九种原始细胞类型的线/细胞相交操作总结。假定线在参数坐标 t 中以 $0 \leq t \leq 1$ 归一化。

 

8.9 标量和颜色

标量数据和颜色之间存在密切的对应关系。我们在第 6 章的“颜色映射”中谈到了这一点,在那里我们看到了如何使用颜色表将标量值映射到颜色规范(即红色、绿色、蓝色和 alpha,或 RGBA)。然而,在某些情况下,我们想要规避此映射过程。当提供颜色数据而不是标量数据时,就会发生这种情况。

一个常见的例子发生在成像中。回想一下,图像是规则的二维点阵列。这些点定义像素,像素又形成二维图像数据集。图像经常存储为一对维度以及数据值。数据值可以是黑白(例如,位图)、灰度或彩色(例如,像素图)之一。位图和灰度图像可以直接转换为单值标量数据的形式,我们可以使用我们之前的方法。然而,像素图由(至少)每个像素的红色、绿色和蓝色三个值组成。(有时,还可能包括第四个 alpha 不透明度值。)因此,像素图不能直接转换为标量形式。

为了容纳颜色数据,必须定义多分量颜色数据和单值标量之间的转换。每个类都必须像标量一样工作:也就是说,对特定点的数据的请求必须返回单个标量值。这使我们能够使用标准的标量可视化技术,例如等高线或变形。因此需要从 RGB 或 RGBA 颜色坐标到单个标量值的映射。

最简单的转换是选择颜色元组中的 n 个分量之一,并将其​​用作标量值。另一个常见的映射返回颜色的亮度 Y。给定三个分量 RGB,亮度为

屏幕截图 2022-11-20 150720

如果颜色包括透明度、RGBA,则亮度为

屏幕截图 2022-11-20 150727

使用这种抽象允许我们将单值标量和由多值颜色组成的标量视为相同。最终结果是我们可以将两种类型的标量数据混合到我们的可视化网络中。

8.10 图像数据的特殊技术

使用 2 维和 3 维图像数据的一个重要吸引力是计算的速度和简单性。在本节中,我们将探讨利用图像数据的特殊规则拓扑和几何形状的具体技术。

坐标变换

给定一分pp我们可以通过执行三个除法运算来找到结构坐标(图 8-32). 采用整数 floor 函数产生结构化坐标。取结果的小数部分得到单元格的参数坐标。然后我们可以使用公式 8-3 转换为数据集坐标。

图8-32

图 8-32。图像数据坐标变换。

 

导数计算

因为图像数据集的方向平行于坐标 x、y 和 z 轴,并且因为每个方向上的点间距是规则的,所以可以使用有限差分格式来计算像元点处的偏导数。参考图 8-33,我们看到中心差异可以根据等式用于三个方向中的每一个:

屏幕截图 2022-11-20 150737

(请注意,在数据集的边界,可能会使用单边差异。)我们也可以使用这些方程来计算单元内的导数。我们只需根据公式 8-28 计算每个单元格点处的导数,然后使用单元格插值函数计算单元格内点处的导数。

图8-33

图 8-33。使用有限差分计算图像数据的导数。

 

拓扑结构

结构化数据集有助于高效的拓扑操作(即图像数据和结构化网格)。给定一个单元 ID,可以使用简单的常量时间操作来确定顶点、边或面的邻居。首先,给定三维结构化数据集中的单元格 ID,我们使用除法和模运算的组合来计算结构化坐标

屏幕截图 2022-11-20 150745

面部邻居通过递增 i、j 或 k 索引之一来确定。边缘邻居是通过递增任意两个索引来确定的,而顶点邻居是通过递增所有三个索引来找到的。递增时必须小心,以确保指数落在范围内

屏幕截图 2022-11-20 150752

尝试索引超出这些范围表明所讨论的邻居不存在。

搜索中

给定一分yz)◂=▸p=(x,y,z)我们可以确定包含的单元格pp通过使用给出的方程式图 8-32. 这些方程生成结构坐标(k )(i,j,k),然后可以使用公式 8-3 将其转换为单元 ID(即数据集坐标)。

找到最近的点pp,我们通过舍入到最接近的整数值(而不是使用 floor 函数)来计算结构化坐标。因此,

屏幕截图 2022-11-20 150800

8.11 把它们放在一起

在本节中,我们将完成之前对非结构化数据实现的描述。我们还为单元格和数据集定义了一个高级抽象接口。该接口允许我们在Visualization Toolkit中实现通用(即,数据集特定)算法。我们还描述了颜色标量、搜索和拾取的实现,并以一系列示例作为结尾来演示其中的一些概念。

非结构化拓扑

第 5 章 – 数据表示中,我们描述了非结构化数据集类型vtkPolyDatavtkUnstructuredGrid的数据表示。仔细检查此数据结构会发现检索拓扑邻接的操作效率低下。事实上,要实现检索顶点、边或面邻居的任何操作,都需要搜索元胞数组,从而导致 O(n) 时间复杂度。除了最小的应用程序之外,这对于所有应用程序都是不可接受的,因为遍历元胞数组和检索邻接信息的任何算法的最小 O(n2)。

这种低效率的原因是数据表示是一个“向下”的层次结构(图 8-34(b)). 也就是说,给定一个单元格,我们可以快速确定拓扑层次结构中较低的拓扑特征,例如面、边和点。但是,给定面、边或点,我们必须搜索元胞数组以确定所属元胞。为了提高这种数据表示的效率,我们必须在允许“向上”层次结构遍历的层次结构中引入额外的信息(类似于图 8-34(一个))。

图8-34
图 8-34。增强分层非结构化数据表示。(a) 几何模型的传统拓扑层次。(b) 基本的非结构化数据层次结构。(c) 完整的非结构化数据层次结构。通过引入从点到单元格的向上引用,非结构化数据层次结构可以在两个方向上高效地遍历,并且比传统的拓扑层次结构更紧凑。

图8-35

图 8-35。完整的非结构化数据表示,包括链接列表。有 m 个单元格和 n 个点。链接列表中的 n 个结构是使用每个顶点的单元列表。每个链接列表的长度都是可变的。

 

这个问题的解决方案是用单元链接扩展非结构化数据结构。单元格链接数组是使用每个点的单元格列表的列表,对应于图 8-34(C)。单元格链接数组转换的层次结构图 5-13成环形结构。单元格引用它们的组成点,而点又引用使用它们的单元格。完整的非结构化数据结构显示在图 8-35.单元格链接数组实际上是使用公式 5-1 的集合的一个实现。如果使用一个点的最大像元数远小于数据集中的点数,我们可以使用这个方程在恒定时间内计算邻接操作。为了看到这一点,我们参考公式 8-25 并看到邻接操作由有限数量的集合交集组成。每个操作都是每个点的链接列表的交集。如果每个链表中的单元格数量“少”,那么交集操作可以在时间上以固定常数为界,并且总操作可以被认为是常数时间操作。

这种数据表示有几个重要的特征。

  • 单元格链接数组是基本非结构化数据表示的扩展。因此,我们可以推迟单元链接的构建,直到需要它们为止。通常永远不需要单元格链接,也不需要计算机资源来计算或存储。

  • 构建单元链接是一个线性 O(n) 操作。遍历每个单元格,并且对于该单元格使用的每个点,该点的使用单元格列表被扩展以包括当前单元格。构建单元格链接只需要作为初始化步骤一次。

  • 相对于其他拓扑表示方案(例如,翼边结构和径向边结构[Baumgart74] [Weiler88]),数据表示是紧凑的。这些其他数据结构包含中间拓扑的显式表示,例如边、环、面,或特殊的邻接信息,例如相邻边(翼边结构)或广泛的“使用”描述(径向边结构)。表示的紧凑性对于可视化尤为重要,因为数据量通常很大。

可视化工具包中的非结构化数据结构是使用四个类vtkPoints(和子类)、vtkCellArrayvtkCellTypesvtkCellLinks 实现的。这种数据结构的构建是增量的。至少,点和单元格使用vtkPointsvtkCellArray表示。如果需要随机访问或额外的类型信息,则使用对象vtkCellTypes。如果需要邻接信息,类vtkCellLinks的实例被建造。这些操作在幕后进行,一般不需要应用程序员额外的知识。

抽象接口

完成第5章和第8章,我们可以总结出单元格、数据集和点数据属性的抽象接口。这些伪代码描述封装了类vtkDataSetvtkCellvtkPointData及其子类的核心功能。本文中介绍的所有算法都可以使用这些方法的组合来实现。

数据集抽象。数据集是 VTK 中的中心数据表示。数据集由一个或多个单元格和点组成。与点相关联的是属性数据,包括标量、向量、法线、纹理坐标和张量。

     type = GetDataObjectType()
          Return the type of dataset (e.g., vtkPolyData, vtkImageData, vtkStructuredGrid, vtkRectilinearGrid, or vtkUnstructuredGrid).

     numPoints = GetNumberOfPoints()
          Return the number of points in the dataset.

     numCells = GetNumberOfCells()
          Return the number of cells in the dataset.

     GetPoint(ptId,x)
          Given a point id, return the (x,y,z) coordinates of the point.

     cell = GetCell(cellId)
          Given a cell id, return a pointer to a cell object.

     type = GetCellType(cellId)
          Return the type of the cell given by cell id.

     GetCellTypes(types)
          Return a list of types of cells that compose the dataset.

     cells = GetPointCells(ptId)
          Given a point id, return the cells that use this point.

     GetCellPoints(cellId, ptIds)
          Given a cell id, return the point ids (e.g., connectivity list) defining the cell.

     GetCellNeighbors(cellId, ptIds, neighbors)
          Given a cell id and a list of points composing a boundary face of the cell, return the neighbors of that cell sharing the points.

     cellId = FindCell(x, cell, cellId, tol2, subId, pcoords, weights)
          Given a coordinate value x, an initial search cell defined by cell and cellId, and a tolerance measure (squared), return the cell id and sub-id of the cell containing the  point and its interpolation function weights. The initial search cell is used to speed up the search process when the position x is known to be near the cell. If no cell is found, cellId < 0 is returned.

     pointData = GetPointData()
         Return a pointer to the object maintaining point attribute data. This includes scalars, vectors, normals, tensors, and texture coordinates, as well as any other data arrays that the field carries.

     cellData = GetCellData()
          Return a pointer to the object maintaining cell attribute data. This includes scalars,a vectors, normals, tensors, and texture coordinates, as well as any other data arrays that the field carries.

     bounds = GetBounds()
          Get the bounding box of the dataset.

     length = GetLength()
          Return the length of the diagonal of the bounding box of the dataset.

     center = GetCenter()
          Get the center of the bounding box of the dataset.

     range = GetScalarRange()
          A convenience method to return the (minimum, maximum) range of the scalar attribute data associated with the dataset.

     dataSet = NewInstance()
          Make a copy of the current dataset. A "virtual" constructor. (Typically, reference counting methods are used to copy data.)

     CopyStructure(dataSet)
          Update the current structure definition (i.e., geometry and topology) with the supplied dataset.

细胞抽象。细胞是 VTK 的原子结构。单元由由一系列有序点 ID 定义的拓扑和由点坐标定义的几何组成。单元格坐标由单元格 ID、子单元格 ID 和参数坐标组成。subid 指定位于复合单元格(如三角形带)内的主单元格。单元格的边和面是从单元格的拓扑结构中隐式定义的。

    type = GetCellType()
          Return the type of the cell. Must be one of the twelve