一:主要的知识点
1、说明
本文只是教程内容的一小段,因博客字数限制,故进行拆分。主教程链接:https://mp.csdn.net/mp_blog/creation/editor/151804258
2、知识点纪要
本段代码主要涉及的有①vtkTessellatorFilter 的作用,②
二:代码及注释
python
# !/usr/bin/env python
# -*- coding: utf-8 -*-
# noinspection PyUnresolvedReferences
import vtkmodules.vtkInteractionStyle
# noinspection PyUnresolvedReferences
import vtkmodules.vtkRenderingFreeType
# noinspection PyUnresolvedReferences
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import (
vtkColorSeries,
vtkNamedColors
)
from vtkmodules.vtkCommonCore import (
vtkIntArray,
vtkLookupTable,
vtkMinimalStandardRandomSequence,
vtkPoints
)
from vtkmodules.vtkCommonDataModel import (
VTK_CUBIC_LINE,
VTK_HEXAHEDRON,
VTK_LINE,
VTK_PYRAMID,
VTK_QUAD,
VTK_QUADRATIC_EDGE,
VTK_QUADRATIC_HEXAHEDRON,
VTK_QUADRATIC_PYRAMID,
VTK_QUADRATIC_QUAD,
VTK_QUADRATIC_TETRA,
VTK_QUADRATIC_TRIANGLE,
VTK_QUADRATIC_WEDGE,
VTK_TETRA,
VTK_TRIANGLE,
VTK_WEDGE,
vtkCellTypes
)
from vtkmodules.vtkFiltersGeneral import (
vtkShrinkFilter,
vtkTessellatorFilter
)
from vtkmodules.vtkFiltersSources import vtkCellTypeSource
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkActor2D,
vtkDataSetMapper,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkRenderer,
vtkTextMapper,
vtkTextProperty
)
def main():
cellName = get_program_parameters()
# Store the cell class names in a dictionary.
cellMap = dict()
cellMap[vtkCellTypes.GetClassNameFromTypeId(VTK_LINE)] = VTK_LINE
cellMap[vtkCellTypes.GetClassNameFromTypeId(VTK_QUADRATIC_EDGE)] = VTK_QUADRATIC_EDGE
cellMap[vtkCellTypes.GetClassNameFromTypeId(VTK_CUBIC_LINE)] = VTK_CUBIC_LINE
cellMap[vtkCellTypes.GetClassNameFromTypeId(VTK_TRIANGLE)] = VTK_TRIANGLE
cellMap[vtkCellTypes.GetClassNameFromTypeId(VTK_QUADRATIC_TRIANGLE)] = VTK_QUADRATIC_TRIANGLE
cellMap[vtkCellTypes.GetClassNameFromTypeId(VTK_QUAD)] = VTK_QUAD
cellMap[vtkCellTypes.GetClassNameFromTypeId(VTK_QUADRATIC_QUAD)] = VTK_QUADRATIC_QUAD
cellMap[vtkCellTypes.GetClassNameFromTypeId(VTK_TETRA)] = VTK_TETRA
cellMap[vtkCellTypes.GetClassNameFromTypeId(VTK_HEXAHEDRON)] = VTK_HEXAHEDRON
cellMap[vtkCellTypes.GetClassNameFromTypeId(VTK_WEDGE)] = VTK_WEDGE
cellMap[vtkCellTypes.GetClassNameFromTypeId(VTK_PYRAMID)] = VTK_PYRAMID
cellMap[vtkCellTypes.GetClassNameFromTypeId(VTK_QUADRATIC_WEDGE)] = VTK_QUADRATIC_WEDGE
cellMap[vtkCellTypes.GetClassNameFromTypeId(VTK_QUADRATIC_PYRAMID)] = VTK_QUADRATIC_PYRAMID
cellMap[vtkCellTypes.GetClassNameFromTypeId(VTK_QUADRATIC_HEXAHEDRON)] = VTK_QUADRATIC_HEXAHEDRON
cellMap[vtkCellTypes.GetClassNameFromTypeId(VTK_QUADRATIC_TETRA)] = VTK_QUADRATIC_TETRA
if cellName not in cellMap:
print('Cell type ', cellName, ' is not supported.')
return
source = vtkCellTypeSource()
source.SetCellType(cellMap[cellName])
source.Update()
print('Cell: ', cellName)
originalPoints = source.GetOutput().GetPoints()
points = vtkPoints()
points.SetNumberOfPoints(source.GetOutput().GetNumberOfPoints())
rng = vtkMinimalStandardRandomSequence()
rng.SetSeed(5070) # for testing
for i in range(0, points.GetNumberOfPoints()):
perturbation = [0.0] * 3
for j in range(0, 3):
rng.Next()
perturbation[j] = rng.GetRangeValue(-0.1, 0.1)
currentPoint = [0.0] * 3
originalPoints.GetPoint(i, currentPoint)
points.SetPoint(i, currentPoint[0] + perturbation[0],
currentPoint[1] + perturbation[1],
currentPoint[2] + perturbation[2])
source.GetOutput().SetPoints(points)
"""
VTK 数据集中的每个单元格(cell)分配一个唯一的 ID,并将其作为单元格数据(cell data)
附加到数据集中,以便后续可以根据这些 ID 进行着色或处理
"""
numCells = source.GetOutput().GetNumberOfCells()
print('Number of cells: ', numCells)
idArray = vtkIntArray() # 创建int数组,用来存储和保存每个单元的ID
idArray.SetNumberOfTuples(numCells) # 为数组分配内存空间
for i in range(0, numCells):
# 在数组的第 i 个位置插入整数 i + 1。这确保了每个单元格都有一个从 1 到 numCells 的唯一 ID
idArray.InsertTuple1(i, i + 1)
idArray.SetName('Ids') # 赋予idArray一个名称
"""
将 idArray 作为单元格数据附加到数据集中
VTK 数据集分为点数据(PointData)和单元格数据(CellData)
将 ID 作为 CellData 意味着每个 ID 都与一个单元格相关联
"""
source.GetOutput().GetCellData().AddArray(idArray)
"""
这行代码将刚刚添加的 'Ids' 数组设置为活动标量(active scalars)。
在 VTK 中,"标量"通常是用来着色的数据。通过将 Ids 设置为活动标量,
你在告诉 vtkDataSetMapper 或其他处理单元格数据的过滤器:
"请使用这个名为 'Ids' 的数组来获取每个单元格的颜色或数值"
"""
source.GetOutput().GetCellData().SetActiveScalars('Ids')
shrink = vtkShrinkFilter()
shrink.SetInputConnection(source.GetOutputPort())
shrink.SetShrinkFactor(.8)
"""
vtkTessellatorFilter 是 VTK 中的一个细分过滤器,它的作用是将复杂或高阶的几何单元格分解成更简单、更低阶的单元格。
你可以把它想象成一个"几何体分解器",它能把一个复杂的形状(比如一个扭曲的四面体)转换成许多小的、规则的形状(比如许多小的、平直的三角形)。
什么是高阶单元格?
VTK 中的单元格可以是线性的(由直边构成)或高阶的(由曲线或曲面构成)。例如:
线性:vtkTetra(由4个平面三角形构成)、vtkLine(由2个点构成)。
高阶:vtkQuadraticTetra(由弯曲的边和面构成)、vtkCubicLine(由一条曲线构成)。
高阶单元格可以更精确地表示复杂的、弯曲的几何形状,但许多图形硬件和渲染算法只能直接处理简单的线性单元格(如三角形和四边形)。
vtkTessellatorFilter 的作用
vtkTessellatorFilter 的任务就是解决这个问题:
它接收一个包含高阶单元格的 VTK 数据集作为输入。
它根据你设定的细分级别,将每个高阶单元格分解成一系列小的、线性的单元格。
它输出一个新的 VTK 数据集,这个数据集只包含这些简单的、可渲染的线性单元格。
例如,如果你给它一个 vtkQuadraticTetra(二次四面体),它会将其分解成多个 vtkTetra(线性四面体)。
为什么需要它?
渲染:如前所述,大多数图形管线无法直接渲染高阶几何体,所以需要通过细分将其转换为简单的三角形或四边形。
计算:许多算法,例如表面积或体积计算,需要简单、线性的单元格才能高效地工作。
总之,vtkTessellatorFilter 是 VTK 管道中一个非常重要的工具,它弥合了高阶复杂几何和低阶高效计算之间的差距。
"""
tessellate = vtkTessellatorFilter()
tessellate.SetInputConnection(shrink.GetOutputPort())
tessellate.SetMaximumNumberOfSubdivisions(3)
"""
创建了一个查找表实例。查找表是 VTK 中一个核心的概念,它的作用是将数据值映射到颜色。
你可以将它看作一个"字典"或"表格",它能将一个数值(如 1、2、3)转换成一个特定的颜色(如红色、蓝色、绿色)
"""
lut = vtkLookupTable()
"""
这里创建了一个颜色系列实例。vtkColorSeries 提供了多套经过专业设计的颜色方案,
它们能让你的可视化效果更专业、更容易理解。你不需要自己去选择颜色
"""
colorSeries = vtkColorSeries()
"""
这行代码选择了特定的颜色方案。BREWER_QUALITATIVE_SET3 是一种定性颜色方案,
它包含了一组颜色,这些颜色彼此之间易于区分,但没有内在的顺序。
"""
seriesEnum = colorSeries.BREWER_QUALITATIVE_SET3
"""
将选择的颜色方案应用到 colorSeries 对象上
"""
colorSeries.SetColorScheme(seriesEnum)
# 将前面选定的颜色方案加载到查找表中
colorSeries.BuildLookupTable(lut, colorSeries.ORDINAL)
# Fill in a few known colors, the rest will be generated if needed.
colors = vtkNamedColors()
# Create a mapper and actor.
mapper = vtkDataSetMapper()
#mapper.SetInputConnection(source.GetOutputPort())
#mapper.SetInputConnection(shrink.GetOutputPort())
"""
设置了标量数据的范围
这告诉 VTK,你的数据值在这个范围内变化,查找表(lookup table)会根据这个范围来正确地映射颜色
"""
mapper.SetScalarRange(0, numCells + 1)
# 根据颜色映射表,将具体数值转换为颜色
mapper.SetLookupTable(lut)
# 在着色时,应该使用单元格数据
mapper.SetScalarModeToUseCellData()
"""
这是一个用于解决**共面拓扑(coincident topology)**问题的技术
当两个面或多边形在三维空间中完全重合时,渲染引擎可能会在它们之间来回闪烁,这被称为"Z-fighting"
PolygonOffset 是一种解决方案,它会稍微偏移其中一个面,使其不再完全重合,从而消除闪烁问题。这通常用于渲染带有边框的多边形,以确保边框线不会与面闪烁
"""
mapper.SetResolveCoincidentTopologyToPolygonOffset()
if (source.GetCellType() == VTK_QUADRATIC_PYRAMID or
source.GetCellType() == VTK_QUADRATIC_WEDGE):
mapper.SetInputConnection(shrink.GetOutputPort())
else:
mapper.SetInputConnection(tessellate.GetOutputPort())
actor = vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().EdgeVisibilityOn()
# actor.GetProperty().SetLineWidth(3)
textProperty = vtkTextProperty()
textProperty.SetFontSize(20)
textProperty.SetJustificationToCentered()
textProperty.SetColor(colors.GetColor3d('Lamp_Black'))
textMapper = vtkTextMapper()
textMapper.SetInput(cellName)
textMapper.SetTextProperty(textProperty)
textActor = vtkActor2D()
textActor.SetMapper(textMapper)
textActor.SetPosition(320, 20)
# Create a renderer, render window, and interactor.
renderer = vtkRenderer()
renderWindow = vtkRenderWindow()
renderWindow.SetWindowName('CellTypeSource')
renderWindow.AddRenderer(renderer)
renderWindowInteractor = vtkRenderWindowInteractor()
renderWindowInteractor.SetRenderWindow(renderWindow)
# Add the actors to the scene.
renderer.AddViewProp(textActor)
renderer.AddActor(actor)
renderer.SetBackground(colors.GetColor3d('Silver'))
renderer.ResetCamera()
renderer.GetActiveCamera().Azimuth(30)
renderer.GetActiveCamera().Elevation(30)
renderer.ResetCameraClippingRange()
# Render and interact.
renderWindow.SetSize(640, 480)
renderWindow.Render()
renderWindowInteractor.Start()
def get_program_parameters():
import argparse
description = 'Cell Type Source.'
epilogue = '''
You can supply an optional argument consisting of a vtkCell name e.g: vtkTriangle.
The default is vtkTetra.
'''
parser = argparse.ArgumentParser(description=description, epilog=epilogue,
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('cell_name', nargs='?', const='vtkTetra', default='vtkWedge', type=str, help='The cell name.')
args = parser.parse_args()
return args.cell_name
if __name__ == '__main__':
main()