一:教程前提
(1)说明
本博客与其说是教程,不如说是编者在学习过程中笔记的分享。编者也是初入vtk学习的大门,在学习vtk的过程中,想将自己的一些学习笔记与心得分享给大家。里面的部分关于对代码的理解也是有可能是有错误的,因此读者若发现有误,烦请在评论区不吝赐教。
(2)示例代码版本介绍
编者基于Python3.10版本,vtk版本为9.5.0
(3)教程内容说明
教程内容是编者根据vtk官方所有的python示例代码进行演示的,示例代码地址为:https://examples.vtk.org/site/Python/

教程内容里面的逻辑可能存在一些混乱,图形图像的处理并没有分门别类。主要是考虑到不同内容混乱的学习,也有利于多方面内容的对比与理解,因此并没有将演示内容逐个归类进行讲解。
在下面的第二大章节内容中。前面的为序号,英文为官方示例代码对应的示例名称,中文后缀则是对整篇代码核心内容的简介。读者只需逐个按照序号进行学习,相信能够对vtk有一个初步的认识与了解。
部分官方示例代码,在本博客中并没有体现,主要有两个原因,一是因为与前面内容有所重复,因此不想浪费时间,二是因为作者本人没有特别吃透里面的逻辑,因此暂时没有更新。
部分官方示例代码,本人觉得过于繁琐,不利于抓住vtk核心内容,因此部分内容是去除了一小部分的冗余代码的个人的简化版本,读者在对比时,发现若有不同,可不必理会。
(4)示例代码对应的源文件下载地址
官方示例数据下载地址:https://gitlab.kitware.com/vtk/vtk-examples/-/archive/master/vtk-examples-master.zip 读者只需按照这个链接进行下载并解压,即可找到每个示例代码所对应的源文件
二:示例代码
001:PolyDataToImageDataConverter 三维网格模型数据转换体素数据
002:ReadExodusData读取并可视化ExodusII格式的有限元分析结果数据,并根据制定的节点变量进行渲染
003:ReadPLOT3D读取和可视化 PLOT3D 格式的计算流体力学 (CFD) 数据
004:ReadSLC读取slice格式的三维体数据,并进行等值面提取与可视化
006:ReadUnstructuredGrid读取非结构化数据
008:TransientHDFReader读取并动画化显示一个包含瞬态(时间序列)数据的 vtkhdf 文件
009:3DSImporter导入并显示一个.3ds格式的三维模型文件
011:ReadLegacyUnstructuredGrid读取结构化文件
012:WriteLegacyLinearCells生成并保存多种类型的非结构化网格文件
013:HDRReader读取并显示一个 HDR(高动态范围)图像文件
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkIOImage import vtkHDRReader
from vtkmodules.vtkInteractionImage import vtkImageViewer
from vtkmodules.vtkRenderingCore import vtkRenderWindowInteractor
def main():
file_name = "spiaggia_di_mondello_1k.hdr"
reader = vtkHDRReader()
if not reader.CanReadFile(file_name): # 检查文件是否可读
print("can not read file")
return
reader.SetFileName(file_name)
reader.UpdateInformation()
# 获取数据的空间范围,会返回一个包含六个整数的元组或列表:[xmin, xmax, ymin, ymax, zmin, zmax]
we = reader.GetDataExtent()
extents = [we[0], we[1], we[2], we[3], 0, 0]
reader.UpdateExtent(extents) # 获取范围之内的数据,简介取代了Updata的作用
# reader.Update()
# visualize
imgviewer = vtkImageViewer()
imgviewer.SetInputData(reader.GetOutput())
"""
设置窗宽
控制图像的对比度,窗宽越大,对比度越低
值大 → 灰度拉伸范围大 → 对比度低 → 图像更灰、更平
值小 → 范围窄 → 对比度高 → 细节容易“过曝/过黑”
"""
imgviewer.SetColorWindow(1)
"""
设置窗位
值大 → 整体变亮
值小 → 整体变暗。
控制图像的亮度,窗位值决定了哪个灰度值会显示为中等亮度
高动态范围(HDR)图像的像素值范围可能非常大,比如从 0 到 100000。
如果直接显示,图像会非常暗,因为大部分像素值都集中在低端。
通过设置窗宽和窗位,你可以只显示你感兴趣的像素值范围,从而让图像内容更清晰可见
"""
imgviewer.SetColorLevel(1)
imgviewer.SetPosition(0, 100)
imgviewer.Render()
iren = vtkRenderWindowInteractor()
imgviewer.SetupInteractor(iren)
imgviewer.GetRenderWindow().SetWindowName("HDRReader")
"""
注册一个事件监听器,让程序能够相应用户的特定交互行为
EndInteractionEvent VTK 中预定义的事件名称。它表示“交互结束事件”。
当用户完成一个交互动作(例如,在调整窗宽窗位后松开鼠标左键,或者完成一次缩放操作后停止拖动鼠标)时,
这个事件就会被触发
代码中的鼠标交互功能是 VTK 内置的,而回调函数只是利用这个功能来展示窗宽窗位是如何变化的
"""
iren.AddObserver("EndInteractionEvent", ColorCallback(imgviewer))
iren.Start()
class ColorCallback(object):
def __init__(self, img_viewer):
self.img_viewer = img_viewer
def __call__(self, caller, event):
res = 'Color window: {} level: {}'.format(self.img_viewer.GetColorWindow(),
self.img_viewer.GetColorLevel())
print(res)
if __name__ == '__main__':
main()
014:ReadDICOM读取dicom文件并展示
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkIOImage import vtkDICOMImageReader
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkInteractionImage import vtkImageViewer2
from vtkmodules.vtkRenderingCore import vtkRenderWindowInteractor
def main():
filename = "Data/test.dcm"
colors = vtkNamedColors()
reader = vtkDICOMImageReader()
reader.SetFileName(filename)
# reader.SetDirectoryName(filename)
reader.Update()
img_viewer = vtkImageViewer2()
img_viewer.SetInputConnection(reader.GetOutputPort())
render_window_interactor = vtkRenderWindowInteractor()
img_viewer.SetupInteractor(render_window_interactor)
img_viewer.Render()
img_viewer.GetRenderer().SetBackground(colors.GetColor3d("SlateGray"))
img_viewer.GetRenderWindow().SetWindowName("ReadDICOM")
img_viewer.GetRenderer().ResetCamera()
img_viewer.SetSlice(150)
img_viewer.Render()
render_window_interactor.Start()
if __name__ == '__main__':
main()
015:ReadDICOMSeries读取系列的dcm文件(没看懂,也没实现应该有的功能)
from vtkmodules.vtkIOImage import vtkDICOMImageReader
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkInteractionImage import vtkImageViewer2
from vtkmodules.vtkInteractionStyle import vtkInteractorStyleImage
from vtkmodules.vtkRenderingCore import vtkActor2D, vtkRenderWindowInteractor, vtkTextMapper, vtkTextProperty
class StatusMessage():
@staticmethod
def format(slice: int, max_slice: int):
return f'Slice Number {slice + 1}/{max_slice + 1}'
class MyVtkInteractorStyleImage(vtkInteractorStyleImage):
def __init__(self, parent=None):
super().__init__()
self.AddObserver('KeyPressEvent', self.key_press_event)
self.AddObserver('MouseWheelForwardEvent', self.mouse_wheel_forward_event)
self.AddObserver('MouseWheelBackwardEvent', self.mouse_wheel_backward_event)
self.image_viewer = None
self.status_mapper = None
self.slice = 0
self.min_slice = 0
self.max_slice = 0
def set_image_viewer(self, image_viewer):
self.image_viewer = image_viewer
self.min_slice = image_viewer.GetSliceMin()
self.max_slice = image_viewer.GetSliceMax()
self.slice = self.min_slice
print(f'Slicer: Min = {self.min_slice}, Max= {self.max_slice}')
def set_status_mapper(self, status_mapper):
self.status_mapper = status_mapper
def move_slice_forward(self):
if self.slice < self.max_slice:
self.slice += 1
print(f'MoveSliceForward::Slice = {self.slice}')
self.imageviewer.SetSlice(self.slice)
msg = StatusMessage.format(self.slice, self.max_slice)
self.status_mapper.SetInput(msg)
self.imageviewer.Render()
def move_slice_backward(self):
if self.slice > self.min_slice:
self.slice -= 1
print(f'MoveSliceBackward::Slice = {self.slice}')
self.imageviewer.SetSlice(self.slice)
msg = StatusMessage.format(self.slice, self.max_slice)
self.status_mapper.SetInput(msg)
self.imageviewer.Render()
def key_press_event(self, obj, event):
key = self.GetInteractor().GetKeySym()
if key == 'Up':
self.move_slice_forward()
elif key == 'Down':
self.move_slice_backward()
def mouse_wheel_forward_event(self, obj, event):
self.move_slice_forward()
def mouse_wheel_backward_event(self, obj, event):
self.move_slice_backward()
def main():
colors = vtkNamedColors()
reader = vtkDICOMImageReader()
reader.SetDirectoryName('./convert')
reader.Update()
image_viewer = vtkImageViewer2()
image_viewer.SetInputConnection(reader.GetOutputPort())
slice_text_mapper = vtkTextMapper()
slice_text_prop = vtkTextProperty()
slice_text_prop.SetFontFamilyToCourier() # 使用Courier字体
slice_text_prop.SetFontSize(20) # 设置字体大小
"""
SetVerticalJustificationToBottom 设置文本的垂直对齐方式
确保文本的底部与你指定的位置对齐
例子:
如果你将文本演员(vtkActor2D)的位置设置为 (15, 10),并使用 SetVerticalJustificationToBottom():
文本的底部会正好位于 Y 坐标为 10 的位置。
文本本身会从这个位置向上延伸
"""
slice_text_prop.SetVerticalJustificationToBottom()
"""
SetJustificationToLeft 设置文本的对齐方式
确保文本的左边缘与你指定的水平位置对齐
例子:
如果你将文本演员(vtkActor2D)的位置设置为 (15, 10) 并使用 SetJustificationToLeft(),
那么文本的左边缘会正好位于 X 坐标为 15 的位置。文本本身会从这个位置向右延伸
"""
slice_text_prop.SetJustificationToLeft()
msg = StatusMessage.format(image_viewer.GetSliceMin(), image_viewer.GetSliceMax())
slice_text_mapper.SetInput(msg)
slice_text_mapper.SetTextProperty(slice_text_prop)
# actor
slice_text_actor = vtkActor2D()
slice_text_actor.SetMapper(slice_text_mapper)
slice_text_actor.SetPosition(15, 10) # 这里是固定到一个固定的像素位置
usage_text_prop = vtkTextProperty()
usage_text_prop.SetFontFamilyToCourier()
usage_text_prop.SetFontSize(14)
usage_text_prop.SetVerticalJustificationToTop()
usage_text_prop.SetJustificationToLeft()
usage_text_mappper = vtkTextMapper()
usage_text_mappper.SetInput('Slice with mouse wheel\n or Up/Down-Key\n- Zoom with pressed right\n '
' mouse button while dragging')
usage_text_mappper.SetTextProperty(usage_text_prop)
usage_text_actor = vtkActor2D()
usage_text_actor.SetMapper(usage_text_mappper)
"""
SetCoordinateSystemToNormalizedDisplay 设定演员的坐标位置
作用:将坐标系设置为归一化的显示坐标系(Normalized Display Coordinates)
在 VTK 中,显示坐标系通常以屏幕像素为单位。
但如果你想让你的程序在不同大小的窗口中都能正确显示,使用像素就不够灵活
归一化坐标是一种更通用的方式,它将整个显示窗口的范围归一化到 0.0 到 1.0 之间
窗口的左下角是 (0.0, 0.0)。
窗口的右上角是 (1.0, 1.0)。
这样做的好处是,无论你的窗口是 800x800 还是 1600x1600, (0.05, 0.95)
这个归一化位置都代表窗口的左上角附近,从而实现了自适应布局。
"""
usage_text_actor.GetPositionCoordinate().SetCoordinateSystemToNormalizedDisplay()
"""
将演员(Actor)的位置设置为 (0.05, 0.95)
x = 0.05:水平位置距离窗口左边缘 5% 的宽度。
y = 0.95:垂直位置距离窗口下边缘 95% 的高度,也就是距离上边缘 5% 的高度
"""
usage_text_actor.GetPositionCoordinate().SetValue(0.05, 0.95)
# 窗口及交互
render_window_interactor = vtkRenderWindowInteractor()
my_interactor_style = MyVtkInteractorStyleImage()
my_interactor_style.set_image_viewer(image_viewer)
my_interactor_style.set_status_mapper(slice_text_mapper)
image_viewer.SetupInteractor(render_window_interactor)
render_window_interactor.SetInteractorStyle(my_interactor_style)
render_window_interactor.Render()
image_viewer.GetRenderer().AddViewProp(slice_text_actor)
image_viewer.GetRenderer().AddViewProp(usage_text_actor)
image_viewer.Render()
image_viewer.GetRenderer().ResetCamera()
image_viewer.GetRenderer().SetBackground(colors.GetColor3d('SlateGray'))
image_viewer.GetRenderWindow().SetSize(800, 800)
image_viewer.GetRenderWindow().SetWindowName('ReadDICOMSeries')
image_viewer.Render()
render_window_interactor.Start()
if __name__ == '__main__':
main()
016:ImageWriter写入图片数据
import os.path
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkFiltersSources import vtkSphereSource
from vtkmodules.vtkIOImage import vtkBMPWriter, vtkPNGWriter, vtkJPEGWriter, vtkPNMWriter, vtkTIFFWriter, \
vtkPostScriptWriter
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkRenderingCore import vtkActor, vtkPolyDataMapper, vtkRenderer, vtkRenderWindowInteractor, \
vtkRenderWindow, vtkWindowToImageFilter
def WriteImage(fileName, renWin, rgba=True):
if fileName:
path, ext = os.path.splitext(fileName)
ext = ext.lower()
if not ext:
ext = '.png'
fileName = fileName + ext
if ext == '.bmp':
writer = vtkBMPWriter()
elif ext == '.jpg':
writer = vtkJPEGWriter()
elif ext == '.pnm':
writer = vtkPNMWriter()
elif ext == '.ps':
if rgba:
rgba = False
writer = vtkPostScriptWriter()
elif ext == '.tiff':
writer = vtkTIFFWriter()
else:
writer = vtkPNGWriter()
windowTOimage_fillter = vtkWindowToImageFilter()
windowTOimage_fillter.SetInput(renWin) # 这里接受的是一个渲染窗口对象
"""
用来设置图像缩放比例的
1 表示输出图像的尺寸将是渲染窗口尺寸的 1 倍。
例如,如果渲染窗口是 800x600 像素,输出的图像也将是 800x600 像素
2,就是800x600 的窗口会输出 1600x1200 的图像
"""
windowTOimage_fillter.SetScale(1)
"""
if else 里面的都是设置截图时捕捉的颜色缓冲区类型
渲染窗口(vtkRenderWindow)在内部使用不同的缓冲区来存储渲染信息。颜色缓冲区(color buffer)就是其中之一,它存储了每个像素的颜色值。
这些颜色值可以有不同的格式:
RGB:每个像素只存储红色、绿色和蓝色三个通道的颜色值。
RGBA:在 RGB 的基础上,增加了一个**Alpha(透明度)**通道。
"""
if rgba:
windowTOimage_fillter.SetInputBufferTypeToRGBA()
"""
当你调用 SetInputBufferTypeToRGBA() 时,
你明确地告诉过滤器:在截图时,也要把透明度信息(Alpha 通道)捕捉下来
背景透明:如果你的渲染窗口背景是透明的(例如,设置了 renderer.SetBackgroundAlpha(0.0)),
使用 RGBA 格式可以确保输出的图像文件(如 PNG)也保留透明背景,而不是将其变为黑色或白色。
半透明对象:如果你渲染了半透明的模型或物体,使用 RGBA 可以确保这些物体的透明效果在截图后依然保留。
"""
else:
windowTOimage_fillter.SetInputBufferTypeToRGB()
"""
ReadFrontBufferOff 关闭从前缓冲区读取像素数据的功能, 从后缓冲区读取数据
这行代码的作用是确保你的截图是最新且完整的渲染结果,尤其是在处理动态或动画场景时
VTK 的双缓冲机制
在 VTK 的渲染管线中,渲染窗口通常使用**双缓冲(double buffering)**技术来提供平滑、无闪烁的动画效果。
它有两个缓冲区:
前缓冲区 (Front Buffer):这是用户在屏幕上直接看到的缓冲区。
后缓冲区 (Back Buffer):这是 VTK 在后台进行所有渲染操作的缓冲区。
当渲染完成后,前后缓冲区会进行交换,新渲染的图像会瞬间显示出来。
"""
windowTOimage_fillter.ReadFrontBufferOff()
windowTOimage_fillter.Update()
writer.SetFileName(fileName)
writer.SetInputConnection(windowTOimage_fillter.GetOutputPort())
writer.Write()
else:
raise RuntimeError('Need a filename.')
def main():
colors = vtkNamedColors()
colors.SetColor("bkg", [26, 51, 102, 255])
source = vtkSphereSource()
source.SetRadius(5.0)
source.SetCenter(0, 0, 0)
mapper = vtkPolyDataMapper()
mapper.SetInputConnection(source.GetOutputPort())
actor = vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetColor(colors.GetColor3d("Yellow"))
render = vtkRenderer()
window = vtkRenderWindow()
window.AddRenderer(render)
iren = vtkRenderWindowInteractor()
iren.SetRenderWindow(window)
render.AddActor(actor)
render.SetBackground(colors.GetColor3d("bkg"))
window.SetWindowName('ImageWriter')
window.Render()
ext = ['', '.png', '.jpg', '.ps', '.tiff', '.bmp', '.pnm']
filenames = list(map(lambda x: 'ImageWriter' + x, ext))
filenames[0] = filenames[0] + '1'
for f in filenames:
WriteImage(f, window, rgba=False)
if __name__ == '__main__':
main()
017:Axes坐标轴
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkFiltersSources import vtkSphereSource
from vtkmodules.vtkRenderingAnnotation import vtkAxesActor
from vtkmodules.vtkCommonTransforms import vtkTransform
from vtkmodules.vtkRenderingCore import vtkActor, vtkRenderer, vtkPolyDataMapper, vtkRenderWindow, \
vtkRenderWindowInteractor
def main():
colors = vtkNamedColors()
# source
sphereSource = vtkSphereSource()
sphereSource.SetCenter(0, 0, 0)
sphereSource.SetRadius(0.5)
sphereMapper = vtkPolyDataMapper()
sphereMapper.SetInputConnection(sphereSource.GetOutputPort())
sphereActor = vtkActor()
sphereActor.SetMapper(sphereMapper)
transform = vtkTransform()
"""
在当前变换矩阵的基础上,做 平移变换。
参数 (1, 0, 0) 表示:沿 X 方向平移 1 个单位,Y 和 Z 方向不变
"""
transform.Translate(1, 0, 0)
axes = vtkAxesActor()
axes.SetUserTransform(transform)
render = vtkRenderer()
render.AddActor(sphereActor)
render.AddActor(axes)
render.SetBackground(colors.GetColor3d('SlateGray'))
render.GetActiveCamera().Azimuth(50)
render.GetActiveCamera().Elevation(-30)
render.ResetCamera()
renderWindow = vtkRenderWindow()
renderWindow.SetWindowName("Axes")
renderWindow.AddRenderer(render)
renderWindowInteractor = vtkRenderWindowInteractor()
renderWindowInteractor.SetRenderWindow(renderWindow)
renderWindowInteractor.Initialize()
renderWindow.Render()
renderWindowInteractor.Start()
if __name__ == '__main__':
main()
018:Circle写出一个圆
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkFiltersSources import vtkRegularPolygonSource
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkRenderingCore import vtkActor, vtkPolyDataMapper, vtkRenderWindowInteractor, vtkRenderWindow, \
vtkRenderer
def main():
colors = vtkNamedColors()
source = vtkRegularPolygonSource()
"""
是 VTK 中的一个几何体源(source),用于生成一个正多边形。这个多边形可以是任何你想要的边数(比如三角形、四边形、五边形等)
默认情况下,vtkRegularPolygonSource 会生成两种类型的几何体:
边线(Lines):组成多边形边界的线。
填充面(Polygon):填充多边形内部的三角形集合。
当你调用 source.GeneratePolygonOff() 时,你明确地告诉这个源对象:“我只需要多边形的边界线,不需要内部的填充面
"""
source.GeneratePolygonOff()
source.SetNumberOfSides(50) # 设置正多边形的边的数目,数目越大,越像一个圆
source.SetCenter(0, 0, 0)
mapper = vtkPolyDataMapper()
mapper.SetInputConnection(source.GetOutputPort())
actor = vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetColor(colors.GetColor3d("Cornsilk"))
render = vtkRenderer()
render.AddActor(actor)
render.SetBackground(colors.GetColor3d("DarkGreen"))
renderWindow = vtkRenderWindow()
renderWindow.AddRenderer(render)
renderWindow.SetWindowName("Circle")
renderWindowInteractor = vtkRenderWindowInteractor()
renderWindowInteractor.SetRenderWindow(renderWindow)
renderWindowInteractor.Initialize()
renderWindow.Render()
renderWindowInteractor.Start()
if __name__ == '__main__':
main()
019:ColoredLines通过坐标和索引去构建想要的mesh
"""
代码的含义类似于vedo.Mesh([vertices, faces])
"""
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonCore import vtkPoints, vtkUnsignedCharArray
from vtkmodules.vtkCommonDataModel import vtkCellArray, vtkLine, vtkPolyData
from vtkmodules.vtkRenderingCore import vtkActor, vtkPolyDataMapper, vtkRenderWindowInteractor, vtkRenderWindow, \
vtkRenderer
def main():
origin = [0, 0, 0]
p0 = [1, 0, 0]
p1 = [0, 1, 0]
points = vtkPoints()
points.InsertNextPoint(origin)
points.InsertNextPoint(p0)
points.InsertNextPoint(p1)
linesPolyData = vtkPolyData()
linesPolyData.SetPoints(points)
line0 = vtkLine()
line0.GetPointIds().SetId(0, 0) # the second 0 is the index of the Origin in linesPolyData's points
line0.GetPointIds().SetId(1, 1)
line1 = vtkLine()
line1.GetPointIds().SetId(0, 0) # the second 0 is the index of the Origin in linesPolyData's points
line1.GetPointIds().SetId(1, 2) #
"""
vtkCellArray
用于存储几何单元格拓扑信息的类。类似于vedo中mesh的cells,获得每个网格面的拓扑信息结构,每个网格面具体由哪些点的索引构成
"""
lines = vtkCellArray()
lines.InsertNextCell(line0)
lines.InsertNextCell(line1)
linesPolyData.SetLines(lines) # 连接拓扑结构
namedColors = vtkNamedColors()
colors = vtkUnsignedCharArray() # 创建一个用于存储颜色数据的数组
colors.SetNumberOfComponents(3) # 告诉数组每个颜色值有 3 个分量(代表 R、G、B)
try:
colors.InsertNextTupleValue(namedColors.GetColor3ub("Tomato"))
colors.InsertNextTupleValue(namedColors.GetColor3ub("Mint"))
except AttributeError:
colors.InsertNextTypedTuple(namedColors.GetColor3ub("Tomato"))
colors.InsertNextTypedTuple(namedColors.GetColor3ub("Mint"))
"""
GetCellData 获取 linesPolyData 对象的单元格数据
SetScalars 是一个通用方法,用于将一个数组(在这里是 colors)设置为单元格数据的**标量(Scalars)**属性
VTK 的一个重要特性是,它会自动将颜色数组中的每个值,按索引与单元格数组中的每个单元格进行匹配。
在代码中,linesPolyData 中有两条线,colors 数组中也有两个颜色值。
第一条线(索引为 0)会被分配 colors 数组中的第一个颜色值(Tomato)。
第二条线(索引为 1)会被分配 colors 数组中的第二个颜色值(Mint)。
"""
linesPolyData.GetCellData().SetScalars(colors)
mapper = vtkPolyDataMapper()
mapper.SetInputData(linesPolyData)
actor = vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetLineWidth(4)
renderer = vtkRenderer()
renderer.AddActor(actor)
renderer.SetBackground(namedColors.GetColor3d("SlateGray"))
window = vtkRenderWindow()
window.SetWindowName("ColoredLines")
window.AddRenderer(renderer)
interactor = vtkRenderWindowInteractor()
interactor.SetRenderWindow(window)
# Visualize
window.Render()
interactor.Start()
if __name__ == '__main__':
main()
020:Dodecahedron创建正十二面体
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonDataModel import vtkPolyhedron
from vtkmodules.vtkRenderingCore import vtkActor, vtkPolyDataMapper, vtkRenderer, vtkRenderWindow, \
vtkRenderWindowInteractor
def MakeDodecahedron():
aDodecahedron = vtkPolyhedron()
for i in range(0, 20):
"""
InsertNextId() 适用于你按顺序添加 ID 的情况,它更像 Python 列表的 append() 方法。
SetId() 适用于你在已知位置修改或设置 ID 的情况,
它更像 Python 列表的 my_list[i] = value 赋值操作
"""
aDodecahedron.GetPointIds().InsertNextId(i)
"""
aDodecahedron 没有.SetPoints()方法。
这一点与
ug = vtkUnstructuredGrid()
ug.SetPoints(points)
ug.InsertNextCell(polyVertex.GetCellType(), polyVertex.GetPointIds())
截然不同
vtkPolyhedron 的设计
vtkPolyhedron 是一个单一的、自包含的几何单元。
它的设计理念是,一个多面体的所有信息都应该直接存储在这个对象内部。
当你调用 aDodecahedron = vtkPolyhedron() 时,
这个对象内部已经为你创建了一个空的 vtkPoints 容器和一个空的 vtkCellArray 容器。
你不需要自己去创建和设置这些容器。
因此,你通过 GetPoints() 方法来获取内部的 vtkPoints 容器,然后直接在上面插入点
而类似于vtkUnstructuredGrid
是一个通用且灵活的数据容器。它的设计理念是,它只是一个将点(vtkPoints)和单元格(vtkCellArray)
组合起来的**“网格”**,而不是一个自包含的单元
因此,你必须自己创建点和单元格,然后通过 SetPoints() 和 SetCells() 方法将它们设置给 vtkUnstructuredGrid 对象
"""
aDodecahedron.GetPoints().InsertNextPoint(1.21412, 0, 1.58931)
aDodecahedron.GetPoints().InsertNextPoint(0.375185, 1.1547, 1.58931)
aDodecahedron.GetPoints().InsertNextPoint(-0.982247, 0.713644, 1.58931)
aDodecahedron.GetPoints().InsertNextPoint(-0.982247, -0.713644, 1.58931)
aDodecahedron.GetPoints().InsertNextPoint(0.375185, -1.1547, 1.58931)
aDodecahedron.GetPoints().InsertNextPoint(1.96449, 0, 0.375185)
aDodecahedron.GetPoints().InsertNextPoint(0.607062, 1.86835, 0.375185)
aDodecahedron.GetPoints().InsertNextPoint(-1.58931, 1.1547, 0.375185)
aDodecahedron.GetPoints().InsertNextPoint(-1.58931, -1.1547, 0.375185)
aDodecahedron.GetPoints().InsertNextPoint(0.607062, -1.86835, 0.375185)
aDodecahedron.GetPoints().InsertNextPoint(1.58931, 1.1547, -0.375185)
aDodecahedron.GetPoints().InsertNextPoint(-0.607062, 1.86835, -0.375185)
aDodecahedron.GetPoints().InsertNextPoint(-1.96449, 0, -0.375185)
aDodecahedron.GetPoints().InsertNextPoint(-0.607062, -1.86835, -0.375185)
aDodecahedron.GetPoints().InsertNextPoint(1.58931, -1.1547, -0.375185)
aDodecahedron.GetPoints().InsertNextPoint(0.982247, 0.713644, -1.58931)
aDodecahedron.GetPoints().InsertNextPoint(-0.375185, 1.1547, -1.58931)
aDodecahedron.GetPoints().InsertNextPoint(-1.21412, 0, -1.58931)
aDodecahedron.GetPoints().InsertNextPoint(-0.375185, -1.1547, -1.58931)
aDodecahedron.GetPoints().InsertNextPoint(0.982247, -0.713644, -1.58931)
faces = [12, # number of faces
5, 0, 1, 2, 3, 4, # number of ids on face, ids
5, 0, 5, 10, 6, 1,
5, 1, 6, 11, 7, 2,
5, 2, 7, 12, 8, 3,
5, 3, 8, 13, 9, 4,
5, 4, 9, 14, 5, 0,
5, 15, 10, 5, 14, 19,
5, 16, 11, 6, 10, 15,
5, 17, 12, 7, 11, 16,
5, 18, 13, 8, 12, 17,
5, 19, 14, 9, 13, 18,
5, 19, 18, 17, 16, 15]
aDodecahedron.SetFaces(faces)
aDodecahedron.Initialize()
return aDodecahedron
def main():
colors = vtkNamedColors()
dodecahedron = MakeDodecahedron()
"""
mapper.SetInputConnection(dodecahedron.GetOutPutPort())
dodecahedron 这种仅仅是数据的容器被设计用来存储几何和拓扑信息,而不是主动生成或处理数据。
因此,它们没有 GetOutputPort() 方法来连接到管道中
"""
mapper = vtkPolyDataMapper()
mapper.SetInputData(dodecahedron.GetPolyData())
"""
mapper.SetInputData(dodecahedron)
因为 vtkPolyDataMapper 只能处理 vtkPolyData 类型的数据,
而 dodecahedron 是一个 vtkPolyhedron 类型的对象。尽管它们都代表三维几何体,
但它们的内部数据结构是不同的。
你可以把 vtkPolyhedron 看作一个高级、专业的模型,专门用于描述凸多面体。
而 vtkPolyData 是一个通用的、标准的模型,用于表示任何由点、线和多边形组成的几何体。
vtkPolyhedron 的 GetPolyData() 方法就像一个转换器,它将自己内部存储的复杂多面体数据,
转换成 vtkPolyDataMapper 能够理解和渲染的通用 vtkPolyData 格式
"""
actor = vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetColor(colors.GetColor3d("PapayaWhip"))
render = vtkRenderer()
renderWindow = vtkRenderWindow()
renderWindow.AddRenderer(render)
renderWindowInteractor = vtkRenderWindowInteractor()
renderWindowInteractor.SetRenderWindow(renderWindow)
render.AddActor(actor)
render.SetBackground(colors.GetColor3d("CadetBlue"))
render.GetActiveCamera().Azimuth(30)
render.GetActiveCamera().Elevation(30)
render.ResetCamera()
renderWindowInteractor.Initialize()
renderWindow.Render()
renderWindowInteractor.Start()
if __name__ == '__main__':
main()
021:EllipticalCylinder椭圆及拉伸
from vtkmodules.vtkCommonColor import vtkNamedColors
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonCore import vtkMath, vtkPoints
from vtkmodules.vtkCommonDataModel import vtkCellArray, vtkPolyData, vtkPolyLine
from vtkmodules.vtkFiltersModeling import vtkLinearExtrusionFilter
from vtkmodules.vtkRenderingCore import vtkActor, vtkCamera, vtkPolyDataMapper, vtkProperty, vtkRenderWindow, \
vtkRenderWindowInteractor, vtkRenderer
import math
"""
vtkPolyLine:
这是一个拓扑单元。它描述的是由一系列有序点连接而成的一条连续折线。
它本身不包含点的坐标,只存储构成折线的点 ID 列表。
你可以把它想象成一条“线绳”,知道要经过哪些点,但不知道这些点在空间中的具体位置。
vtkCellArray:
这是一个拓扑容器。它的作用是存储一个或多个拓扑单元,比如一个或多个 vtkPolyLine 对象。
vtkPolyData 会从 vtkCellArray 中获取所有的拓扑信息。你可以把它想象成一个“线绳收纳盒”,
里面可以放很多条线绳。
vtkPolyData:
这是一个数据模型。它是 VTK 中最通用的数据结构之一,用于存储由点、线和多边形组成的几何体。
它内部包含了几何信息(vtkPoints 存放点的坐标)和拓扑信息(vtkCellArray 存放单元格的连接关系)。
vtkPolyData 将几何和拓扑信息组合在一起,形成一个完整的模型。你可以把它想象成一个“工具箱”,
里面既有螺丝(点),也有螺丝的连接图(拓扑信息),你可以用它们来组装任何你想要的东西。
"""
def main():
colors = vtkNamedColors()
angle = 0
r1 = 50
r2 = 30
centerX = 10.0
centerY = 5.0
points = vtkPoints()
idx = 0
while angle <= 2.0 * vtkMath.Pi() + (vtkMath.Pi() / 60.0):
points.InsertNextPoint(r1 * math.cos(angle) + centerX,
r2 * math.sin(angle) + centerY,
0.0)
angle = angle + (vtkMath.Pi() / 60.0)
idx += 1
line = vtkPolyLine()
line.GetPointIds().SetNumberOfIds(idx)
for i in range(0, idx):
line.GetPointIds().SetId(i, i)
lines = vtkCellArray()
lines.InsertNextCell(line)
polydata = vtkPolyData()
polydata.SetPoints(points)
polydata.SetLines(lines)
extrude = vtkLinearExtrusionFilter()
extrude.SetInputData(polydata)
extrude.SetExtrusionTypeToNormalExtrusion() # 沿输入数据的法线方向进行挤压。
extrude.SetVector(0, 0, 100.0) # 在z方向挤压100个单位
extrude.Update()
lineMapper = vtkPolyDataMapper()
lineMapper.SetInputData(polydata)
lineActor = vtkActor()
lineActor.SetMapper(lineMapper)
lineActor.GetProperty().SetColor(colors.GetColor3d("Peacock"))
mapper = vtkPolyDataMapper()
mapper.SetInputConnection(extrude.GetOutputPort())
mapper = vtkPolyDataMapper()
mapper.SetInputConnection(extrude.GetOutputPort())
back = vtkProperty()
back.SetColor(colors.GetColor3d("Tomato"))
actor = vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetColor(colors.GetColor3d("Banana"))
actor.SetBackfaceProperty(back)
ren = vtkRenderer()
ren.SetBackground(colors.GetColor3d("SlateGray"))
ren.AddActor(actor)
ren.AddActor(lineActor)
renWin = vtkRenderWindow()
renWin.SetWindowName("EllipticalCylinder")
renWin.AddRenderer(ren)
renWin.SetSize(600, 600)
iren = vtkRenderWindowInteractor()
iren.SetRenderWindow(renWin)
camera = vtkCamera()
camera.SetPosition(0, 1, 0)
camera.SetFocalPoint(0, 0, 0)
camera.SetViewUp(0, 0, 1)
camera.Azimuth(30)
camera.Elevation(30)
ren.SetActiveCamera(camera)
ren.ResetCamera()
ren.ResetCameraClippingRange()
renWin.Render()
iren.Start()
if __name__ == '__main__':
main()
022:EllipticalCylinderDemo 椭圆+箭头
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonCore import vtkMinimalStandardRandomSequence, vtkMath, vtkPoints
from vtkmodules.vtkCommonMath import vtkMatrix4x4
from vtkmodules.vtkCommonTransforms import vtkTransform
from vtkmodules.vtkFiltersGeneral import vtkTransformPolyDataFilter
from vtkmodules.vtkFiltersCore import vtkTubeFilter
from vtkmodules.vtkFiltersModeling import vtkLinearExtrusionFilter
from vtkmodules.vtkFiltersSources import vtkArrowSource
from vtkmodules.vtkCommonDataModel import vtkCellArray, vtkPolyLine, vtkPolyData
import math
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkCamera,
vtkPolyDataMapper,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkRenderer
)
def main():
nx, ny, nz = 0, 0, 100
colors = vtkNamedColors()
angle = 0
r1 = 50
r2 = 30
centerX = 15
centerY = 5
points = vtkPoints()
idx = 0
while angle <= 2.0 * vtkMath.Pi() + (vtkMath.Pi() / 60.0):
points.InsertNextPoint(r1 * math.cos(angle) + centerX,
r2 * math.sin(angle) + centerY,
0.0)
angle = angle + (vtkMath.Pi() / 60)
idx += 1
line = vtkPolyLine()
line.GetPointIds().SetNumberOfIds(idx)
for i in range(0, idx):
line.GetPointIds().SetId(i, i)
lines = vtkCellArray()
lines.InsertNextCell(line)
polyData = vtkPolyData()
polyData.SetPoints(points)
polyData.SetLines(lines)
extrude = vtkLinearExtrusionFilter()
extrude.SetInputData(polyData)
extrude.SetExtrusionTypeToNormalExtrusion()
extrude.SetVector(nx, ny, nz)
extrude.Update()
# 创建一个arrow
startpoint = [centerX, centerY, 0]
endPoint = [0.0] * 3
for i in range(0, 3):
endPoint[i] = startpoint[i] + extrude.GetVector()[i]
normalizedX = [0.0] * 3
normalizedY = [0.0] * 3
normalizedZ = [0.0] * 3
"""
计算从 startPoint 到 endPoint 的向量。这个向量将作为新坐标系的 X 轴
"""
vtkMath.Subtract(endPoint, startpoint, normalizedX)
"""
Norm 求计算这个向量的长度
"""
length = vtkMath.Norm(normalizedX)
"""
Normalize 将向量转换为单位向量
"""
vtkMath.Normalize(normalizedX)
"""
这里的arbitrary实际上就是随机生成一个向量,用来确定一个三系的坐标轴
"""
rng = vtkMinimalStandardRandomSequence()
rng.SetSeed(8775070)
max_r = 10.0
arbitrary = [0.0, 0.0, 0.0]
for i in range(0, 3):
"""
每一次循环,rng.GetRangeValue(-max_r, max_r)
生成一个在 -max_r 和 max_r 范围内的随机浮点数
"""
arbitrary[i] = rng.GetRangeValue(-max_r, max_r)
rng.Next() # 更新生成器的内部状态, 以便下次调用时能产生一个不同的随机数
"""
计算归一化 X 轴与随机向量的叉积。
两个向量的叉积会产生一个同时垂直于它们的向量。这确保了生成的 Z 轴与 X 轴正交
"""
vtkMath.Cross(normalizedX, arbitrary, normalizedZ)
vtkMath.Normalize(normalizedZ)
vtkMath.Cross(normalizedZ, normalizedX, normalizedY)
matrix = vtkMatrix4x4()
matrix.Identity() # 将矩阵初始化为单位阵
for i in range(0, 3):
# 将矩阵的第一列(X 列)设置为 normalizedX 向量的分
matrix.SetElement(i, 0, normalizedX[i])
# 将第二列(Y 列)设置为 normalizedY 的分量。
matrix.SetElement(i, 1, normalizedY[i])
# 将第三列(Z 列)设置为 normalizedZ 的分量
matrix.SetElement(i, 2, normalizedZ[i])
"""
vtkTransform 内部有一个堆栈,可以存储多个变换。
Concatenate() 将 matrix 这个变换堆到平移变换的后面
VTK 的变换顺序是从右到左,这意味着先执行 matrix 的变换(旋转和缩放),然后再执行平移。
这正是我们想要的效果:先将物体旋转到正确的方向,然后将它移动到正确的位置
"""
transform = vtkTransform()
# 平移,将物体从原点 (0, 0, 0) 移动到 startpoint 指定的位置
transform.Translate(startpoint)
# 将之前创建的对齐矩阵(matrix)与当前的 transform 对象组合(或“连接”)在一起
transform.Concatenate(matrix)
# 缩放。它将物体在三个维度上都缩放到 length 指定的长度
transform.Scale(length, length, length)
arrowSource = vtkArrowSource()
# 设置了箭头尖端(Tip)的分辨率 31 表示箭头尖端将由 31 个三角形组成,这些三角形围绕圆锥体的轴线排列
arrowSource.SetTipResolution(31)
# 设置了箭头箭杆(Shaft)的分辨率 21 表示箭杆将由 21 个四边形侧面组成,这些四边形围绕圆柱体的轴线排列
arrowSource.SetShaftResolution(21)
transformPD = vtkTransformPolyDataFilter()
transformPD.SetTransform(transform)
transformPD.SetInputConnection(arrowSource.GetOutputPort())
arrowMapper = vtkPolyDataMapper()
arrowMapper.SetInputConnection(transformPD.GetOutputPort())
arrowActor = vtkActor()
arrowActor.SetMapper(arrowMapper)
arrowActor.GetProperty().SetColor(colors.GetColor3d("Tomato"))
tubes = vtkTubeFilter()
tubes.SetInputData(polyData)
tubes.SetRadius(2.0)
tubes.SetNumberOfSides(21)
lineMapper = vtkPolyDataMapper()
lineMapper.SetInputConnection(tubes.GetOutputPort())
lineActor = vtkActor()
lineActor.SetMapper(lineMapper)
lineActor.GetProperty().SetColor(colors.GetColor3d("Peacock"))
mapper = vtkPolyDataMapper()
mapper.SetInputConnection(extrude.GetOutputPort())
actor = vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetColor(colors.GetColor3d("Banana"))
actor.GetProperty().SetOpacity(.7)
ren = vtkRenderer()
ren.SetBackground(colors.GetColor3d("SlateGray"))
ren.AddActor(actor)
ren.AddActor(lineActor)
ren.AddActor(arrowActor)
renWin = vtkRenderWindow()
renWin.SetWindowName("Elliptical Cylinder Demo")
renWin.AddRenderer(ren)
renWin.SetSize(600, 600)
iren = vtkRenderWindowInteractor()
iren.SetRenderWindow(renWin)
camera = vtkCamera()
camera.SetPosition(0, 1, 0)
camera.SetFocalPoint(0, 0, 0)
camera.SetViewUp(0, 0, 1)
camera.Azimuth(30)
camera.Elevation(30)
ren.SetActiveCamera(camera)
ren.ResetCamera()
ren.ResetCameraClippingRange()
renWin.Render()
iren.Start()
if __name__ == '__main__':
main()
023:GeometricObjectsDemo多视口展示多种基本模型
# import vtkmodules.vtkInteractionStyle
# import vtkmodules.vtkRenderingFreeType
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkFiltersSources import (
vtkArrowSource,
vtkConeSource,
vtkCubeSource,
vtkCylinderSource,
vtkDiskSource,
vtkLineSource,
vtkRegularPolygonSource,
vtkSphereSource
)
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkActor2D,
vtkPolyDataMapper,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkRenderer,
vtkTextMapper,
vtkTextProperty
)
def main():
colors = vtkNamedColors()
colors.SetColor("BkgColor", [51, 77, 102, 255])
geometricObjectSources = list()
geometricObjectSources.append(vtkArrowSource())
geometricObjectSources.append(vtkConeSource())
geometricObjectSources.append(vtkCubeSource())
geometricObjectSources.append(vtkCylinderSource())
geometricObjectSources.append(vtkDiskSource())
geometricObjectSources.append(vtkLineSource())
geometricObjectSources.append(vtkRegularPolygonSource())
geometricObjectSources.append(vtkSphereSource())
mappers = []
actors = []
textmappers = []
textactors = []
textProperty = vtkTextProperty()
textProperty.SetFontSize(16)
textProperty.SetJustificationToCentered()
textProperty.SetColor(colors.GetColor3d("LightGoldenrodYellow"))
for i in range(0, len(geometricObjectSources)):
geometricObjectSources[i].Update()
mappers.append(vtkPolyDataMapper())
mappers[i].SetInputConnection(geometricObjectSources[i].GetOutputPort())
actors.append(vtkActor())
actors[i].SetMapper(mappers[i])
actors[i].GetProperty().SetColor(colors.GetColor3d("PeachPuff"))
textmappers.append(vtkTextMapper())
textmappers[i].SetInput(geometricObjectSources[i].GetClassName())
textmappers[i].SetTextProperty(textProperty)
textactors.append(vtkActor2D())
textactors[i].SetMapper(textmappers[i])
textactors[i].SetPosition(120, 16)
gridCols = 3
gridRows = 3
renderSize = 300
renderWindow = vtkRenderWindow()
renderWindow.SetSize(renderSize * 3, renderSize * 3)
renderWindow.SetWindowName("GeometricObjectsDemo")
for row in range(0, gridRows):
for col in range(0, gridCols):
index = row * gridCols + col
renderer = vtkRenderer()
renderer.SetBackground(colors.GetColor3d('BkgColor'))
"""
设置一个渲染器(renderer)在渲染窗口中的可视区域
个可视区域被称为视口(viewport)。
它的坐标是归一化的,范围从 0.0 到 1.0,其中 (0.0, 0.0) 代表渲染窗口的左下角,
而 (1.0, 1.0) 代表右上角
SetViewport 被用于将渲染窗口分割成一个网格,并在网格的每个单元格中显示一个独立的渲染器
例如 row=col=0时,viewPort = [0.0, 0.6666666666666666, 0.3333333333333333, 1.0]
表示的就是窗口九分格最左上角的那个
"""
viewPort = [float(col) / gridCols,
float(gridRows - row - 1) / gridRows,
float(col + 1) / gridCols,
float(gridRows - row) / gridRows]
print(viewPort)
renderer.SetViewport(viewPort)
if index < len(geometricObjectSources):
renderer.AddActor(actors[index])
renderer.AddActor2D(textactors[index])
renderer.ResetCameraClippingRange()
renderWindow.AddRenderer(renderer)
interactor = vtkRenderWindowInteractor()
interactor.SetRenderWindow(renderWindow)
renderWindow.Render()
interactor.Start()
if __name__ == '__main__':
main()
024:Planes通过生成平截头体和边界框,将这两种几何体转换为凸包
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonDataModel import vtkPlanes, vtkPolyData
from vtkmodules.vtkFiltersCore import vtkHull
from vtkmodules.vtkFiltersSources import vtkSphereSource
from vtkmodules.vtkRenderingCore import vtkActor, vtkActor2D, vtkCamera, vtkPolyDataMapper, vtkRenderWindow, \
vtkRenderWindowInteractor, vtkRenderer, vtkTextMapper, vtkTextProperty
def main():
"""
这段示例代码的主要作用是展示两种不同的方法来创建凸多面体,并将它们并排显示在同一个窗口中。
它通过生成平截头体和边界框,将这两种几何体转换为凸包,然后进行可视化
"""
colors = vtkNamedColors()
planes = list()
titles = list()
"""
使用视椎体创建平面集合。
利用 vtkCamera 对象的 GetFrustumPlanes() 方法,从摄像机的视锥体中提取出六个平面
"""
titles.append("Using frustum planes")
camera = vtkCamera()
planes_array = [0] * 24
"""
GetFrustumPlanes
这个方法计算并返回摄像机的六个平截头体平面。这些平面分别是:
近裁剪面(near clipping plane)、远裁剪面(far clipping plane)、左、右、下、上裁剪面。
它们共同构成了一个金字塔状的可见区域
1 这个参数表示世界坐标系(world coordinates)
planes_array: 这是作为输出参数的列表。方法执行后,
计算出的 24 个浮点数(六个平面)会被填充到这个列表中
"""
camera.GetFrustumPlanes(1, planes_array)
planes.append(vtkPlanes())
# 是将多个平面的信息导入到一个 vtkPlanes 对象中
planes[0].SetFrustumPlanes(planes_array)
"""
使用球体的边界框生成一个凸包
"""
titles.append("Using bounds")
sphere_source = vtkSphereSource()
sphere_source.Update()
bounds = [0] * 6
sphere_source.GetOutput().GetBounds(bounds)
planes.append(vtkPlanes())
planes[1].SetBounds(bounds)
text_property = vtkTextProperty()
text_property.SetFontSize(16)
text_property.SetJustificationToCentered()
ren_win = vtkRenderWindow()
ren_win.SetSize(600, 600)
ren_win.SetWindowName("Planes")
iren = vtkRenderWindowInteractor()
iren.SetRenderWindow(ren_win)
hulls = []
pds = []
mappers = []
actors = []
renderers = []
text_mappers = []
text_actors = []
for i in range(0, len(planes)):
hulls.append(vtkHull())
# 接收一个 vtkPlanes 对象作为参数
hulls[i].SetPlanes(planes[i])
# 接收一个单个平面
# hulls[i].SetPlane()
pds.append(vtkPolyData())
"""
GenerateHull 执行了主要的凸包计算。它接收两个主要参数
输出数据对象 pds[i]
pds[i] 是一个空的 vtkPolyData 对象。
GenerateHull() 方法会把计算出的多面体几何信息(点、面、拓扑等)填充到这个对象中。
一旦这个方法执行完毕,pds[i] 就不再是空的,而是包含了完整的、可渲染的凸多面体数据
初始边界框 (-200, 200, -200, 200, -200, 200):
这六个数字定义了一个初始的、临时的三维边界框:
(-200, 200):X 轴范围
(-200, 200):Y 轴范围
(-200, 200):Z 轴范围
vtkHull 利用这个边界框来生成一组初始的多边形。然后,它用你提供的所有平面来裁剪这些多边形,
最终形成一个由所有平面所包围的封闭凸多面体。提供一个足够大的边界框很重要,
因为它需要包含你最终想要的凸包。
"""
hulls[i].GenerateHull(pds[i], -200, 200, -200, 200, -200, 200)
mappers.append(vtkPolyDataMapper())
mappers[i].SetInputData(pds[i])
actors.append(vtkActor())
actors[i].SetMapper(mappers[i])
actors[i].GetProperty().SetColor(colors.GetColor3d('Moccasin'))
actors[i].GetProperty().SetSpecular(0.8)
actors[i].GetProperty().SetSpecularPower(30)
renderers.append(vtkRenderer())
renderers[i].AddActor(actors[i])
text_mappers.append(vtkTextMapper())
text_mappers[i].SetInput(titles[i])
text_mappers[i].SetTextProperty(text_property)
text_actors.append(vtkActor2D())
text_actors[i].SetMapper(text_mappers[i])
text_actors[i].SetPosition(100, 10)
renderers[i].AddViewProp(text_actors[i])
ren_win.AddRenderer(renderers[i])
x_grid_dimensions = 2
y_grid_dimensions = 1
render_size = 300
ren_win.SetSize(render_size * x_grid_dimensions, render_size * y_grid_dimensions)
for row in range(0, y_grid_dimensions):
for col in range(0, x_grid_dimensions):
index = row * x_grid_dimensions + col
# (xmin, ymin, xmax, ymax)
viewport = [float(col) / x_grid_dimensions,
float(y_grid_dimensions - (row + 1)) / y_grid_dimensions,
float(col + 1) / x_grid_dimensions,
float(y_grid_dimensions - row) / y_grid_dimensions]
if index > (len(actors) - 1):
# Add a renderer even if there is no actor.
# This makes the render window background all the same color.
ren = vtkRenderer()
ren.SetBackground(colors.GetColor3d('DarkSlateGray'))
ren.SetViewport(viewport)
ren_win.AddRenderer(ren)
continue
renderers[index].SetViewport(viewport)
renderers[index].SetBackground(colors.GetColor3d('DarkSlateGray'))
renderers[index].ResetCamera()
renderers[index].GetActiveCamera().Azimuth(30)
renderers[index].GetActiveCamera().Elevation(-30)
renderers[index].ResetCameraClippingRange()
iren.Initialize()
ren_win.Render()
iren.Start()
if __name__ == '__main__':
main()
025:PlanesIntersection检查一个点集是否与模型的边界框是否相交
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonCore import vtkPoints
from vtkmodules.vtkCommonDataModel import vtkPlanesIntersection
from vtkmodules.vtkFiltersSources import vtkSphereSource
def main():
sphereSource = vtkSphereSource()
sphereSource.Update()
bounds = [0] * 6
sphereSource.GetOutput().GetBounds(bounds)
xMin = bounds[0]
xMax = bounds[1]
yMin = bounds[2]
yMax = bounds[3]
zMin = bounds[4]
zMax = bounds[5]
box = vtkPoints()
box.SetNumberOfPoints(8)
box.SetPoint(0, xMax, yMin, zMax)
box.SetPoint(1, xMax, yMin, zMin)
box.SetPoint(2, xMax, yMax, zMin)
box.SetPoint(3, xMax, yMax, zMax)
box.SetPoint(4, xMin, yMin, zMax)
box.SetPoint(5, xMin, yMin, zMin)
box.SetPoint(6, xMin, yMax, zMin)
box.SetPoint(7, xMin, yMax, zMax)
planeIntersection = vtkPlanesIntersection()
planeIntersection.SetBounds(bounds)
intersects = planeIntersection.IntersectsRegion(box)
if intersects == 1:
res = 'Yes'
else:
res = 'No'
print('Intersects? ', res)
if __name__ == '__main__':
main()
026:PolygonIntersection直线与polygon是否相交
from vtkmodules.vtkCommonCore import mutable, vtkPoints
from vtkmodules.vtkCommonDataModel import vtkPolygon
def main():
points = vtkPoints()
points.InsertNextPoint(0.0, 0.0, 0.0)
points.InsertNextPoint(1.0, 0.0, 0.0)
points.InsertNextPoint(1.0, 1.0, 0.0)
points.InsertNextPoint(0.0, 1.0, 0.0)
polygon = vtkPolygon()
polygon.GetPointIds().SetNumberOfIds(4)
for i in range(4):
polygon.GetPointIds().SetId(i, i)
# polygon.SetPoints(points) 几何单元不保存点坐标
polygon.GetPoints().DeepCopy(points) # 这是局部的点坐标
"""
在 VTK 里有 两层点的概念:
全局点集 (vtkPoints)
属于 vtkPolyData,存储模型里所有的几何点坐标。
这是“权威”的几何信息,单元(cell)只保存点的 ID 来引用这里的点。
局部点引用(单元内部的 vtkPoints)
在某些 cell 类(比如 vtkPolygon)内部,确实有一个 vtkPoints 成员。
但这个 仅用于几何计算(比如法向量、面积、三角化),不用于渲染。
当你 polygon.GetPoints().DeepCopy(points) 时,其实是把外部的 vtkPoints 拷贝到 vtkPolygon 内部临时存一下,方便做算法。
换句话说:
渲染/网格拓扑 → 用 vtkPolyData 里的 vtkPoints + vtkPolygon 的 PointIds。
几何计算 → vtkPolygon 里自己带的 vtkPoints 可以用来临时存点坐标。
为什么会有这种设计?
因为很多几何计算(法向、面积、点是否在多边形内等)需要直接访问点坐标。
如果 vtkPolygon 只存 PointIds,每次计算都要你额外传一个 vtkPoints 进来,很麻烦。
所以它提供了一个 GetPoints() 接口,让你可以选择性地把坐标“拷贝”进来,方便使用 vtkPolygon.ComputeNormal()、vtkPolygon.Triangulate() 等方法。
不过注意 ⚠️:
这个 polygon.GetPoints() 存的点,只对这个 polygon 自己有效,不会自动同步到 vtkPolyData。
如果想真正构建可渲染的数据集,还是要把点放在 vtkPolyData.SetPoints() 里。
"""
p1 = [0.1, 0, -1.0]
p2 = [0.1, 0, 1.0]
tolerance = 0.001
t = mutable(0)
x = [0.0, 0.0, 0.0] # 输出)交点的 世界坐标 (x, y, z)
pcoords = [0.0, 0.0, 0.0] # 输出)交点在 单元局部坐标系 下的坐标(通常在单元内插值时用到)
subId = mutable(0) # (输出)如果单元由多个子单元构成,这里返回子单元的编号。对于 vtkPolygon 一般是 0
iD = polygon.IntersectWithLine(p1, p2, tolerance, t, x, pcoords, subId)
print('intersected? ', 'Yes' if iD == 1 else 'No')
print('intersection: ', x)
if __name__ == '__main__':
main()
027:Cell3DDemonstration多种网格类型数据创建,及手动构建网格
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonCore import vtkPoints, vtkIdList
from vtkmodules.vtkCommonDataModel import VTK_POLYHEDRON, VTK_TETRA, vtkCellArray, vtkHexagonalPrism, \
vtkHexahedron, vtkPentagonalPrism, vtkPyramid, vtkTetra, vtkUnstructuredGrid, vtkVoxel, vtkWedge
from vtkmodules.vtkIOImage import vtkPNMWriter
from vtkmodules.vtkRenderingCore import vtkActor, vtkActor2D, vtkDataSetMapper, vtkRenderer, vtkRenderWindow, \
vtkRenderWindowInteractor, vtkTextMapper, vtkTextProperty, vtkWindowToImageFilter
def MakeHexagonalPrism():
# 六棱柱
points = vtkPoints()
numberOfVertices = 12
points.InsertNextPoint(0.0, 0.0, 0.0)
points.InsertNextPoint(1.0, 0.0, 1.0)
points.InsertNextPoint(1.5, 0.5, 1.0)
points.InsertNextPoint(1.0, 1.0, 1.0)
points.InsertNextPoint(0.0, 1.0, 1.0)
points.InsertNextPoint(-0.5, 0.5, 1.0)
points.InsertNextPoint(0.0, 0.0, 0.0)
points.InsertNextPoint(1.0, 0.0, 0.0)
points.InsertNextPoint(1.5, 0.5, 0.0)
points.InsertNextPoint(1.0, 1.0, 0.0)
points.InsertNextPoint(0.0, 1.0, 0.0)
points.InsertNextPoint(-0.5, 0.5, 0.0)
hexagonalPrism = vtkHexagonalPrism()
for i in range(0, numberOfVertices):
hexagonalPrism.GetPointIds().SetId(i, i)
ug = vtkUnstructuredGrid()
ug.SetPoints(points)
ug.InsertNextCell(hexagonalPrism.GetCellType(), hexagonalPrism.GetPointIds())
return ug
def MakeHexaheron():
points = vtkPoints()
points.InsertNextPoint(0.0, 0.0, 0.0)
points.InsertNextPoint(1.0, 0.0, 0.0)
points.InsertNextPoint(1.0, 1.0, 0.0)
points.InsertNextPoint(0.0, 1.0, 0.0)
points.InsertNextPoint(0.0, 0.0, 1.0)
points.InsertNextPoint(1.0, 0.0, 1.0)
points.InsertNextPoint(1.0, 1.0, 1.0)
points.InsertNextPoint(0.0, 1.0, 1.0)
hex_ = vtkHexahedron()
numberOfVertices = 8
for i in range(0, numberOfVertices):
hex_.GetPointIds().SetId(i, i)
ug = vtkUnstructuredGrid()
ug.InsertNextCell(hex_.GetCellType(), hex_.GetPointIds())
ug.SetPoints(points)
return ug
def MakePentagonalPrism():
numberOfVertices = 10
points = vtkPoints()
points.InsertNextPoint(11, 10, 10)
points.InsertNextPoint(13, 10, 10)
points.InsertNextPoint(14, 12, 10)
points.InsertNextPoint(12, 14, 10)
points.InsertNextPoint(10, 12, 10)
points.InsertNextPoint(11, 10, 14)
points.InsertNextPoint(13, 10, 14)
points.InsertNextPoint(14, 12, 14)
points.InsertNextPoint(12, 14, 14)
points.InsertNextPoint(10, 12, 14)
pentagonalPrism = vtkPentagonalPrism()
for i in range(0, numberOfVertices):
pentagonalPrism.GetPointIds().SetId(i, i)
ug = vtkUnstructuredGrid()
ug.SetPoints(points)
ug.InsertNextCell(pentagonalPrism.GetCellType(), pentagonalPrism.GetPointIds())
return ug
def MakePolyhedron():
points = vtkPoints()
points.InsertNextPoint(1.21412, 0, 1.58931)
points.InsertNextPoint(0.375185, 1.1547, 1.58931)
points.InsertNextPoint(-0.982247, 0.713644, 1.58931)
points.InsertNextPoint(-0.982247, -0.713644, 1.58931)
points.InsertNextPoint(0.375185, -1.1547, 1.58931)
points.InsertNextPoint(1.96449, 0, 0.375185)
points.InsertNextPoint(0.607062, 1.86835, 0.375185)
points.InsertNextPoint(-1.58931, 1.1547, 0.375185)
points.InsertNextPoint(-1.58931, -1.1547, 0.375185)
points.InsertNextPoint(0.607062, -1.86835, 0.375185)
points.InsertNextPoint(1.58931, 1.1547, -0.375185)
points.InsertNextPoint(-0.607062, 1.86835, -0.375185)
points.InsertNextPoint(-1.96449, 0, -0.375185)
points.InsertNextPoint(-0.607062, -1.86835, -0.375185)
points.InsertNextPoint(1.58931, -1.1547, -0.375185)
points.InsertNextPoint(0.982247, 0.713644, -1.58931)
points.InsertNextPoint(-0.375185, 1.1547, -1.58931)
points.InsertNextPoint(-1.21412, 0, -1.58931)
points.InsertNextPoint(-0.375185, -1.1547, -1.58931)
points.InsertNextPoint(0.982247, -0.713644, -1.58931)
numberOfFaces = 12
# dodechedronFace的维数为面数*每个面对应的顶点数
dodechedronFace = [
[0, 1, 2, 3, 4],
[0, 5, 10, 6, 1],
[1, 6, 11, 7, 2],
[2, 7, 12, 8, 3],
[3, 8, 13, 9, 4],
[4, 9, 14, 5, 0],
[15, 10, 5, 14, 19],
[16, 11, 6, 10, 15],
[17, 12, 7, 11, 16],
[18, 13, 8, 12, 17],
[19, 14, 9, 13, 18],
[19, 18, 17, 16, 15]
]
dodechedronFaceIdList = vtkIdList()
dodechedronFaceIdList.InsertNextId(numberOfFaces) # 一共有多少个面
for face in dodechedronFace:
dodechedronFaceIdList.InsertNextId(len(face)) # 每个面有多少个顶点
[dodechedronFaceIdList.InsertNextId(i) for i in face] # 每个面的具体顶点索引
ug = vtkUnstructuredGrid()
ug.SetPoints(points)
ug.InsertNextCell(VTK_POLYHEDRON, dodechedronFaceIdList)
return ug
def MakePyramid():
numberOfVertices = 5
points = vtkPoints()
points.InsertNextPoint(1.0, 1.0, 0.0)
points.InsertNextPoint(-1.0, 1.0, 0.0)
points.InsertNextPoint(-1.0, -1.0, 0.0)
points.InsertNextPoint(1.0, -1.0, 0.0)
points.InsertNextPoint(0.0, 0.0, 1.0)
pyramid = vtkPyramid()
for i in range(0, numberOfVertices):
pyramid.GetPointIds().SetId(i, i)
ug = vtkUnstructuredGrid()
ug.SetPoints(points)
ug.InsertNextCell(pyramid.GetCellType(), pyramid.GetPointIds())
return ug
def MakeTetrahedron():
numberOfVertices = 4
points = vtkPoints()
points.InsertNextPoint(0, 0, 0)
points.InsertNextPoint(1, 0, 0)
points.InsertNextPoint(1, 1, 0)
points.InsertNextPoint(0, 1, 1)
tetra = vtkTetra()
for i in range(0, numberOfVertices):
tetra.GetPointIds().SetId(i, i)
ug = vtkUnstructuredGrid()
ug.SetPoints(points)
ug.InsertNextCell(tetra.GetCellType(), tetra.GetPointIds())
"""
示例代码中的写法是
cellArray = vtkCellArray()
cellArray.InsertNextCell(tetra)
ug = vtkUnstructuredGrid()
ug.SetPoints(points)
ug.SetCells(VTK_TETRA, cellArray)
示例代码是通过 vtkCellArray 来管理单元格的拓扑信息,更加适合创建复杂网格
"""
return ug
def MakeVoxel():
numberOfVertices = 8
points = vtkPoints()
points.InsertNextPoint(0, 0, 0)
points.InsertNextPoint(1, 0, 0)
points.InsertNextPoint(0, 1, 0)
points.InsertNextPoint(1, 1, 0)
points.InsertNextPoint(0, 0, 1)
points.InsertNextPoint(1, 0, 1)
points.InsertNextPoint(0, 1, 1)
points.InsertNextPoint(1, 1, 1)
voxel = vtkVoxel()
for i in range(0, numberOfVertices):
voxel.GetPointIds().SetId(i, i)
ug = vtkUnstructuredGrid()
ug.SetPoints(points)
ug.InsertNextCell(voxel.GetCellType(), voxel.GetPointIds())
return ug
def MakeWedge():
numberOfVertices = 6
points = vtkPoints()
points.InsertNextPoint(0, 1, 0)
points.InsertNextPoint(0, 0, 0)
points.InsertNextPoint(0, .5, .5)
points.InsertNextPoint(1, 1, 0)
points.InsertNextPoint(1, 0.0, 0.0)
points.InsertNextPoint(1, .5, .5)
wedge = vtkWedge()
for i in range(0, numberOfVertices):
wedge.GetPointIds().SetId(i, i)
ug = vtkUnstructuredGrid()
ug.SetPoints(points)
ug.InsertNextCell(wedge.GetCellType(), wedge.GetPointIds())
return ug
def main():
colors = vtkNamedColors()
colors.SetColor("BkgColor", [51, 77, 102, 255])
titles = []
textMappers = []
textActors = []
uGrids = []
mappers = []
actors = []
renderers = []
# 六棱柱
uGrids.append(MakeHexagonalPrism())
titles.append("Hexagonal Prism")
# 六面体
uGrids.append(MakeHexaheron())
titles.append("Hexahedron")
# 五棱柱
uGrids.append(MakePentagonalPrism())
titles.append('Pentagonal Prism')
# vtk手动构建正十二面体。通过定义顶点坐标和面的连接关系来创建一个非结构化网格
uGrids.append(MakePolyhedron())
titles.append("Polyhedron")
# 三棱锥
uGrids.append(MakePyramid())
titles.append("Pyramid")
# 四面体
uGrids.append(MakeTetrahedron())
titles.append("Tetrahedron")
# 体素,与坐标轴对齐
uGrids.append(MakeVoxel())
titles.append("Voxel")
# 楔体(三角柱)
uGrids.append(MakeWedge())
titles.append("Wedeg")
renWin = vtkRenderWindow()
renWin.SetWindowName("Cell3DDemonstration")
iRen = vtkRenderWindowInteractor()
iRen.SetRenderWindow(renWin)
textProperty = vtkTextProperty()
textProperty.SetFontSize(16)
textProperty.SetJustificationToCentered()
textProperty.SetColor(colors.GetColor3d("LightGoldenrodYellow"))
for i in range(0, len(uGrids)):
textMappers.append(vtkTextMapper())
textActors.append(vtkActor2D())
mappers.append(vtkDataSetMapper())
actors.append(vtkActor())
renderers.append(vtkRenderer())
mappers[i].SetInputData(uGrids[i])
actors[i].SetMapper(mappers[i])
actors[i].GetProperty().SetColor(colors.GetColor3d("PeachPuff"))
renderers[i].AddViewProp(actors[i])
textMappers[i].SetInput(titles[i])
textMappers[i].SetTextProperty(textProperty)
textActors[i].SetMapper(textMappers[i])
textActors[i].SetPosition(120, 16)
renderers[i].AddViewProp(textActors[i])
renWin.AddRenderer(renderers[i])
gridDimensions = 3
rendererSize = 300
renWin.SetSize(rendererSize * gridDimensions, rendererSize * gridDimensions)
for row in range(0, gridDimensions):
for col in range(0, gridDimensions):
index = row * gridDimensions + col
viewport = [
float(col) * rendererSize /
(gridDimensions * rendererSize),
float(gridDimensions - (row + 1)) * rendererSize /
(gridDimensions * rendererSize),
float(col + 1) * rendererSize /
(gridDimensions * rendererSize),
float(gridDimensions - row) * rendererSize /
(gridDimensions * rendererSize)]
if index > len(actors) - 1:
ren = vtkRenderer()
ren.SetBackground(colors.GetColor3d("BkgColor"))
ren.SetViewport(viewport)
renWin.AddRenderer(ren)
continue
renderers[index].SetViewport(viewport)
renderers[index].SetBackground(colors.GetColor3d('BkgColor'))
renderers[index].ResetCamera()
renderers[index].GetActiveCamera().Azimuth(30)
renderers[index].GetActiveCamera().Elevation(-30)
renderers[index].GetActiveCamera().Zoom(0.85)
renderers[index].ResetCameraClippingRange()
iRen.Initialize()
renWin.SetWindowName('Cell3DDemonstration')
renWin.Render()
iRen.Start()
if __name__ == '__main__':
main()
028:CellTypeSource
# !/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()
029:ConvexPointSet创建一个凸点集并将其可视化,同时用小球来表示每个顶点
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonCore import vtkPoints
from vtkmodules.vtkCommonDataModel import vtkConvexPointSet, vtkPolyData, vtkUnstructuredGrid
from vtkmodules.vtkFiltersSources import vtkSphereSource
from vtkmodules.vtkRenderingCore import vtkActor, vtkDataSetMapper, vtkGlyph3DMapper, vtkRenderer, vtkRenderWindow, \
vtkRenderWindowInteractor
def main():
points = vtkPoints()
points.InsertNextPoint(0, 0, 0)
points.InsertNextPoint(1, 0, 0)
points.InsertNextPoint(1, 1, 0)
points.InsertNextPoint(0, 1, 0)
points.InsertNextPoint(0, 0, 1)
points.InsertNextPoint(1, 0, 1)
points.InsertNextPoint(1, 1, 1)
points.InsertNextPoint(0, 1, 1)
points.InsertNextPoint(0.5, 0, 0)
points.InsertNextPoint(1, 0.5, 0)
points.InsertNextPoint(0.5, 1, 0)
points.InsertNextPoint(0, 0.5, 0)
points.InsertNextPoint(0.5, 0.5, 0)
cps = vtkConvexPointSet()
for i in range(0, 13):
# cps.GetPointIds().InsertNextId(i) 这种写法也行
cps.GetPointIds().InsertId(i, i)
"""
line = vtkPolyLine()
line.GetPointIds().SetNumberOfIds(idx)
for i in range(0, idx):
line.GetPointIds().SetId(i, i)
为什么不同的数据结构,在插入点的索引的时候,有的得用InsertId,有的得用SetId,有的用InsertNextId
不同的数据结构可能维护的不同的点ID数组,一种是动态增长的vtkIDList,一种是固定长度的数组vtkIdList
使用InsertId(i, i)时,InsertId 会在 vtkIdList 里自动扩展容量,并在指定的位置插入 ID
而使用SetId(i, i)时,需要先这是固定数组的长度,也就是.GetPointIds().SetNumberOfIds(num),提前分配好内存空间
InsertNextId(i)也是用于自动扩容的数据对象,它是直接添加到末尾。
动态数据结构,使用InsertId,InsertNextId。这些cell的点数不是固定的,典型的就是多边形,多面体一类,如vtkPolygon,vtkConvexPointSet
固定长度结构,使用SetId。这些cell的点数都是编译时定义好的,例如三角形就是 3 个点,四面体就是 4 个点
典型的有vtkLine,vtkTriangle,vtkQuad
"""
ug = vtkUnstructuredGrid()
"""
Allocate(numCells, extSize) 的作用就是
numCells:预先分配的 cell 数量(initial size)
extSize:当容量不够时,扩展时的增量(allocation increment)
"""
ug.Allocate(1, 1)
ug.InsertNextCell(cps.GetCellType(), cps.GetPointIds())
ug.SetPoints(points)
colors = vtkNamedColors()
mapper = vtkDataSetMapper()
mapper.SetInputData(ug)
actor = vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetColor(colors.GetColor3d("Tomato"))
actor.GetProperty().SetLineWidth(3)
actor.GetProperty().EdgeVisibilityOn()
sphere = vtkSphereSource()
sphere.SetRadius(.03)
sphere.SetThetaResolution(21)
sphere.SetPhiResolution(21)
polyData = vtkPolyData()
polyData.SetPoints(points)
pointMapper = vtkGlyph3DMapper()
pointMapper.SetInputData(polyData)
pointMapper.SetSourceConnection(sphere.GetOutputPort())
pointActor = vtkActor()
pointActor.SetMapper(pointMapper)
pointActor.GetProperty().SetColor(colors.GetColor3d("Peacock"))
# Create a renderer, render window, and interactor
renderer = vtkRenderer()
renderWindow = vtkRenderWindow()
renderWindow.SetWindowName("ConvexPointSet")
renderWindow.AddRenderer(renderer)
renderWindowInteractor = vtkRenderWindowInteractor()
renderWindowInteractor.SetRenderWindow(renderWindow)
# Add the actors to the scene
renderer.AddActor(actor)
renderer.AddActor(pointActor)
renderer.SetBackground(colors.GetColor3d("Silver"))
renderer.ResetCamera()
renderer.GetActiveCamera().Azimuth(210)
renderer.GetActiveCamera().Elevation(30)
renderer.ResetCameraClippingRange()
# Render and interact
renderWindow.SetSize(640, 480)
renderWindow.Render()
renderWindowInteractor.Start()
if __name__ == '__main__':
main()
030:LinearCellsDemo可视化一系列 VTK 的线性单元类型
from vtkmodules.vtkCommonDataModel import vtkVertex, vtkPolyVertex, vtkLine, vtkPolyLine, vtkTriangle, vtkTriangleStrip, \
vtkPolygon, vtkPixel, vtkQuad, vtkTetra, vtkVoxel, vtkHexahedron, vtkWedge, vtkPyramid, vtkPentagonalPrism, \
vtkHexagonalPrism, VTK_TETRA, vtkCellArray, vtkUnstructuredGrid
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonCore import vtkPoints
from vtkmodules.vtkRenderingLabel import vtkLabeledDataMapper
from vtkmodules.vtkRenderingCore import vtkActor, vtkActor2D, vtkDataSetMapper, vtkGlyph3DMapper, vtkLightKit, \
vtkPolyDataMapper, vtkProperty, vtkRenderWindow, vtkRenderWindowInteractor, vtkRenderer, vtkTextMapper, \
vtkTextProperty
from vtkmodules.vtkFiltersSources import vtkSphereSource
def make_vertex():
points = vtkPoints()
points.InsertNextPoint(0, 0, 0)
vertex = vtkVertex()
for i in range(0, 1):
vertex.GetPointIds().SetId(i, i)
ug = vtkUnstructuredGrid()
ug.SetPoints(points)
ug.InsertNextCell(vertex.GetCellType(), vertex.GetPointIds())
return ug
def make_poly_vertex():
points = vtkPoints()
points.InsertNextPoint(0, 0, 0)
points.InsertNextPoint(0, 0, 1)
points.InsertNextPoint(0, 2, 0)
points.InsertNextPoint(1, 0, 0)
points.InsertNextPoint(1, 1, 1)
poly_vertex = vtkPolyVertex()
poly_vertex.GetPointIds().SetNumberOfIds(5)
for i in range(0, 5):
poly_vertex.GetPointIds().SetId(i, i)
ug = vtkUnstructuredGrid()
ug.SetPoints(points)
ug.InsertNextCell(poly_vertex.GetCellType(), poly_vertex.GetPointIds())
return ug
def make_line():
points = vtkPoints()
points.InsertNextPoint(0, 0, 0)
points.InsertNextPoint(0, 0, 2)
line = vtkLine()
for i in range(0, 2):
line.GetPointIds().SetId(i, i)
ug = vtkUnstructuredGrid()
ug.SetPoints(points)
ug.InsertNextCell(line.GetCellType(), line.GetPointIds())
return ug
def make_polyline():
points = vtkPoints()
points.InsertNextPoint(0, 0, 1)
points.InsertNextPoint(0, 0, 2)
points.InsertNextPoint(0, 1, 2)
points.InsertNextPoint(1, 3, 3)
polyline = vtkPolyLine()
polyline.GetPointIds().SetNumberOfIds(4)
for i in range(0, 4):
polyline.GetPointIds().SetId(i, i)
ug = vtkUnstructuredGrid()
ug.SetPoints(points)
ug.InsertNextCell(polyline.GetCellType(), polyline.GetPointIds())
return ug
def make_triangle():
points = vtkPoints()
points.InsertNextPoint(0, 0, 0)
points.InsertNextPoint(0, 1, 0)
points.InsertNextPoint(1, 0, 0)
triangle = vtkTriangle()
for i in range(0, 3):
triangle.GetPointIds().SetId(i, i)
ug = vtkUnstructuredGrid()
ug.SetPoints(points)
ug.InsertNextCell(triangle.GetCellType(), triangle.GetPointIds())
return ug
def make_triangle_strip():
points = vtkPoints()
points.InsertNextPoint(0, 0, 0)
points.InsertNextPoint(1, -.1, 0)
points.InsertNextPoint(0.5, 1, 0)
points.InsertNextPoint(2.0, -0.1, 0)
points.InsertNextPoint(1.5, 0.8, 0)
points.InsertNextPoint(3.0, 0, 0)
points.InsertNextPoint(2.5, 0.9, 0)
points.InsertNextPoint(4.0, -0.2, 0)
points.InsertNextPoint(3.5, 0.8, 0)
points.InsertNextPoint(4.5, 1.1, 0)
triangle_strip = vtkTriangleStrip()
for i in range(0, 10):
triangle_strip.GetPointIds().InsertId(i, i)
ug = vtkUnstructuredGrid()
ug.SetPoints(points)
ug.InsertNextCell(triangle_strip.GetCellType(), triangle_strip.GetPointIds())
return ug
def make_polygon():
points = vtkPoints()
points.InsertNextPoint(0, 0, 0)
points.InsertNextPoint(0, 0, 0)
points.InsertNextPoint(1, -0.1, 0)
points.InsertNextPoint(0.8, 0.5, 0)
points.InsertNextPoint(1, 1, 0)
points.InsertNextPoint(0.6, 1.2, 0)
points.InsertNextPoint(0, 0.8, 0)
polygon = vtkPolygon()
for i in range(0, 6):
polygon.GetPointIds().InsertNextId(i)
ug = vtkUnstructuredGrid()
ug.SetPoints(points)
ug.InsertNextCell(polygon.GetCellType(), polygon.GetPointIds())
return ug
def make_pixel():
points = vtkPoints()
points.InsertNextPoint(0, 0, 0)
points.InsertNextPoint(1, 0, 0)
points.InsertNextPoint(0, 1, 0)
points.InsertNextPoint(1, 1, 0)
pixel = vtkPixel()
for i in range(0, 4):
pixel.GetPointIds().SetId(i, i)
ug = vtkUnstructuredGrid()
ug.SetPoints(points)
ug.InsertNextCell(pixel.GetCellType(), pixel.GetPointIds())
return ug
def make_quad():
points = vtkPoints()
points.InsertNextPoint(0, 0, 0)
points.InsertNextPoint(1, 0, 0)
points.InsertNextPoint(1, 1, 0)
points.InsertNextPoint(0, 1, 0)
quad = vtkQuad()
for i in range(0, 4):
quad.GetPointIds().SetId(i, i)
ug = vtkUnstructuredGrid()
ug.SetPoints(points)
ug.InsertNextCell(quad.GetCellType(), quad.GetPointIds())
return ug
def make_tetra():
points = vtkPoints()
points.InsertNextPoint(0, 0, 0)
points.InsertNextPoint(1, 0, 0)
points.InsertNextPoint(1, 0, -1)
points.InsertNextPoint(0, 1, -1)
tetra = vtkTetra()
for i in range(0, 4):
tetra.GetPointIds().SetId(i, i)
"""
另外的写法
ug = vtkUnstructuredGrid()
ug.SetPoints(points)
ug.InsertNextCell(tetra.GetCellType(), tetra.GetPointIds())
"""
cell_array = vtkCellArray()
cell_array.InsertNextCell(tetra)
ug = vtkUnstructuredGrid()
ug.SetPoints(points)
"""
InsertNextCell 往现有的 vtkUnstructuredGrid 里 追加一个单元
调用时机:逐个插入单元时用
"""
# ug.InsertNextCell(VTK_TETRA, cell_array)
"""
SetCells 一次性给网格设置 整批单元
调用时机:已经准备好了一个完整的 vtkCellArray,想一次性交给 vtkUnstructuredGrid
"""
ug.SetCells(VTK_TETRA, cell_array)
return ug
def make_voxel():
points = vtkPoints()
points.InsertNextPoint(0, 0, 0)
points.InsertNextPoint(1, 0, 0)
points.InsertNextPoint(0, 1, 0)
points.InsertNextPoint(1, 1, 0)
points.InsertNextPoint(0, 0, 1)
points.InsertNextPoint(1, 0, 1)
points.InsertNextPoint(0, 1, 1)
points.InsertNextPoint(1, 1, 1)
voxel = vtkVoxel()
for i in range(0, 8):
voxel.GetPointIds().SetId(i, i)
ug = vtkUnstructuredGrid()
ug.SetPoints(points)
ug.InsertNextCell(voxel.GetCellType(), voxel.GetPointIds())
return ug
def make_hexahedron():
points = vtkPoints()
points.InsertNextPoint(0, 0, 0)
points.InsertNextPoint(1, 0, 0)
points.InsertNextPoint(1, 1, 0)
points.InsertNextPoint(0, 1, 0)
points.InsertNextPoint(0, 0, 1)
points.InsertNextPoint(1, 0, 1)
points.InsertNextPoint(1, 1, 1)
points.InsertNextPoint(0, 1, 1)
hexahedron = vtkHexahedron()
for i in range(0, 8):
hexahedron.GetPointIds().SetId(i, i)
# Add the points and hexahedron to an unstructured grid
ug = vtkUnstructuredGrid()
ug.SetPoints(points)
ug.InsertNextCell(hexahedron.GetCellType(), hexahedron.GetPointIds())
return ug
def make_wedge():
# A wedge consists of two triangular ends and three rectangular faces.
number_of_vertices = 6
points = vtkPoints()
# points.InsertNextPoint(0, 1, 0)
# points.InsertNextPoint(0, 0, 0)
# points.InsertNextPoint(0, 0.5, 0.5)
# points.InsertNextPoint(1, 1, 0)
# points.InsertNextPoint(1, 0.0, 0.0)
# points.InsertNextPoint(1, 0.5, 0.5)
# Rotate the above points -90° about the X-axis
# and translate -1 along the Y-axis.
points.InsertNextPoint(0.0, 0.0, 0.0)
points.InsertNextPoint(0.0, 0, 1.0)
points.InsertNextPoint(0.0, 0.5, 0.5)
points.InsertNextPoint(1.0, 0.0, 0.0)
points.InsertNextPoint(1.0, 0, 1.0)
points.InsertNextPoint(1.0, 0.5, 0.5)
wedge = vtkWedge()
for i in range(0, number_of_vertices):
wedge.GetPointIds().SetId(i, i)
ug = vtkUnstructuredGrid()
ug.SetPoints(points)
ug.InsertNextCell(wedge.GetCellType(), wedge.GetPointIds())
return ug
def make_pyramid():
# Make a regular square pyramid.
number_of_vertices = 5
points = vtkPoints()
# p0 = [1.0, 1.0, 0.0]
# p1 = [-1.0, 1.0, 0.0]
# p2 = [-1.0, -1.0, 0.0]
# p3 = [1.0, -1.0, 0.0]
# p4 = [0.0, 0.0, 1.0]
# Rotate the above points -90° about the X-axis.
p0 = (1.0, 0, -1.0)
p1 = (-1.0, 0, -1.0)
p2 = (-1.0, 0, 1.0)
p3 = (1.0, 0, 1.0)
p4 = (0.0, 2.0, 0)
points.InsertNextPoint(p0)
points.InsertNextPoint(p1)
points.InsertNextPoint(p2)
points.InsertNextPoint(p3)
points.InsertNextPoint(p4)
pyramid = vtkPyramid()
for i in range(0, number_of_vertices):
pyramid.GetPointIds().SetId(i, i)
ug = vtkUnstructuredGrid()
ug.SetPoints(points)
ug.InsertNextCell(pyramid.GetCellType(), pyramid.GetPointIds())
return ug
def make_pentagonal_prism():
number_of_vertices = 10
pentagonal_prism = vtkPentagonalPrism()
scale = 2.0
pentagonal_prism.GetPoints().SetPoint(0, 11 / scale, 10 / scale, 10 / scale)
pentagonal_prism.GetPoints().SetPoint(1, 13 / scale, 10 / scale, 10 / scale)
pentagonal_prism.GetPoints().SetPoint(2, 14 / scale, 12 / scale, 10 / scale)
pentagonal_prism.GetPoints().SetPoint(3, 12 / scale, 14 / scale, 10 / scale)
pentagonal_prism.GetPoints().SetPoint(4, 10 / scale, 12 / scale, 10 / scale)
pentagonal_prism.GetPoints().SetPoint(5, 11 / scale, 10 / scale, 14 / scale)
pentagonal_prism.GetPoints().SetPoint(6, 13 / scale, 10 / scale, 14 / scale)
pentagonal_prism.GetPoints().SetPoint(7, 14 / scale, 12 / scale, 14 / scale)
pentagonal_prism.GetPoints().SetPoint(8, 12 / scale, 14 / scale, 14 / scale)
pentagonal_prism.GetPoints().SetPoint(9, 10 / scale, 12 / scale, 14 / scale)
for i in range(0, number_of_vertices):
pentagonal_prism.point_ids.SetId(i, i)
ug = vtkUnstructuredGrid()
ug.SetPoints(pentagonal_prism.GetPoints())
ug.InsertNextCell(pentagonal_prism.GetCellType(), pentagonal_prism.GetPointIds())
return ug
def make_hexagonal_prism():
number_of_vertices = 12
hexagonal_prism = vtkHexagonalPrism()
scale = 2.0
hexagonal_prism.GetPoints().SetPoint(0, 11 / scale, 10 / scale, 10 / scale)
hexagonal_prism.GetPoints().SetPoint(1, 13 / scale, 10 / scale, 10 / scale)
hexagonal_prism.GetPoints().SetPoint(2, 14 / scale, 12 / scale, 10 / scale)
hexagonal_prism.GetPoints().SetPoint(3, 13 / scale, 14 / scale, 10 / scale)
hexagonal_prism.GetPoints().SetPoint(4, 11 / scale, 14 / scale, 10 / scale)
hexagonal_prism.GetPoints().SetPoint(5, 10 / scale, 12 / scale, 10 / scale)
hexagonal_prism.GetPoints().SetPoint(6, 11 / scale, 10 / scale, 14 / scale)
hexagonal_prism.GetPoints().SetPoint(7, 13 / scale, 10 / scale, 14 / scale)
hexagonal_prism.GetPoints().SetPoint(8, 14 / scale, 12 / scale, 14 / scale)
hexagonal_prism.GetPoints().SetPoint(9, 13 / scale, 14 / scale, 14 / scale)
hexagonal_prism.GetPoints().SetPoint(10, 11 / scale, 14 / scale, 14 / scale)
hexagonal_prism.GetPoints().SetPoint(11, 10 / scale, 12 / scale, 14 / scale)
for i in range(0, number_of_vertices):
hexagonal_prism.point_ids.SetId(i, i)
ug = vtkUnstructuredGrid()
ug.SetPoints(hexagonal_prism.GetPoints())
ug.InsertNextCell(hexagonal_prism.GetCellType(), hexagonal_prism.GetPointIds())
return ug
def main():
colors = vtkNamedColors()
titles = []
ugrids = []
ugrids.append(make_vertex())
titles.append("vertex")
ugrids.append(make_poly_vertex())
titles.append("poly_vertex")
ugrids.append(make_line())
titles.append("line")
ugrids.append(make_polyline())
titles.append("poly_line")
ugrids.append(make_triangle())
titles.append("triangle")
ugrids.append(make_triangle_strip())
titles.append("triangle_strip")
ugrids.append(make_polygon())
titles.append("polygon")
ugrids.append(make_pixel())
titles.append("pixel")
ugrids.append(make_quad())
titles.append("quad")
ugrids.append(make_tetra())
titles.append("tetra")
ugrids.append(make_voxel())
titles.append("voxel")
ugrids.append(make_hexahedron())
titles.append('hexahedron')
ugrids.append(make_wedge())
titles.append("wedge")
ugrids.append(make_pyramid())
titles.append("pyramid")
ugrids.append(make_pentagonal_prism())
titles.append("pentagonal")
ugrids.append(make_hexagonal_prism())
titles.append("hexagonal_prism")
grid_column_dimensions = 4
grid_row_dimensions = 4
render_size = 300
window_size = (grid_row_dimensions * render_size, grid_column_dimensions * render_size)
viewports = {}
for row in range(0, grid_row_dimensions):
for col in range(0, grid_column_dimensions):
index = row * grid_column_dimensions + col
viewport = (float(col) / grid_column_dimensions,
float(grid_row_dimensions - (row + 1)) / grid_row_dimensions,
float(col + 1) / grid_column_dimensions,
float(grid_row_dimensions - row) / grid_row_dimensions)
viewports[titles[index]] = viewport
ren_win = vtkRenderWindow()
ren_win.SetSize(window_size)
ren_win.SetWindowName('LinearCellsDemo')
iren = vtkRenderWindowInteractor()
iren.SetRenderWindow(ren_win)
# Since we always import vtkmodules.vtkInteractionStyle we can do this
# because vtkInteractorStyleSwitch is automatically imported:
# iren.GetInteractorStyle().SetCurrentStyleToTrackballCamera()
for i in range(len(titles)):
# 要展示图形,点,图形点的索引,文字
# 图形
mapper = vtkDataSetMapper()
mapper.SetInputData(ugrids[i])
actor = vtkActor()
actor.SetMapper(mapper)
actor_property = vtkProperty()
actor_property.SetAmbientColor(colors.GetColor3d('DarkSalmon'))
actor_property.SetDiffuseColor(colors.GetColor3d('Seashell'))
actor_property.SetSpecularColor(colors.GetColor3d('White'))
actor_property.SetSpecular(0.5)
actor_property.SetDiffuse(0.7)
actor_property.SetAmbient(0.5)
actor_property.SetSpecularPower(20.0)
actor_property.SetOpacity(0.8)
actor_property.EdgeVisibilityOn()
actor_property.SetLineWidth(3)
actor.SetProperty(actor_property)
# 图形类型-文字
text_mapper = vtkTextMapper()
text_mapper.SetInput(titles[i])
text_property = vtkTextProperty()
text_property.BoldOn()
text_property.SetJustificationToCentered()
text_property.SetColor(colors.GetColor3d('Black'))
"""
图形类型-文字类设置属性在mapper中设置
"""
text_mapper.SetTextProperty(text_property)
text_actor = vtkActor2D()
text_actor.SetMapper(text_mapper)
text_actor.SetPosition(render_size / 2.0, 8)
# text_actor.SetProperty() # vtkProperty2D
# 索引
label_mapper = vtkLabeledDataMapper()
label_mapper.SetInputData(ugrids[i])
label_mapper.SetLabelTextProperty(text_property)
label_actor = vtkActor2D()
label_actor.SetMapper(label_mapper)
# 点
sphere = vtkSphereSource()
sphere.SetPhiResolution(21)
sphere.SetThetaResolution(21)
sphere.SetRadius(0.04)
point_mapper = vtkGlyph3DMapper()
point_mapper.SetInputData(ugrids[i])
point_mapper.SetSourceConnection(sphere.GetOutputPort())
point_mapper.ScalingOn()
point_mapper.ScalarVisibilityOff()
point_actor = vtkActor()
point_actor.SetMapper(point_mapper)
pty = vtkProperty()
pty.SetAmbientColor(colors.GetColor3d('Gold'))
pty.SetDiffuseColor(colors.GetColor3d('Yellow'))
pty.SetSpecularColor(colors.GetColor3d('White'))
pty.SetSpecular(0.5)
pty.SetDiffuse(0.7)
pty.SetAmbient(0.5)
pty.SetSpecularPower(20.0)
pty.SetOpacity(1.0)
point_actor.SetProperty(pty)
renderer = vtkRenderer()
renderer.SetBackground(colors.GetColor3d("LightSteelBlue"))
name_ = titles[i]
renderer.SetViewport(viewports[name_])
"""
设置灯光
"""
light_kit = vtkLightKit()
light_kit.AddLightsToRenderer(renderer)
renderer.AddActor(actor)
renderer.AddActor(text_actor)
renderer.AddActor(label_actor)
renderer.AddActor(point_actor)
renderer.ResetCamera()
renderer.ResetCameraClippingRange()
ren_win.AddRenderer(renderer)
ren_win.Render()
iren.Initialize()
iren.Start()
if __name__ == '__main__':
main()
031:LongLine多个Line组成折现
from vtkmodules.vtkCommonDataModel import vtkLine, vtkCellArray, vtkPolyData
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonCore import vtkPoints
from vtkmodules.vtkRenderingCore import vtkActor, vtkRenderer, vtkPolyDataMapper, vtkRenderWindow, \
vtkRenderWindowInteractor
def main():
points = vtkPoints()
points.InsertNextPoint(0.0, 0.0, 0.0)
points.InsertNextPoint(1.0, 0.0, 0.0)
points.InsertNextPoint(0.0, 1.0, 0.0)
points.InsertNextPoint(0.0, 1.0, 2.0)
points.InsertNextPoint(1.0, 2.0, 3.0)
lines = vtkCellArray()
for i in range(0, 4):
line = vtkLine()
line.GetPointIds().SetId(0, i)
line.GetPointIds().SetId(1, i+1)
lines.InsertNextCell(line)
linesPolydata = vtkPolyData()
linesPolydata.SetPoints(points)
linesPolydata.SetLines(lines)
colors = vtkNamedColors()
mapper = vtkPolyDataMapper()
mapper.SetInputData(linesPolydata)
actor = vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetLineWidth(4)
actor.GetProperty().SetColor(colors.GetColor3d('Peacock'))
# Setup render window, renderer, and interactor
renderer = vtkRenderer()
renderWindow = vtkRenderWindow()
renderWindow.SetWindowName('LongLine')
renderWindow.AddRenderer(renderer)
renderWindowInteractor = vtkRenderWindowInteractor()
renderWindowInteractor.SetRenderWindow(renderWindow)
renderer.AddActor(actor)
renderer.ResetCamera()
renderer.GetActiveCamera().Azimuth(30)
renderer.GetActiveCamera().Elevation(30)
renderer.ResetCameraClippingRange()
renderer.SetBackground(colors.GetColor3d('Silver'))
renderWindow.Render()
renderWindowInteractor.Start()
if __name__ == '__main__':
main()
032:Point点的展示与写入
from vtkmodules.vtkCommonCore import vtkPoints
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonDataModel import vtkCellArray, vtkPolyData, vtkVertex
from vtkmodules.vtkRenderingCore import vtkActor, vtkRenderer, vtkPolyDataMapper, vtkRenderWindow, \
vtkRenderWindowInteractor
def main():
colors = vtkNamedColors()
points = vtkPoints()
points.InsertNextPoint(1, 2, 3)
vertex = vtkVertex()
for i in range(1):
vertex.GetPointIds().SetId(i, i)
cellArray = vtkCellArray()
cellArray.InsertNextCell(vertex)
polyData = vtkPolyData()
polyData.SetPoints(points)
polyData.SetVerts(cellArray) # 只接受vtkCellArray数据类型
mapper = vtkPolyDataMapper()
mapper.SetInputData(polyData)
actor = vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetColor(colors.GetColor3d('Tomato'))
actor.GetProperty().SetPointSize(20)
renderer = vtkRenderer()
renderWindow = vtkRenderWindow()
renderWindow.SetWindowName('Point')
renderWindow.AddRenderer(renderer)
renderWindowInteractor = vtkRenderWindowInteractor()
renderWindowInteractor.SetRenderWindow(renderWindow)
renderer.AddActor(actor)
renderer.SetBackground(colors.GetColor3d('DarkGreen'))
renderWindow.Render()
renderWindowInteractor.Start()
if __name__ == '__main__':
main()
033:Tetrahedron两个三棱锥的显示与两种不同的写法
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonCore import vtkPoints
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonDataModel import VTK_TETRA, vtkCellArray, vtkTetra, vtkUnstructuredGrid
from vtkmodules.vtkRenderingCore import vtkActor, vtkRenderer, vtkDataSetMapper, vtkRenderWindow, \
vtkRenderWindowInteractor
def main():
color = vtkNamedColors()
points = vtkPoints()
points.InsertNextPoint(0, 0, 0)
points.InsertNextPoint(1, 0, 0)
points.InsertNextPoint(1, 1, 0)
points.InsertNextPoint(0, 1, 1)
points.InsertNextPoint(2, 2, 2)
points.InsertNextPoint(3, 2, 2)
points.InsertNextPoint(3, 3, 2)
points.InsertNextPoint(2, 3, 3)
ug = vtkUnstructuredGrid()
ug.SetPoints(points)
tetra1 = vtkTetra()
for i in range(4):
tetra1.GetPointIds().SetId(i, i)
tetra2 = vtkTetra()
tetra2.GetPointIds().SetId(0, 4)
tetra2.GetPointIds().SetId(1, 5)
tetra2.GetPointIds().SetId(2, 6)
tetra2.GetPointIds().SetId(3, 7)
"""
通过cellArray的另外一种写法
cellArray = vtkCellArray()
cellArray.InsertNextCell(tetra1)
cellArray.InsertNextCell(tetra2)
ug.SetCells(VTK_TETRA, cellArray)
"""
ug.InsertNextCell(tetra1.GetCellType(), tetra1.GetPointIds())
ug.InsertNextCell(tetra2.GetCellType(), tetra2.GetPointIds())
mapper = vtkDataSetMapper()
mapper.SetInputData(ug)
actor = vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetColor(color.GetColor3d("Cyan"))
render = vtkRenderer()
renderWindow = vtkRenderWindow()
renderWindow.SetWindowName("Tetrahedron")
renderWindow.AddRenderer(render)
renderWindowInteractor = vtkRenderWindowInteractor()
renderWindowInteractor.SetRenderWindow(renderWindow)
render.AddActor(actor)
render.SetBackground(color.GetColor3d("DarkGreen"))
render.ResetCamera()
render.GetActiveCamera().Azimuth(-10)
render.GetActiveCamera().Elevation(-20)
# Render and interact
renderWindow.Render()
renderWindowInteractor.Start()
if __name__ == '__main__':
main()
034:TriangleStrip连续三角带
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonCore import vtkPoints
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonDataModel import vtkCellArray, vtkPolyData, vtkTriangleStrip
from vtkmodules.vtkRenderingCore import vtkActor, vtkRenderer, vtkDataSetMapper, vtkRenderWindow, \
vtkRenderWindowInteractor
def main():
colors = vtkNamedColors()
points = vtkPoints()
points.InsertNextPoint(0, 0, 0)
points.InsertNextPoint(0, 1, 0)
points.InsertNextPoint(1, 0, 0)
points.InsertNextPoint(1.5, 1, 0)
triangleStrip = vtkTriangleStrip()
triangleStrip.GetPointIds().SetNumberOfIds(4)
for i in range(0, 4):
triangleStrip.GetPointIds().SetId(i, i)
"""
另外一种写法
不需要SetNumberOfIds
for i in range(0, 4):
triangleStrip.GetPointIds().InsertNextId(i)
"""
cells = vtkCellArray()
cells.InsertNextCell(triangleStrip)
polydata = vtkPolyData()
polydata.SetPoints(points)
polydata.SetStrips(cells)
# Create an actor and mapper
mapper = vtkDataSetMapper()
mapper.SetInputData(polydata)
actor = vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetColor(colors.GetColor3d('PeachPuff'))
actor.GetProperty().SetRepresentationToWireframe()
# Create a renderer, render window, and interactor
renderer = vtkRenderer()
renderWindow = vtkRenderWindow()
renderWindow.SetWindowName('TriangleStrip')
renderWindow.AddRenderer(renderer)
renderWindowInteractor = vtkRenderWindowInteractor()
renderWindowInteractor.SetRenderWindow(renderWindow)
renderer.AddActor(actor)
renderer.SetBackground(colors.GetColor3d('DarkGreen'))
renderWindow.Render()
renderWindowInteractor.Start()
if __name__ == '__main__':
main()
035:Cube 纯手动构建立方体
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonDataModel import vtkCellArray, vtkPolyData, vtkHexahedron
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkRenderingCore import vtkActor, vtkCamera, vtkPolyDataMapper, vtkRenderWindow, \
vtkRenderWindowInteractor, vtkRenderer
from vtkmodules.vtkCommonCore import vtkFloatArray, vtkIdList, vtkPoints
def main():
colors = vtkNamedColors()
points = vtkPoints()
points.InsertNextPoint(0.0, 0.0, 0.0)
points.InsertNextPoint(1.0, 0.0, 0.0)
points.InsertNextPoint(1.0, 1.0, 0.0)
points.InsertNextPoint(0.0, 1.0, 0.0)
points.InsertNextPoint(0.0, 0.0, 1.0)
points.InsertNextPoint(1.0, 0.0, 1.0)
points.InsertNextPoint(1.0, 1.0, 1.0)
points.InsertNextPoint(0.0, 1.0, 1.0)
pts = [(0, 3, 2, 1), (4, 5, 6, 7), (0, 1, 5, 4),
(1, 2, 6, 5), (2, 3, 7, 6), (3, 0, 4, 7)]
polys = vtkCellArray()
for num_face in range(0, 6):
vil = vtkIdList()
for i in pts[num_face]:
vil.InsertNextId(i)
polys.InsertNextCell(vil) # vil只是一个IDlist
"""
另外一种通过坐标点和点的索引构建mesh的写法
def MakePolyhedron():
points = vtkPoints()
points.InsertNextPoint(1.21412, 0, 1.58931)
points.InsertNextPoint(0.375185, 1.1547, 1.58931)
points.InsertNextPoint(-0.982247, 0.713644, 1.58931)
points.InsertNextPoint(-0.982247, -0.713644, 1.58931)
points.InsertNextPoint(0.375185, -1.1547, 1.58931)
points.InsertNextPoint(1.96449, 0, 0.375185)
points.InsertNextPoint(0.607062, 1.86835, 0.375185)
points.InsertNextPoint(-1.58931, 1.1547, 0.375185)
points.InsertNextPoint(-1.58931, -1.1547, 0.375185)
points.InsertNextPoint(0.607062, -1.86835, 0.375185)
points.InsertNextPoint(1.58931, 1.1547, -0.375185)
points.InsertNextPoint(-0.607062, 1.86835, -0.375185)
points.InsertNextPoint(-1.96449, 0, -0.375185)
points.InsertNextPoint(-0.607062, -1.86835, -0.375185)
points.InsertNextPoint(1.58931, -1.1547, -0.375185)
points.InsertNextPoint(0.982247, 0.713644, -1.58931)
points.InsertNextPoint(-0.375185, 1.1547, -1.58931)
points.InsertNextPoint(-1.21412, 0, -1.58931)
points.InsertNextPoint(-0.375185, -1.1547, -1.58931)
points.InsertNextPoint(0.982247, -0.713644, -1.58931)
numberOfFaces = 12
# dodechedronFace的维数为面数*每个面对应的顶点数
dodechedronFace = [
[0, 1, 2, 3, 4],
[0, 5, 10, 6, 1],
[1, 6, 11, 7, 2],
[2, 7, 12, 8, 3],
[3, 8, 13, 9, 4],
[4, 9, 14, 5, 0],
[15, 10, 5, 14, 19],
[16, 11, 6, 10, 15],
[17, 12, 7, 11, 16],
[18, 13, 8, 12, 17],
[19, 14, 9, 13, 18],
[19, 18, 17, 16, 15]
]
dodechedronFaceIdList = vtkIdList()
dodechedronFaceIdList.InsertNextId(numberOfFaces) # 一共有多少个面
for face in dodechedronFace:
dodechedronFaceIdList.InsertNextId(len(face)) # 每个面有多少个顶点
[dodechedronFaceIdList.InsertNextId(i) for i in face] # 每个面的具体顶点索引
ug = vtkUnstructuredGrid()
ug.SetPoints(points)
ug.InsertNextCell(VTK_POLYHEDRON, dodechedronFaceIdList)
return ug
"""
polydata = vtkPolyData()
polydata.SetPoints(points)
polydata.SetPolys(polys)
cubeMapper = vtkPolyDataMapper()
cubeMapper.SetInputData(polydata)
# cubeMapper.SetScalarRange(polydata.GetScalarRange())
cubeActor = vtkActor()
cubeActor.SetMapper(cubeMapper)
# The usual rendering stuff.
camera = vtkCamera()
camera.SetPosition(1, 1, 1)
camera.SetFocalPoint(0, 0, 0)
renderer = vtkRenderer()
renWin = vtkRenderWindow()
renWin.AddRenderer(renderer)
iren = vtkRenderWindowInteractor()
iren.SetRenderWindow(renWin)
renderer.AddActor(cubeActor)
renderer.SetActiveCamera(camera)
renderer.ResetCamera()
renderer.SetBackground(colors.GetColor3d("Cornsilk"))
renWin.SetSize(600, 600)
renWin.SetWindowName("Cube")
# interact with data
renWin.Render()
iren.Start()
if __name__ == '__main__':
main()
036:Frustum 通过获取 VTK 相机的参数来定义一个截锥体,然后将其作为模型进行渲染
from vtkmodules.vtkRenderingCore import vtkCamera, vtkActor, vtkPolyDataMapper, vtkProperty, vtkRenderWindow, \
vtkRenderWindowInteractor, vtkRenderer
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonDataModel import vtkPlanes
from vtkmodules.vtkFiltersGeneral import vtkShrinkPolyData
from vtkmodules.vtkFiltersSources import vtkFrustumSource
from vtkmodules.vtkCommonColor import vtkNamedColors
def main():
colors = vtkNamedColors()
camera = vtkCamera()
camera.SetClippingRange(0.1, 0.4)
planesArray = [0] * 24
"""
获取可见空间的接椎体,将每个平面的方程系数存储到planesArray中
1.0 是一个缩放因子。它决定了最终的截锥体大小
"""
camera.GetFrustumPlanes(1.0, planesArray)
planes = vtkPlanes()
"""
将一个包含六个平面方程系数的数组,设置给一个 vtkPlanes 对象
"""
planes.SetFrustumPlanes(planesArray)
frustumSource = vtkFrustumSource()
frustumSource.ShowLinesOff()
frustumSource.SetPlanes(planes)
"""
vtkShrinkPolyData 是一个专用过滤器,仅用于 vtkPolyData
vtkShrinkFillters 是一个通用过滤器,适用各种数据类型
"""
shrink = vtkShrinkPolyData()
shrink.SetInputConnection(frustumSource.GetOutputPort())
shrink.SetShrinkFactor(.9)
mapper = vtkPolyDataMapper()
mapper.SetInputConnection(shrink.GetOutputPort())
back = vtkProperty()
back.SetColor(colors.GetColor3d("Tomato"))
actor = vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().EdgeVisibilityOn()
actor.GetProperty().SetColor(colors.GetColor3d("Banana"))
# 为三维模型的背景设置不同的渲染属性
actor.SetBackfaceProperty(back)
# a renderer and render window
renderer = vtkRenderer()
renderWindow = vtkRenderWindow()
renderWindow.SetWindowName("Frustum")
renderWindow.AddRenderer(renderer)
# an interactor
renderWindowInteractor = vtkRenderWindowInteractor()
renderWindowInteractor.SetRenderWindow(renderWindow)
# add the actors to the scene
renderer.AddActor(actor)
renderer.SetBackground(colors.GetColor3d("Silver"))
# Position the camera so that we can see the frustum
renderer.GetActiveCamera().SetPosition(1, 0, 0)
renderer.GetActiveCamera().SetFocalPoint(0, 0, 0)
renderer.GetActiveCamera().SetViewUp(0, 1, 0)
renderer.GetActiveCamera().Azimuth(30)
renderer.GetActiveCamera().Elevation(30)
renderer.ResetCamera()
# render an image (lights and cameras are created automatically)
renderWindow.Render()
# begin mouse interaction
renderWindowInteractor.Start()
if __name__ == '__main__':
main()
037:OrientedArrow 在两个随机生成的点之间绘制一根带箭头的线,以可视化一个向量
from vtkmodules.vtkFiltersSources import vtkArrowSource
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonCore import vtkMath, vtkMinimalStandardRandomSequence
from vtkmodules.vtkCommonMath import vtkMatrix4x4
from vtkmodules.vtkCommonTransforms import vtkTransform
from vtkmodules.vtkFiltersGeneral import vtkTransformPolyDataFilter
from vtkmodules.vtkFiltersSources import (
vtkArrowSource,
vtkSphereSource
)
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkPolyDataMapper,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkRenderer
)
def main():
colors = vtkNamedColors()
colors.SetColor("BkgColor", [26, 51, 77, 255])
USE_MATRIX = True
"""
arrowSource无法通过自身携带的函数设置箭头的起点、终点和方向
很多vtk源都创建了一个标准化的模型,然后由变换负责将其移动、旋转和缩放到正确的位置
这样做的目的是:
解耦:将几何体的生成和它的空间变换分离开来,使得代码更清晰、更模块化
效率:你可以在一次渲染中,对一个原始模型应用不同的变换,来创建多个实例
"""
arrowSource = vtkArrowSource() # 标准箭头,从(0,0,0)指向(1, 0, 0)
"""
下面这段代码的目的就是创建一个变换矩阵,用于将一个标准化的箭头
(从 (0,0,0) 指向 (1,0,0) 的箭头)移动、旋转和缩放,使其从一个随机起点指向另一个随机终点
"""
startPoint = [0] * 3
endPoint = [0] * 3
rng = vtkMinimalStandardRandomSequence()
rng.SetSeed(82443)
for i in range(3):
rng.Next()
startPoint[i] = rng.GetRangeValue(-10, 10)
rng.Next()
endPoint[i] = rng.GetRangeValue(-10, 10)
normalizedX = [0] * 3
normalizedY = [0] * 3
normalizedZ = [0] * 3
vtkMath.Subtract(endPoint, startPoint, normalizedX)
length = vtkMath.Norm(normalizedX)
vtkMath.Normalize(normalizedX)
arbitrary = [0] * 3
for i in range(0, 3):
rng.Next()
arbitrary[i] = rng.GetRangeValue(-10, 10)
vtkMath.Cross(normalizedX, arbitrary, normalizedZ)
vtkMath.Normalize(normalizedZ)
vtkMath.Cross(normalizedZ, normalizedX, normalizedZ)
matrix = vtkMatrix4x4()
matrix.Identity()
for i in range(0, 3):
matrix.SetElement(i, 0, normalizedX[i])
matrix.SetElement(i, 1, normalizedY[i])
matrix.SetElement(i, 2, normalizedZ[i])
"""
必须严格按照缩放 → 旋转 → 平移的顺序来应用这些变换
个变换矩阵可以将一个点从其局部坐标系转换到世界坐标系。
T 代表平移矩阵(Translation)
R 代表旋转矩阵(Rotation)
S 代表缩放矩阵(Scale)
P_local 代表点在局部坐标系中的位置
P_world 代表点在世界坐标系中的位置
最终的变换公式是:
Pworld =T∗R∗S∗Plocal
这个公式意味着,对一个点应用变换时,应该先缩放,再旋转,最后平移。这是因为:
如果你先平移再旋转:物体会绕着新的平移位置旋转,而不是绕着它自身的中心旋转,这通常不是你想要的效果。
如果你先旋转再缩放:缩放会沿着已经旋转过的轴进行,可能导致不均匀的变形。
因此,从数学上讲,缩放 → 旋转 → 平移 的顺序是正确的。
"""
"""
vtkTransform的Concatenate 方法
有一个重要的特性,它执行的是矩阵后乘。这意味着,新连接的变换矩阵会放在当前变换矩阵的右边
最终的复合变换矩阵是 T∗R∗S,这与数学上的正确顺序完全一致。
因此,在 VTK 中使用 Concatenate 方法时,你需要按照平移、旋转、缩放的顺序调用函数,
才能得到正确的缩放、旋转、平移效果。
"""
transform = vtkTransform()
transform.Translate(startPoint)
transform.Concatenate(matrix)
transform.Scale(length, length, length)
"""
两种思路
1、直接对数据进行操作,产生新的数据,
2、在actor中,使用SetUserMatrix,不会产生新的数据,效率更高,但是不会改变底层数据
"""
transformPD = vtkTransformPolyDataFilter()
transformPD.SetTransform(transform)
transformPD.SetInputConnection(arrowSource.GetOutputPort())
mapper = vtkPolyDataMapper()
actor = vtkActor()
if USE_MATRIX:
mapper.SetInputConnection(arrowSource.GetOutputPort())
actor.SetUserMatrix(transform.GetMatrix())
else:
mapper.SetInputConnection(transformPD.GetOutputPort())
actor.SetMapper(mapper)
actor.GetProperty().SetColor(colors.GetColor3d("Cyan"))
sphereStartSource = vtkSphereSource()
sphereStartSource.SetCenter(startPoint)
sphereStartSource.SetRadius(0.8)
sphereStartMapper = vtkPolyDataMapper()
sphereStartMapper.SetInputConnection(sphereStartSource.GetOutputPort())
sphereStart = vtkActor()
sphereStart.SetMapper(sphereStartMapper)
sphereStart.GetProperty().SetColor(colors.GetColor3d('Yellow'))
sphereEndSource = vtkSphereSource()
sphereEndSource.SetCenter(endPoint)
sphereEndSource.SetRadius(0.8)
sphereEndMapper = vtkPolyDataMapper()
sphereEndMapper.SetInputConnection(sphereEndSource.GetOutputPort())
sphereEnd = vtkActor()
sphereEnd.SetMapper(sphereEndMapper)
sphereEnd.GetProperty().SetColor(colors.GetColor3d('Magenta'))
# Create a renderer, render window, and interactor
renderer = vtkRenderer()
renderWindow = vtkRenderWindow()
renderWindow.SetWindowName('OrientedArrow')
renderWindow.AddRenderer(renderer)
renderWindowInteractor = vtkRenderWindowInteractor()
renderWindowInteractor.SetRenderWindow(renderWindow)
# Add the actor to the scene
renderer.AddActor(actor)
renderer.AddActor(sphereStart)
renderer.AddActor(sphereEnd)
renderer.SetBackground(colors.GetColor3d('BkgColor'))
# Render and interact
renderWindow.Render()
renderWindowInteractor.Start()
if __name__ == '__main__':
main()
038:OrientedCylinder圆柱体旋转、平移缩放
from vtkmodules.vtkFiltersSources import vtkCylinderSource, vtkSphereSource
from vtkmodules.vtkCommonMath import vtkMatrix4x4
from vtkmodules.vtkCommonCore import vtkMath, vtkMinimalStandardRandomSequence
from vtkmodules.vtkCommonTransforms import vtkTransform
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkFiltersGeneral import vtkTransformPolyDataFilter
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkPolyDataMapper,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkRenderer
)
def main():
colors = vtkNamedColors()
colors.SetColor("BkgColor", [26, 51, 77, 255])
USER_MATRIX = True
cylinderSource = vtkCylinderSource()
cylinderSource.SetResolution(15)
start_point = [0] * 3
end_point = [0] * 3
rng = vtkMinimalStandardRandomSequence()
rng.SetSeed(8775070)
for i in range(0, 3):
rng.Next()
start_point[i] = rng.GetRangeValue(-10, 10)
rng.Next()
end_point[i] = rng.GetRangeValue(-10, 10)
normalizedX = [0] * 3
normalizedY = [0] * 3
normalizedZ = [0] * 3
vtkMath.Subtract(end_point, start_point, normalizedX)
length = vtkMath.Norm(normalizedX)
vtkMath.Normalize(normalizedX)
arbitrary = [0] * 3
for i in range(3):
rng.Next()
arbitrary[i] = rng.GetRangeValue(-10, 10)
vtkMath.Cross(normalizedX, arbitrary, normalizedZ)
vtkMath.Normalize(arbitrary)
vtkMath.Cross(normalizedX, normalizedZ, normalizedY)
matrix = vtkMatrix4x4()
matrix.Identity()
for i in range(3):
matrix.SetElement(i, 0, normalizedX[i])
matrix.SetElement(i, 1, normalizedY[i])
matrix.SetElement(i, 2, normalizedZ[i])
transform = vtkTransform()
transform.Translate(start_point)
transform.Concatenate(matrix)
"""
这是新增加的一步。因为圆柱体的默认方向是Y轴,而你的 normalizedX 向量与X轴对齐,
你需要先将圆柱体绕其中心旋转 −90 度,使其从 Y 轴转向 X 轴。这个操作必须在应用 matrix 之前进行
"""
transform.RotateZ(-90.0) # align cylinder to x axis
transform.Scale(length, length, length)
"""
vtkCylinderSource 的中心位于圆柱体中间。为了让圆柱体的一端(而不是中心)位于 startPoint,
你需要将其沿着Y轴(圆柱体的高度方向)平移 0.5
"""
transform.Translate(0, .5, 0) # translate to start of cylinder
# Transform the polydata
transformPD = vtkTransformPolyDataFilter()
transformPD.SetTransform(transform)
transformPD.SetInputConnection(cylinderSource.GetOutputPort())
# Create a mapper and actor for the arrow
mapper = vtkPolyDataMapper()
actor = vtkActor()
if USER_MATRIX:
mapper.SetInputConnection(cylinderSource.GetOutputPort())
actor.SetUserMatrix(transform.GetMatrix())
else:
mapper.SetInputConnection(transformPD.GetOutputPort())
actor.SetMapper(mapper)
actor.GetProperty().SetColor(colors.GetColor3d('Cyan'))
# Create spheres for start and end point
sphereStartSource = vtkSphereSource()
sphereStartSource.SetCenter(start_point)
sphereStartSource.SetRadius(0.8)
sphereStartMapper = vtkPolyDataMapper()
sphereStartMapper.SetInputConnection(sphereStartSource.GetOutputPort())
sphereStart = vtkActor()
sphereStart.SetMapper(sphereStartMapper)
sphereStart.GetProperty().SetColor(colors.GetColor3d('Yellow'))
sphereEndSource = vtkSphereSource()
sphereEndSource.SetCenter(end_point)
sphereEndSource.SetRadius(0.8)
sphereEndMapper = vtkPolyDataMapper()
sphereEndMapper.SetInputConnection(sphereEndSource.GetOutputPort())
sphereEnd = vtkActor()
sphereEnd.SetMapper(sphereEndMapper)
sphereEnd.GetProperty().SetColor(colors.GetColor3d('Magenta'))
# Create a renderer, render window, and interactor
renderer = vtkRenderer()
renderWindow = vtkRenderWindow()
renderWindow.AddRenderer(renderer)
renderWindow.SetWindowName('Oriented Cylinder')
renderWindowInteractor = vtkRenderWindowInteractor()
renderWindowInteractor.SetRenderWindow(renderWindow)
# Add the actor to the scene
renderer.AddActor(actor)
renderer.AddActor(sphereStart)
renderer.AddActor(sphereEnd)
renderer.SetBackground(colors.GetColor3d('BkgColor'))
# Render and interact
renderWindow.Render()
renderWindowInteractor.Start()
if __name__ == '__main__':
main()
039:PlatonicSolids柏拉图多面体的生成
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonCore import vtkLookupTable
from vtkmodules.vtkFiltersSources import vtkPlatonicSolidSource
from vtkmodules.vtkRenderingCore import vtkActor, vtkRenderer, vtkActor2D, vtkPolyDataMapper, vtkRenderWindow, \
vtkRenderWindowInteractor, vtkTextMapper, vtkTextProperty
def get_platonic_lut():
lut = vtkLookupTable()
lut.SetNumberOfTableValues(20) # 设置查找表可以存储20个颜色值
"""
SetNumberOfColors与SetNumberOfTableValues功能相近
是定义实际使用的离散颜色数量
但是SetNumberOfTableValues更常用,也更加精确
"""
# lut.SetNumberOfColors()
"""
SetTableRange设置查找表映射的数据值范围。
这告诉VTK,输入的数据值会从0.0变化到19.0,并根据这个范围去查找对应的颜色
"""
lut.SetTableRange(0.0, 19.0)
lut.Build() # 构建颜色查找表内部结构,以高效查找颜色
"""
SetTableValue(index, r, g, b)
手动为查找表设置了20个独立的颜色值
例如:0, 0.1, 0.1, 0.1 将数据值0映射为灰黑色
"""
lut.SetTableValue(0, 0.1, 0.1, 0.1)
lut.SetTableValue(1, 0, 0, 1)
lut.SetTableValue(2, 0, 1, 0)
lut.SetTableValue(3, 0, 1, 1)
lut.SetTableValue(4, 1, 0, 0)
lut.SetTableValue(5, 1, 0, 1)
lut.SetTableValue(6, 1, 1, 0)
lut.SetTableValue(7, 0.9, 0.7, 0.9)
lut.SetTableValue(8, 0.5, 0.5, 0.5)
lut.SetTableValue(9, 0.0, 0.0, 0.7)
lut.SetTableValue(10, 0.5, 0.7, 0.5)
lut.SetTableValue(11, 0, 0.7, 0.7)
lut.SetTableValue(12, 0.7, 0, 0)
lut.SetTableValue(13, 0.7, 0, 0.7)
lut.SetTableValue(14, 0.7, 0.7, 0)
lut.SetTableValue(15, 0, 0, 0.4)
lut.SetTableValue(16, 0, 0.4, 0)
lut.SetTableValue(17, 0, 0.4, 0.4)
lut.SetTableValue(18, 0.4, 0, 0)
lut.SetTableValue(19, 0.4, 0, 0.4)
return lut
def main():
colors = vtkNamedColors()
platonic_solids = []
mappers = []
actors = []
text_mappers = []
text_actors = []
renderers = []
# 文字的属性
text_property = vtkTextProperty()
text_property.SetFontSize(16)
text_property.SetJustificationToCentered()
ren_win = vtkRenderWindow()
ren_win.SetWindowName("PlatonicSolids")
iren = vtkRenderWindowInteractor()
iren.SetRenderWindow(ren_win)
# 获取各个render的viewport
all_viewport = []
grid_dimension_x = 3
grid_dimension_y = 2
renderer_size = 300
ren_win.SetSize(renderer_size * grid_dimension_x, renderer_size * grid_dimension_y)
for row in range(0, grid_dimension_y):
for col in range(0, grid_dimension_x):
index = row * grid_dimension_x + col
viewport = [float(col) / grid_dimension_x,
float(grid_dimension_y - (row + 1)) / grid_dimension_y,
float(col + 1) / grid_dimension_x,
float(grid_dimension_y - row) / grid_dimension_y]
all_viewport.append(viewport)
# 建立颜色映射表
lut = get_platonic_lut()
name_list = ['Tetrahedron', 'Cube', 'Octahedron', 'Icosahedron', 'Dodecahedron']
for i in range(0, len(name_list)):
platonic_solids.append(vtkPlatonicSolidSource())
# 柏拉图多面体对象通过SetSolidType来生成特定的多面体
platonic_solids[i].SetSolidType(i)
mappers.append(vtkPolyDataMapper())
mappers[i].SetInputConnection(platonic_solids[i].GetOutputPort())
"""
SetLookupTable 将颜色映射表与映射器mapper关联起来。
查找表是一个将数据值(如一个面的ID)映射到颜色值的字典。
当映射器处理多面体数据时,它会查找每个面对应的标量值,
然后使用这个查找表来决定该面应该是什么颜色
"""
mappers[i].SetLookupTable(lut)
"""
SetScalarRange 设置了标量数据的范围
柏拉图多面体的每个面都有一个唯一的整数ID,从0开始。
SetScalarRange(0, 19) 告诉映射器,数据值在这个范围内变化。
这个范围必须与查找表(lut)的范围相匹配,
以确保每个数据值都能找到一个正确的颜色
"""
mappers[i].SetScalarRange(0, 19)
"""
以上代码并没有显式设置以单元格数据进行着色,而mapper却能够以单元格ID进行着色
这是因为在 VTK 中,当一个 PolyDataMapper 或 DataSetMapper
接收一个带有**单元格数据(Cell Data)**的输入时,
它会默认使用这个数据来进行着色
当然也可以显式设置标量模式
mapper.SetScalarModeToUseCellData()
设置以顶点索引作为着色模式
mappers[i].SetScalarModeToUsePointData()
mappers[i].SetScalarRange(0, platonic_solids[i].GetOutput().GetNumberOfPoints() - 1)
具体写法如下:
platonic_solids.append(vtkPlatonicSolidSource())
platonic_solids[i].SetSolidType(i)
platonic_solids[i].Update()
polydata = platonic_solids[i].GetOutput()
# 🔑 构造 scalars:每个点一个 ID
scalars = vtkIntArray()
scalars.SetNumberOfComponents(1)
scalars.SetName("PointIds")
for pid in range(polydata.GetNumberOfPoints()):
scalars.InsertNextValue(pid)
polydata.GetPointData().SetScalars(scalars)
mappers.append(vtkPolyDataMapper())
mappers[i].SetInputData(polydata)
mappers[i].SetLookupTable(lut)
mappers[i].SetScalarModeToUsePointData()
mappers[i].SetScalarRange(0, polydata.GetNumberOfPoints() - 1)
"""
actors.append(vtkActor())
actors[i].SetMapper(mappers[i])
text_mappers.append(vtkTextMapper())
text_mappers[i].SetInput(name_list[i])
text_mappers[i].SetTextProperty(text_property)
text_actors.append(vtkActor2D())
text_actors[i].SetMapper(text_mappers[i])
text_actors[i].SetPosition(120, 16)
renderers.append(vtkRenderer())
renderers[i].AddActor(actors[i])
renderers[i].AddViewProp(text_actors[i])
renderers[i].SetViewport(all_viewport[i])
renderers[i].SetBackground(colors.GetColor3d('SlateGray'))
renderers[i].ResetCamera()
renderers[i].ResetCameraClippingRange()
ren_win.AddRenderer(renderers[i])
# 最后一个render的设置
ren_ = vtkRenderer()
ren_.SetBackground(colors.GetColor3d('SlateGray'))
ren_.SetViewport(all_viewport[-1])
ren_win.AddRenderer(ren_)
iren.Initialize()
ren_win.Render()
iren.Start()
if __name__ == '__main__':
main()
040:PolyLine1 形成空间的polyline,source和cellArray的区别
from vtkmodules.vtkFiltersSources import vtkPolyLineSource
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonCore import vtkPoints
from vtkmodules.vtkCommonDataModel import vtkCellArray, vtkPolyData
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkPolyDataMapper,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkRenderer
)
import math
def main():
colors = vtkNamedColors()
colors.SetColor("BkgColor", [26, 51, 102, 255])
"""
source的写法
c = math.cos(math.pi / 6)
polyLine = vtkPolyLineSource()
polyLine.SetNumberOfPoints(6)
polyLine.SetPoint(0, 0.0, -1.0, 0.0)
polyLine.SetPoint(1, c, -0.5, 0.0)
polyLine.SetPoint(2, c, 0.5, 0.0)
polyLine.SetPoint(3, 0.0, 1.0, 0.0)
polyLine.SetPoint(4, -c, 0.5, 0.0)
polyLine.SetPoint(5, -c, -0.5, 0.0)
polyLine.ClosedOn() # 或者 polyLine.SetClosed(True)
# polyLine.SetPoint(0, 0.0, -1.0, 0.0)
polyLine.Update()
polygonMapper = vtkPolyDataMapper()
polygonMapper.SetInputData(polyLine.GetOutput())
polygonMapper.Update()
"""
# 第二种写法
c = math.cos(math.pi / 6)
points = vtkPoints()
points.SetNumberOfPoints(6)
points.SetPoint(0, 0.0, -1.0, 0.0)
points.SetPoint(1, c, -0.5, 0.0)
points.SetPoint(2, c, 0.5, 0.0)
points.SetPoint(3, 0.0, 1.0, 0.0)
points.SetPoint(4, -c, 0.5, 0.0)
points.SetPoint(5, -c, -0.5, 0.0)
lines = vtkCellArray()
"""
InsertNextCell(n)
作用:告诉 VTK,“我要插入一条新的 Cell(单元)”,并且它包含 n 个点。
参数 n:表示这个 Cell 的顶点数量。
调用后,VTK 就会准备一段空间,等着你用 InsertCellPoint 把这些点的 id 填进去。
InsertCellPoint(id)
作用:往当前 Cell 里依次插入点的 id(索引)。
参数 id:是 vtkPoints 中点的索引,而不是坐标。
你必须插入的点数量和 InsertNextCell(n) 声明的 n 一致,否则会报错
"""
lines.InsertNextCell(7)
lines.InsertCellPoint(0)
lines.InsertCellPoint(1)
lines.InsertCellPoint(2)
lines.InsertCellPoint(3)
lines.InsertCellPoint(4)
lines.InsertCellPoint(5)
lines.InsertCellPoint(0)
polygon = vtkPolyData()
polygon.SetPoints(points)
polygon.SetLines(lines)
polygonMapper = vtkPolyDataMapper()
polygonMapper.SetInputData(polygon)
polygonMapper.Update()
polygonActor = vtkActor()
polygonActor.SetMapper(polygonMapper)
polygonActor.GetProperty().SetColor(colors.GetColor3d('AliceBlue'))
ren = vtkRenderer()
ren.AddActor(polygonActor)
ren.SetBackground(colors.GetColor3d('BkgColor'))
ren.ResetCamera()
renWin = vtkRenderWindow()
renWin.SetWindowName('PolyLine1')
renWin.AddRenderer(ren)
renWin.SetSize(300, 300)
iren = vtkRenderWindowInteractor()
iren.SetRenderWindow(renWin)
iren.Initialize()
iren.Start()
if __name__ == '__main__':
main()
041:RegularPolygonSource规则多边形
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkFiltersSources import vtkRegularPolygonSource
from vtkmodules.vtkFiltersGeneral import vtkShrinkPolyData
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkPolyDataMapper,
vtkProperty,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkRenderer
)
def main():
colors = vtkNamedColors()
source = vtkRegularPolygonSource()
source.SetCenter(0,0,0)
source.SetRadius(5)
source.SetNumberOfSides(5)
source.Update()
shrink = vtkShrinkPolyData()
"""
SetInputData(source.GetOutput()) 想要使用这个
source需要调用Updata()方法
因为VTK的过滤器和数据源采用按需执行的惰性求值(lazy evaluation)模型。
当你创建像 vtkRegularPolygonSource 这样的数据源时,它
实际上并不会立刻生成几何数据。它只是准备好在需要时进行计算
当你调用 source.GetOutput() 时,你拿到的是一个数据对象(data object),
但这个对象是空的或过时的,因为它还没有被任何下游过滤器或渲染器请求更新
SetInputData() vs. SetInputConnection()
SetInputConnection(source.GetOutputPort()):这是VTK中首选的连接管道的方式。
它连接的是数据源的输出端口,而不是数据本身。
当管道中的下游对象(例如 mapper)需要数据时,
它会向 shrink 过滤器请求,shrink 过滤器再向 source 请求,
这个请求会触发 source 的 Update() 方法,从而生成数据。这个过程是自动的。
SetInputData(source.GetOutput()):这个方法直接将一个已经存在的数据对象作为输入。
但问题在于,source.GetOutput() 返回的数据对象可能根本没有数据。
如果你不手动调用 source.Update(),数据源就不会执行任何计算,
导致 shrink 过滤器接收到的是一个空的数据集
"""
# shrink.SetInputData(source.GetOutput())
shrink.SetInputConnection(source.GetOutputPort())
shrink.SetShrinkFactor(0.9)
# shrink.Update()
mapper = vtkPolyDataMapper()
mapper.SetInputConnection(shrink.GetOutputPort())
back = vtkProperty()
back.SetColor(colors.GetColor3d('Tomato'))
actor = vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().EdgeVisibilityOn()
actor.GetProperty().SetLineWidth(5)
actor.GetProperty().SetColor(colors.GetColor3d('Banana'))
actor.SetBackfaceProperty(back)
renderer = vtkRenderer()
renderWindow = vtkRenderWindow()
renderWindow.SetWindowName('RegularPolygonSource')
renderWindow.AddRenderer(renderer)
renderWindowInteractor = vtkRenderWindowInteractor()
renderWindowInteractor.SetRenderWindow(renderWindow)
renderer.AddActor(actor)
renderer.SetBackground(colors.GetColor3d('Silver'))
renderWindow.Render()
renderWindowInteractor.Start()
if __name__ == '__main__':
main()
042:TessellatedBoxSource 创建并渲染一个细分的、可移动的箱体模型
#!/usr/bin/env python
# noinspection PyUnresolvedReferences
import vtkmodules.vtkInteractionStyle
# noinspection PyUnresolvedReferences
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonExecutionModel import vtkAlgorithm
from vtkmodules.vtkFiltersGeneral import vtkShrinkFilter
from vtkmodules.vtkFiltersSources import vtkTessellatedBoxSource
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkDataSetMapper,
vtkProperty,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkRenderer
)
def main():
colors = vtkNamedColors()
bounds = [-10.0, 10.0, 10.0, 20.0, -5.0, 5.0]
boxSource = vtkTessellatedBoxSource()
"""
设置箱体的细分级别。Level=3意味着箱体的每个维度(x, y, z)将被划分为 2的3次方=8,
总共创建 8×8×8=512 个小立方体
"""
boxSource.SetLevel(3)
boxSource.QuadsOn() # 使用四边形(而不是三角形)来构建箱体的面
boxSource.SetBounds(bounds)
"""
SetOutputPointsPrecision
设置输出点坐标的精度
"""
boxSource.SetOutputPointsPrecision(vtkAlgorithm.SINGLE_PRECISION)
shrink = vtkShrinkFilter()
shrink.SetInputConnection(boxSource.GetOutputPort())
shrink.SetShrinkFactor(.8)
# Create a mapper and actor.
mapper = vtkDataSetMapper()
mapper.SetInputConnection(shrink.GetOutputPort())
back = vtkProperty()
back.SetColor(colors.GetColor3d('Tomato'))
actor = vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().EdgeVisibilityOn()
actor.GetProperty().SetColor(colors.GetColor3d('Banana'))
actor.SetBackfaceProperty(back)
# Create a renderer, render window, and interactor.
renderer = vtkRenderer()
renderWindow = vtkRenderWindow()
renderWindow.AddRenderer(renderer)
renderWindowInteractor = vtkRenderWindowInteractor()
renderWindowInteractor.SetRenderWindow(renderWindow)
# Add the actors to the scene.
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.SetWindowName('TessellatedBoxSource')
renderWindow.Render()
renderWindowInteractor.Start()
if __name__ == '__main__':
main()
043:IsoparametricCellsDemo非线性单元类型展示
#!/usr/bin/env python3
# noinspection PyUnresolvedReferences
import vtkmodules.vtkInteractionStyle
# noinspection PyUnresolvedReferences
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonCore import (
VTK_VERSION_NUMBER,
vtkVersion
)
from vtkmodules.vtkCommonDataModel import (
vtkBiQuadraticQuad,
vtkBiQuadraticQuadraticHexahedron,
vtkBiQuadraticQuadraticWedge,
vtkBiQuadraticTriangle,
vtkCubicLine,
vtkQuadraticEdge,
vtkQuadraticHexahedron,
vtkQuadraticLinearQuad,
vtkQuadraticLinearWedge,
vtkQuadraticPolygon,
vtkQuadraticPyramid,
vtkQuadraticQuad,
vtkQuadraticTetra,
vtkQuadraticTriangle,
vtkQuadraticWedge,
vtkTriQuadraticHexahedron,
vtkUnstructuredGrid
)
# noinspection PyUnresolvedReferences
from vtkmodules.vtkCommonTransforms import vtkTransform
# noinspection PyUnresolvedReferences
from vtkmodules.vtkFiltersGeneral import vtkTransformFilter
from vtkmodules.vtkFiltersSources import (
vtkCubeSource,
vtkSphereSource
)
from vtkmodules.vtkInteractionWidgets import (
vtkCameraOrientationWidget,
vtkOrientationMarkerWidget
)
from vtkmodules.vtkRenderingAnnotation import vtkAxesActor
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkActor2D,
vtkDataSetMapper,
vtkGlyph3DMapper,
vtkLightKit,
vtkPolyDataMapper,
vtkProperty,
vtkRenderer,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkTextMapper,
vtkTextProperty
)
from vtkmodules.vtkRenderingLabel import vtkLabeledDataMapper
def get_program_parameters():
import argparse
description = 'Demonstrate the isoparametric cell types found in VTK.'
epilogue = '''
The numbers define the ordering of the points making the cell.
'''
parser = argparse.ArgumentParser(description=description, epilog=epilogue,
formatter_class=argparse.RawDescriptionHelpFormatter)
group1 = parser.add_mutually_exclusive_group()
group1.add_argument('-w', '--wireframe', action='store_true',
help='Render a wireframe.')
group1.add_argument('-b', '--backface', action='store_true',
help='Display the back face in a different colour.')
parser.add_argument('-o', '--object_number', type=int, default=None,
help='The number corresponding to the object.')
parser.add_argument('-n', '--no_plinth', action='store_true',
help='Remove the plinth.')
args = parser.parse_args()
return args.wireframe, args.backface, args.object_number, args.no_plinth
def main():
wireframe_on, backface_on, object_num, plinth_off = get_program_parameters()
objects = specify_objects()
# The order here should match the order in specify_objects().
object_order = list(objects.keys())
# Check for a single object.
single_object = None
if object_num:
if object_num in object_order:
single_object = True
else:
print('Object not found.\nPlease enter the number corresponding to the object.')
print('Available objects are:')
for obj in object_order:
print(f'{objects[obj]} (={str(obj)})')
return
colors = vtkNamedColors()
# Create one sphere for all.
sphere = vtkSphereSource()
sphere.SetPhiResolution(21)
sphere.SetThetaResolution(21)
sphere.SetRadius(0.04)
cells = get_unstructured_grids()
# The text to be displayed in the viewport.
names = list()
# The keys of the objects selected for display.
keys = list()
if single_object:
names.append(f'{objects[object_num]} (={str(object_num)})')
keys.append(object_num)
else:
for obj in object_order:
names.append(f'{objects[obj]} (={str(obj)})')
keys.append(obj)
add_plinth = (24, 25, 12, 26, 27, 29, 31, 32, 33)
lines = (21, 35)
# Set up the viewports.
grid_column_dimensions = 4
grid_row_dimensions = 4
renderer_size = 300
if single_object:
grid_column_dimensions = 1
grid_row_dimensions = 1
renderer_size = 1200
window_size = (grid_column_dimensions * renderer_size, grid_row_dimensions * renderer_size)
viewports = dict()
blank = len(cells)
blank_viewports = list()
for row in range(0, grid_row_dimensions):
for col in range(0, grid_column_dimensions):
index = row * grid_column_dimensions + col
# Set the renderer's viewport dimensions (xmin, ymin, xmax, ymax) within the render window.
# Note that for the Y values, we need to subtract the row index from grid_rows
# because the viewport Y axis points upwards, and we want to draw the grid from top to down.
viewport = (float(col) / grid_column_dimensions,
float(grid_row_dimensions - (row + 1)) / grid_row_dimensions,
float(col + 1) / grid_column_dimensions,
float(grid_row_dimensions - row) / grid_row_dimensions)
if index < blank:
viewports[keys[index]] = viewport
else:
s = f'vp_{col:d}_{row:d}'
viewports[s] = viewport
blank_viewports.append(s)
ren_win = vtkRenderWindow()
ren_win.SetSize(window_size)
ren_win.SetWindowName('IsoparametricCellsDemo')
iren = vtkRenderWindowInteractor()
iren.SetRenderWindow(ren_win)
# Since we always import vtkmodules.vtkInteractionStyle we can do this
# because vtkInteractorStyleSwitch is automatically imported:
iren.GetInteractorStyle().SetCurrentStyleToTrackballCamera()
renderers = dict()
# Create and link the mappers, actors and renderers together.
single_object_key = None
for idx, key in enumerate(keys):
print('Creating:', names[idx])
if single_object:
single_object_key = key
text_property = get_text_property()
if single_object:
text_property.SetFontSize(renderer_size // 28)
else:
text_property.SetFontSize(renderer_size // 24)
text_mapper = vtkTextMapper()
text_mapper.SetTextProperty(text_property)
text_mapper.SetInput(names[idx])
text_actor = vtkActor2D()
text_actor.SetMapper(text_mapper)
text_actor.SetPosition(renderer_size / 2.0, 8)
mapper = vtkDataSetMapper()
mapper.SetInputData(cells[key][0])
actor = vtkActor()
actor.SetMapper(mapper)
actor.SetProperty(get_actor_property())
if wireframe_on or key in lines:
actor.GetProperty().SetRepresentationToWireframe()
actor.GetProperty().SetLineWidth(2)
actor.GetProperty().SetOpacity(1)
actor.GetProperty().SetColor(colors.GetColor3d('Black'))
else:
if backface_on:
actor.SetBackfaceProperty(get_back_face_property())
# Label the points.
label_property = get_label_property()
if single_object:
label_property.SetFontSize(renderer_size // 36)
else:
label_property.SetFontSize(renderer_size // 16)
label_mapper = vtkLabeledDataMapper()
label_mapper.SetInputData(cells[key][0])
label_mapper.SetLabelTextProperty(label_property)
label_actor = vtkActor2D()
label_actor.SetMapper(label_mapper)
# Glyph the points.
point_mapper = vtkGlyph3DMapper()
point_mapper.SetInputData(cells[key][0])
point_mapper.SetSourceConnection(sphere.GetOutputPort())
point_mapper.ScalingOn()
point_mapper.ScalarVisibilityOff()
point_actor = vtkActor()
point_actor.SetMapper(point_mapper)
point_actor.SetProperty(get_point_actor_property())
renderer = vtkRenderer()
renderer.SetBackground(colors.GetColor3d('LightSteelBlue'))
renderer.SetViewport(viewports[key])
light_kit = vtkLightKit()
light_kit.AddLightsToRenderer(renderer)
renderer.AddActor(text_actor)
renderer.AddActor(actor)
renderer.AddActor(label_actor)
renderer.AddActor(point_actor)
if not plinth_off:
# Add a plinth.
if key in add_plinth:
tile_actor = make_tile(cells[key][0].GetBounds(),
expansion_factor=0.5, thickness_ratio=0.01, shift_y=-0.05)
tile_actor.SetProperty(get_tile_property())
renderer.AddActor(tile_actor)
renderer.ResetCamera()
renderer.GetActiveCamera().Azimuth(cells[key][1])
renderer.GetActiveCamera().Elevation(cells[key][2])
renderer.GetActiveCamera().Dolly(cells[key][3])
renderer.ResetCameraClippingRange()
renderers[key] = renderer
ren_win.AddRenderer(renderers[key])
for name in blank_viewports:
viewport = viewports[name]
renderer = vtkRenderer()
renderer.SetBackground = colors.GetColor3d('LightSteelBlue')
renderer.SetViewport(viewport)
renderers[name] = renderer
ren_win.AddRenderer(renderers[name])
if single_object:
if vtk_version_ok(9, 0, 20210718):
try:
cam_orient_manipulator = vtkCameraOrientationWidget()
cam_orient_manipulator.SetParentRenderer(renderers[single_object_key])
cam_orient_manipulator.SetInteractor(iren)
# Enable the widget.
cam_orient_manipulator.On()
except AttributeError:
pass
else:
axes = vtkAxesActor()
widget = vtkOrientationMarkerWidget()
rgba = [0.0, 0.0, 0.0, 0.0]
colors.GetColor("Carrot", rgba)
widget.SetOutlineColor(rgba[0], rgba[1], rgba[2])
widget.SetOrientationMarker(axes)
widget.SetInteractor(iren)
widget.SetViewport(0.0, 0.0, 0.2, 0.2)
widget.EnabledOn()
widget.InteractiveOn()
ren_win.Render()
iren.Initialize()
iren.Start()
def specify_objects():
"""
Link the unstructured grid number to the unstructured grid name.
:return: A dictionary: {index number: unstructured grid name}.
"""
objects = {
21: 'VTK_QUADRATIC_EDGE',
22: 'VTK_QUADRATIC_TRIANGLE',
23: 'VTK_QUADRATIC_QUAD',
36: 'VTK_QUADRATIC_POLYGON',
24: 'VTK_QUADRATIC_TETRA',
25: 'VTK_QUADRATIC_HEXAHEDRON',
26: 'VTK_QUADRATIC_WEDGE',
27: 'VTK_QUADRATIC_PYRAMID',
28: 'VTK_BIQUADRATIC_QUAD',
29: 'VTK_TRIQUADRATIC_HEXAHEDRON',
30: 'VTK_QUADRATIC_LINEAR_QUAD',
31: 'VTK_QUADRATIC_LINEAR_WEDGE',
32: 'VTK_BIQUADRATIC_QUADRATIC_WEDGE',
33: 'VTK_BIQUADRATIC_QUADRATIC_HEXAHEDRON',
34: 'VTK_BIQUADRATIC_TRIANGLE',
35: 'VTK_CUBIC_LINE',
}
return objects
def get_unstructured_grids():
"""
Get the unstructured grid names, the unstructured grid and initial orientations.
Get the unstructured grid names, the unstructured grid and initial orientations.
:return: A dictionary: {index number: (unstructured grid, azimuth, elevation and dolly)}.
"""
return {
21: (make_ug(vtkQuadraticEdge()), 0, 0, 0.8),
22: (make_ug(vtkQuadraticTriangle()), 0, 0, 0),
23: (make_ug(vtkQuadraticQuad()), 0, 0, 0),
36: (make_quadratic_polygon(), 0, 0, 0),
24: (make_ug(vtkQuadraticTetra()), 20, 20, 1.0),
25: (make_ug(vtkQuadraticHexahedron()), -30, 12, 0.95),
26: (make_ug(vtkQuadraticWedge()), 45, 15, 1.0),
27: (make_quadratic_pyramid(), -110, 8, 1.0),
28: (make_ug(vtkBiQuadraticQuad()), 0, 0, 0),
29: (make_ug(vtkTriQuadraticHexahedron()), -15, 15, 0.95),
30: (make_ug(vtkQuadraticLinearQuad()), 0, 0, 0),
31: (make_ug(vtkQuadraticLinearWedge()), 60, 22.5, 1.0),
32: (make_ug(vtkBiQuadraticQuadraticWedge()), 70, 22.5, 1.0),
33: (make_ug(vtkBiQuadraticQuadraticHexahedron()), -15, 15, 0.95),
34: (make_ug(vtkBiQuadraticTriangle()), 0, 0, 0),
35: (make_ug(vtkCubicLine()), 0, 0, 0.85),
}
# These functions return a vtkUnstructured grid corresponding to the object.
def make_ug(cell):
pcoords = cell.GetParametricCoords()
for i in range(0, cell.number_of_points):
cell.point_ids.SetId(i, i)
cell.points.SetPoint(i, (pcoords[3 * i]), (pcoords[3 * i + 1]), (pcoords[3 * i + 2]))
ug = vtkUnstructuredGrid()
ug.SetPoints(cell.GetPoints())
ug.InsertNextCell(cell.cell_type, cell.point_ids)
return ug
def make_quadratic_polygon():
number_of_vertices = 8
quadratic_polygon = vtkQuadraticPolygon()
quadratic_polygon.points.SetNumberOfPoints(8)
quadratic_polygon.points.SetPoint(0, 0.0, 0.0, 0.0)
quadratic_polygon.points.SetPoint(1, 2.0, 0.0, 0.0)
quadratic_polygon.points.SetPoint(2, 2.0, 2.0, 0.0)
quadratic_polygon.points.SetPoint(3, 0.0, 2.0, 0.0)
quadratic_polygon.points.SetPoint(4, 1.0, 0.0, 0.0)
quadratic_polygon.points.SetPoint(5, 2.0, 1.0, 0.0)
quadratic_polygon.points.SetPoint(6, 1.0, 2.0, 0.0)
quadratic_polygon.points.SetPoint(7, 0.0, 1.0, 0.0)
quadratic_polygon.points.SetPoint(5, 3.0, 1.0, 0.0)
quadratic_polygon.point_ids.SetNumberOfIds(number_of_vertices)
for i in range(0, number_of_vertices):
quadratic_polygon.point_ids.SetId(i, i)
ug = vtkUnstructuredGrid(points=quadratic_polygon.points)
ug.SetPoints(quadratic_polygon.GetPoints())
ug.InsertNextCell(quadratic_polygon.cell_type, quadratic_polygon.point_ids)
return ug
def make_quadratic_pyramid():
cell = vtkQuadraticPyramid()
pcoords = cell.GetParametricCoords()
for i in range(0, cell.number_of_points):
cell.point_ids.SetId(i, i)
cell.points.SetPoint(i, (pcoords[3 * i]), (pcoords[3 * i + 1]), (pcoords[3 * i + 2]))
ug = vtkUnstructuredGrid(points=cell.points)
ug.SetPoints(cell.GetPoints())
ug.InsertNextCell(cell.cell_type, cell.point_ids)
t = vtkTransform()
t.RotateX(-90)
t.Translate(0, 0, 0)
tf = vtkTransformFilter()
tf.SetTransform(t)
tf.SetInputData(ug)
tf.Update()
# Put the transformed points back.
ug.SetPoints(tf.GetOutput().GetPoints())
return ug
def make_tile(bounds, expansion_factor=0.5, thickness_ratio=0.05, shift_y=-0.05):
"""
Make a tile slightly larger or smaller than the bounds in the
X and Z directions and thinner or thicker in the Y direction.
A thickness_ratio of zero reduces the tile to an XZ plane.
:param bounds: The bounds for the tile.
:param expansion_factor: The expansion factor in the XZ plane.
:param thickness_ratio: The thickness ratio in the Y direction, >= 0.
:param shift_y: Used to shift the centre of the plinth in the Y-direction.
:return: An actor corresponding to the tile.
"""
d_xyz = (
bounds[1] - bounds[0],
bounds[3] - bounds[2],
bounds[5] - bounds[4]
)
thickness = d_xyz[2] * abs(thickness_ratio)
center = ((bounds[1] + bounds[0]) / 2.0,
bounds[2] - thickness / 2.0 + shift_y,
(bounds[5] + bounds[4]) / 2.0)
x_length = bounds[1] - bounds[0] + (d_xyz[0] * expansion_factor)
z_length = bounds[5] - bounds[4] + (d_xyz[2] * expansion_factor)
plane = vtkCubeSource()
plane.SetCenter(center)
plane.SetXLength(x_length)
plane.SetYLength(thickness)
plane.SetZLength(z_length)
plane_mapper = vtkPolyDataMapper()
plane_mapper.SetInputConnection(plane.GetOutputPort())
tile_actor = vtkActor()
tile_actor.SetMapper(plane_mapper)
return tile_actor
def get_text_property():
colors = vtkNamedColors()
pty = vtkTextProperty()
pty.BoldOn()
pty.SetJustificationToCentered()
pty.SetColor(colors.GetColor3d('Black'))
return pty
def get_label_property():
colors = vtkNamedColors()
pty = vtkTextProperty()
pty.BoldOn()
pty.ShadowOn()
pty.SetJustificationToCentered()
pty.SetColor(colors.GetColor3d('DeepPink'))
return pty
def get_back_face_property():
colors = vtkNamedColors()
pty = vtkProperty()
pty.SetAmbientColor(colors.GetColor3d('LightSalmon'))
pty.SetDiffuseColor(colors.GetColor3d('OrangeRed'))
pty.SetSpecularColor(colors.GetColor3d('White'))
pty.SetSpecular(0.2)
pty.SetDiffuse(1.0)
pty.SetAmbient(0.2)
pty.SetSpecularPower(20.0)
pty.SetOpacity(1.0)
return pty
def get_actor_property():
colors = vtkNamedColors()
pty = vtkProperty()
pty.SetAmbientColor(colors.GetColor3d('DarkSalmon'))
pty.SetDiffuseColor(colors.GetColor3d('Seashell'))
pty.SetSpecularColor(colors.GetColor3d('White'))
pty.SetSpecular(0.5)
pty.SetDiffuse(0.7)
pty.SetAmbient(0.5)
pty.SetSpecularPower(20.0)
pty.SetOpacity(0.9)
pty.EdgeVisibilityOn()
pty.SetLineWidth(3)
return pty
def get_point_actor_property():
colors = vtkNamedColors()
pty = vtkProperty()
pty.SetAmbientColor(colors.GetColor3d('Gold'))
pty.SetDiffuseColor(colors.GetColor3d('Yellow'))
pty.SetSpecularColor(colors.GetColor3d('White'))
pty.SetSpecular(0.5)
pty.SetDiffuse(0.7)
pty.SetAmbient(0.5)
pty.SetSpecularPower(20.0)
pty.SetOpacity(1.0)
return pty
def get_tile_property():
colors = vtkNamedColors()
pty = vtkProperty()
pty.SetAmbientColor(colors.GetColor3d('SteelBlue'))
pty.SetDiffuseColor(colors.GetColor3d('LightSteelBlue'))
pty.SetSpecularColor(colors.GetColor3d('White'))
pty.SetSpecular(0.5)
pty.SetDiffuse(0.7)
pty.SetAmbient(0.5)
pty.SetSpecularPower(20.0)
pty.SetOpacity(0.8)
pty.EdgeVisibilityOn()
pty.SetLineWidth(1)
return pty
def vtk_version_ok(major, minor, build):
"""
Check the VTK version.
:param major: Major version.
:param minor: Minor version.
:param build: Build version.
:return: True if the requested VTK version is greater or equal to the actual VTK version.
"""
needed_version = 10000000000 * int(major) + 100000000 * int(minor) + int(build)
try:
vtk_version_number = VTK_VERSION_NUMBER
except AttributeError: # as error:
ver = vtkVersion()
vtk_version_number = 10000000000 * ver.GetVTKMajorVersion() + 100000000 * ver.GetVTKMinorVersion() \
+ ver.GetVTKBuildVersion()
if vtk_version_number >= needed_version:
return True
else:
return False
if __name__ == '__main__':
main()
044:QuadraticHexahedronDemo 非线性单元的展示与窗口交互
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonCore import vtkCommand, vtkPoints, vtkMinimalStandardRandomSequence
from vtkmodules.vtkCommonDataModel import vtkGenericCell, vtkQuadraticHexahedron, vtkUnstructuredGrid
from vtkmodules.vtkFiltersCore import vtkGlyph3D
from vtkmodules.vtkFiltersGeneral import vtkTessellatorFilter
from vtkmodules.vtkFiltersSources import vtkSphereSource
from vtkmodules.vtkInteractionWidgets import (
vtkSliderRepresentation2D,
vtkSliderWidget
)
from vtkmodules.vtkRenderingCore import vtkActor, vtkActor2D, vtkDataSetMapper, vtkRenderWindow, \
vtkRenderWindowInteractor, vtkRenderer, vtkTextMapper, vtkTextProperty
class SliderCallbackChordError():
def __init__(self, tessellate, textMapper):
self.tessellate = tessellate
self.textMapper = textMapper
def __call__(self, caller, ev):
"""
:param caller: 触发的对象,这里就是slider widget
:param ev: 触发事件 一个整数常量,表示触发的事件类型
VTK 内部用枚举 vtkCommand.EventIds 定义了常见事件,例如:
vtkCommand.InteractionEvent
vtkCommand.StartInteractionEvent
vtkCommand.EndInteractionEvent
vtkCommand.MouseMoveEvent
vtkCommand.KeyPressEvent
目前代码只监听了 InteractionEvent。
所以 ev 每次都会是 vtkCommand.InteractionEvent(对应的整数 ID)。
虽然你代码里没用 ev,但如果你监听多个事件,就能通过 ev 区分:
如果想监听多个事件,就需要在sliderWidget中添加多个监听事件
"""
sliderWidget = caller
value = sliderWidget.GetRepresentation().GetValue()
"""
SetChordError 和 SetMaximumNumberOfSubdivisions
两个方法都用于控制细分(tessellation)过程,但它们从不同的角度来限制细分,通常是共同作用的
它们之间的区别在于:
SetChordError() 是基于几何精确度的限制。
SetMaximumNumberOfSubdivisions() 是基于迭代次数的限制。
SetChordError(value)
这个方法关注的是细分后曲面与原始曲面之间的最大允许误差。
工作方式: 过滤器会不断地细分弯曲的单元格,直到细分后的小平面多边形与原始曲面之间的**弦误差(chord error)**小于你设置的 value。
优点: 它能确保最终渲染的模型达到你所需的视觉精确度。
缺点: 如果原始曲面非常复杂或有很多尖锐的弯曲,为了达到这个误差阈值,过滤器可能会进行无限次的细分,导致性能问题或程序崩溃。
SetMaximumNumberOfSubdivisions()
这个方法关注的是细分的最大迭代次数。
工作方式: 过滤器在细分时,会计算每个单元格需要细分的次数。但无论需要多少次,它都不会超过你设置的 n 次。
优点: 这是一个安全网。它能防止细分过程因为 SetChordError() 的要求而变得无限或过于昂贵,从而保证程序的性能。
缺点: 如果 n 设置得太小,即使 SetChordError() 要求更高的精度,细分也会提前停止,导致最终模型不够平滑。
"""
self.tessellate.SetChordError(value)
self.tessellate.SetMaximumNumberOfSubdivisions(4)
self.tessellate.Update()
cellMap = dict()
numTets = 0
cell = vtkGenericCell()
it = self.tessellate.GetOutput().NewCellIterator()
it.InitTraversal()
while not it.IsDoneWithTraversal():
it.GetCell(cell)
cellMap[cell.GetRepresentativeCell().GetClassName()] = numTets
numTets += 1
it.GoToNextCell()
ss = '# of Tetras: ' + str(numTets)
self.textMapper.SetInput(ss)
def main():
colors = vtkNamedColors()
# 创建一个grid
aHexahedron = vtkQuadraticHexahedron()
points = vtkPoints()
pcoords = aHexahedron.GetParametricCoords()
rng = vtkMinimalStandardRandomSequence()
rng.SetSeed(5070)
points.SetNumberOfPoints(aHexahedron.GetNumberOfPoints())
for i in range(0, aHexahedron.GetNumberOfPoints()):
perturbation = [0] * 3
for j in range(3):
rng.Next()
perturbation[j] = rng.GetRangeValue(-0.1, 0.1)
aHexahedron.GetPointIds().SetId(i, i)
points.SetPoint(i, pcoords[3 * i] + perturbation[0],
pcoords[3 * i + 1] + perturbation[1],
pcoords[3 * i + 2] + perturbation[2])
ug = vtkUnstructuredGrid()
ug.SetPoints(points)
ug.InsertNextCell(aHexahedron.GetCellType(), aHexahedron.GetPointIds())
tessellate = vtkTessellatorFilter()
tessellate.SetInputData(ug)
"""
SetChordError 控制曲面细分(tessellation)的精确度
较小的值:会导致更多的细分,生成更多的小多边形
较大的值:更少的细分
"""
tessellate.SetChordError(0.035)
tessellate.Update()
# 统计经过细分过后的网格面情况
cellMap = {}
numTests = 0
"""
vtkGenericCell 是一个通用容器,能装的了任何类型的单元
遍历网格时,你需要一个“临时盒子”来装当前单元,它就是这个作用
"""
cell = vtkGenericCell()
"""
NewCellIterator 返回一个单元迭代器
"""
it = tessellate.GetOutput().NewCellIterator()
"""
InitTraversal 把迭代器重置到第一个单元
"""
it.InitTraversal()
while not it.IsDoneWithTraversal():
it.GetCell(cell) # 把当前单元拷贝到vtkGenericCell
# 单元类型当作 key,存储当前单元的编号
cellMap[cell.GetRepresentativeCell().GetClassName()] = numTests
numTests += 1
it.GoToNextCell()
mapper = vtkDataSetMapper()
mapper.SetInputConnection(tessellate.GetOutputPort())
"""
ScalarVisibilityOff 关闭标量数据的可见性
"""
mapper.ScalarVisibilityOff()
actor = vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetDiffuseColor( # 设置漫反射颜色为番茄红
colors.GetColor3d('Tomato'))
actor.GetProperty().SetEdgeColor( # 设置边线颜色为象牙黑
colors.GetColor3d('IvoryBlack'))
actor.GetProperty().EdgeVisibilityOn() # 打开边线的可见性
# 显示图形上的关键顶点
sphereSource = vtkSphereSource()
sphereSource.SetRadius(0.02)
glyph3d = vtkGlyph3D()
glyph3d.SetInputData(ug)
glyph3d.SetSourceConnection(sphereSource.GetOutputPort())
"""
ScalingOff 关闭三维字形(glyph)的缩放功能
在默认情况下,vtkGlyph3DMapper 会根据每个点的标量数据来改变字形的大小。
如果一个点的标量值很大,那么在这个点上绘制的字形就会更大;
如果标量值很小,字形就会更小。这种功能非常有用,可以用来可视化数据的分布,
例如显示网格上不同点的压力或温度大小
"""
glyph3d.ScalingOff()
glyph3d.Update()
glyph3DMapper = vtkDataSetMapper()
glyph3DMapper.SetInputConnection(glyph3d.GetOutputPort())
glyph3DMapper.ScalarVisibilityOff()
glyph3DActor = vtkActor()
glyph3DActor.SetMapper(glyph3DMapper)
glyph3DActor.GetProperty().SetColor(colors.GetColor3d('Banana'))
textProperty = vtkTextProperty()
textProperty.SetFontSize(24)
ss = '# of Tetras: ' + str(numTests)
textMapper = vtkTextMapper()
textMapper.SetInput(ss)
textMapper.SetTextProperty(textProperty)
textActor = vtkActor2D()
textActor.SetMapper(textMapper)
textActor.SetPosition(10, 400)
renderer = vtkRenderer()
renderWindow = vtkRenderWindow()
renderWindow.SetWindowName("QuadraticHexahedronDemo")
renderWindow.AddRenderer(renderer)
renderWindow.SetSize(640, 512)
interactor = vtkRenderWindowInteractor()
interactor.SetRenderWindow(renderWindow)
# 设定滑块
"""
vtkSliderWidget 是 滑块交互控件,它和 vtkSliderRepresentation2D/3D 搭配使用,
用来在渲染窗口里添加一个可拖动的滑块(slider),并且可以通过回调函数与应用逻辑交互
vtkSliderRepresentation2D/3D 定义滑块的外观和布局(轨道、端点、滑块大小、数值范围等)。
vtkSliderWidget 控制交互逻辑,处理鼠标拖动、事件分发,并绑定回
"""
widget = vtkSliderWidget()
sliderRepChordError = vtkSliderRepresentation2D()
sliderRepChordError.SetMinimumValue(0.0)
sliderRepChordError.SetMaximumValue(0.07)
sliderRepChordError.SetValue(tessellate.GetChordError())
sliderRepChordError.SetTitleText("Chord error")
# 设置滑块部件的具体位置
"""
下面两行SetCoordinateSystemToNormalizedDisplay代码
作用是将滑块的两个端点设置为归一化显示坐标系
在这个坐标系中,渲染窗口的左下角是 (0,0),右上角是 (1,1)。这种坐标系与窗口大小无关,
使得界面元素可以随着窗口的缩放而自动调整位置。
"""
sliderRepChordError.GetPoint1Coordinate().SetCoordinateSystemToNormalizedDisplay()
sliderRepChordError.GetPoint2Coordinate().SetCoordinateSystemToNormalizedDisplay()
sliderRepChordError.GetPoint1Coordinate().SetValue(0.1, 0.1)
sliderRepChordError.GetPoint2Coordinate().SetValue(0.9, 0.1)
tubeWidth = 0.008
sliderLength = 0.008
titleHeight = 0.04
labelHeight = 0.04
"""
SetTubeWidth 设置轨道的宽度,也是一个归一化大小 这些值都是相对于滑块的长度
SetSliderLength() 的参数虽然是归一化比例(0–1),但是 它的“参照物”不是整个 slider 的管道长度
而是 slider representation 内部定义的归一化范围
在 VTK 的实现里:
SetSliderLength(x) 控制的是 滑块手柄沿管道方向所占的比例。
这个比例再乘上 representation 里定义的 有效范围 (end points – tube 两端的 margin),
才是最终手柄能占的长度。
换句话说,即使你设成 1.0,也不会真的覆盖整个 slider,
因为 VTK 在两端给手柄留了边界(手柄中心不能超过 slider 的 min/max 端点)
"""
sliderRepChordError.SetTubeWidth(tubeWidth)
# SetSliderLength 设置滑块手柄的长度,是用户可以拖动的部分
sliderRepChordError.SetSliderLength(sliderLength)
# SetTitleHeight 设置滑块标题的高度,是字体的高度,而非字体在窗口位置的高度 位置通常在滑块上方
sliderRepChordError.SetLabelHeight(titleHeight)
# SetLabelHeight 设置滑块标签的高度,通常用来显示当前滑块的值,
# 设置的是字体的高度,而非字体在窗口位置的高度
# 默认在滑块的下方
sliderRepChordError.SetLabelHeight(labelHeight)
"""
SetInteractor 把 slider widget 绑定到某个交互器(vtkRenderWindowInteractor)
没有这一步,widget 不会接收鼠标事件(点不到、拖不动)
相当于告诉 VTK:“这个滑块要监听这个窗口的交互事件。”
"""
widget.SetInteractor(interactor)
"""
指定这个 widget 的“外观表示”(representation)
"""
widget.SetRepresentation(sliderRepChordError)
"""
设置交互模式为“动画更新”
VTK 里有两种常见模式:
Animate:拖动过程中,滑块值会 连续更新,实时触发回调(适合做实时可视化)。
Jump:拖动结束时(鼠标松开),才更新数值。
"""
widget.SetAnimationModeToAnimate()
"""
启用 widget
默认 widget 是禁用状态,调用 EnabledOn() 之后它才会真正出现并响应事件
也可以用 widget.EnabledOff() 来隐藏/禁用它
"""
widget.EnabledOn()
"""
为滑块部件添加一个事件监听器
当用户与滑块进行交互(interaction)时,这个监听器就会被触发,
然后执行一个特定的函数(SliderCallbackChordError),从而实现模型的动态更新
参数:event 时间类型
这个参数指定了你想要监听的事件类
vtkCommand.InteractionEvent 表示任何用户与部件(如滑块)进行的交互,比如拖动滑块手柄
参数:command 回调函数
当事件发生时,VTK会调用这个回调函数
SliderCallbackChordError 是一个自定义的类或函数,它包含了你想要执行的逻辑
"""
widget.AddObserver(vtkCommand.InteractionEvent, SliderCallbackChordError(tessellate, textMapper))
# 若想多个事件的监听,则继续添加Observer
renderer.AddActor(actor)
renderer.AddActor(glyph3DActor)
renderer.AddViewProp(textActor)
renderer.SetBackground(colors.GetColor3d('SlateGray'))
renderWindow.Render()
interactor.Start()
if __name__ == '__main__':
main()
045:ParametricKuenDemo交互式的库恩曲面
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# noinspection PyUnresolvedReferences
import vtkmodules.vtkInteractionStyle
# noinspection PyUnresolvedReferences
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonComputationalGeometry import vtkParametricKuen
from vtkmodules.vtkCommonCore import (
vtkCommand,
vtkMath
)
from vtkmodules.vtkFiltersSources import vtkParametricFunctionSource
from vtkmodules.vtkInteractionWidgets import (
vtkSliderRepresentation2D,
vtkSliderWidget
)
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkPolyDataMapper,
vtkProperty,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkRenderer
)
def main():
colors = vtkNamedColors()
colors.SetColor('BkgColor', [26, 51, 102, 255])
surface = vtkParametricKuen()
source = vtkParametricFunctionSource()
renderer = vtkRenderer()
mapper = vtkPolyDataMapper()
actor = vtkActor()
backProperty = vtkProperty()
backProperty.SetColor(colors.GetColor3d('Tomato'))
# Create a parametric function source, renderer, mapper, and actor
source.SetParametricFunction(surface)
mapper.SetInputConnection(source.GetOutputPort())
actor.SetMapper(mapper)
actor.SetBackfaceProperty(backProperty)
actor.GetProperty().SetDiffuseColor(colors.GetColor3d('Banana'))
actor.GetProperty().SetSpecular(.5)
actor.GetProperty().SetSpecularPower(20)
renderWindow = vtkRenderWindow()
renderWindow.SetWindowName('ParametricKuenDemo')
renderWindow.AddRenderer(renderer)
renderWindow.SetSize(640, 480)
renderer.AddActor(actor)
renderer.SetBackground(colors.GetColor3d('BkgColor'))
renderer.ResetCamera()
renderer.GetActiveCamera().Azimuth(30)
renderer.GetActiveCamera().Elevation(-30)
renderer.GetActiveCamera().Zoom(0.9)
renderer.ResetCameraClippingRange()
interactor = vtkRenderWindowInteractor()
interactor.SetRenderWindow(renderWindow)
# Setup a slider widget for each varying parameter
tubeWidth = 0.008
sliderLength = 0.008
titleHeight = 0.02
labelHeight = 0.02
sliderRepMinimumU = vtkSliderRepresentation2D()
sliderRepMinimumU.SetMinimumValue(-4.5)
sliderRepMinimumU.SetMaximumValue(4.5)
sliderRepMinimumU.SetValue(-4.5)
sliderRepMinimumU.SetTitleText('U min')
sliderRepMinimumU.GetPoint1Coordinate().SetCoordinateSystemToNormalizedDisplay()
sliderRepMinimumU.GetPoint1Coordinate().SetValue(.1, .1)
sliderRepMinimumU.GetPoint2Coordinate().SetCoordinateSystemToNormalizedDisplay()
sliderRepMinimumU.GetPoint2Coordinate().SetValue(.9, .1)
sliderRepMinimumU.SetTubeWidth(tubeWidth)
sliderRepMinimumU.SetSliderLength(sliderLength)
sliderRepMinimumU.SetTitleHeight(titleHeight)
sliderRepMinimumU.SetLabelHeight(labelHeight)
sliderWidgetMinimumU = vtkSliderWidget()
sliderWidgetMinimumU.SetInteractor(interactor)
sliderWidgetMinimumU.SetRepresentation(sliderRepMinimumU)
sliderWidgetMinimumU.SetAnimationModeToAnimate()
sliderWidgetMinimumU.EnabledOn()
sliderWidgetMinimumU.AddObserver('InteractionEvent', SliderCallbackMinimumU(surface))
sliderRepMaximumU = vtkSliderRepresentation2D()
sliderRepMaximumU.SetMinimumValue(-4.5)
sliderRepMaximumU.SetMaximumValue(4.5)
sliderRepMaximumU.SetValue(4.5)
sliderRepMaximumU.SetTitleText('U max')
sliderRepMaximumU.GetPoint1Coordinate().SetCoordinateSystemToNormalizedDisplay()
sliderRepMaximumU.GetPoint1Coordinate().SetValue(.1, .9)
sliderRepMaximumU.GetPoint2Coordinate().SetCoordinateSystemToNormalizedDisplay()
sliderRepMaximumU.GetPoint2Coordinate().SetValue(.9, .9)
sliderRepMaximumU.SetTubeWidth(tubeWidth)
sliderRepMaximumU.SetSliderLength(sliderLength)
sliderRepMaximumU.SetTitleHeight(titleHeight)
sliderRepMaximumU.SetLabelHeight(labelHeight)
sliderWidgetMaximumU = vtkSliderWidget()
sliderWidgetMaximumU.SetInteractor(interactor)
sliderWidgetMaximumU.SetRepresentation(sliderRepMaximumU)
sliderWidgetMaximumU.SetAnimationModeToAnimate()
sliderWidgetMaximumU.EnabledOn()
sliderWidgetMaximumU.AddObserver('InteractionEvent', SliderCallbackMaximumU(surface))
sliderRepMinimumV = vtkSliderRepresentation2D()
sliderRepMinimumV.SetMinimumValue(0.05)
sliderRepMinimumV.SetMaximumValue(vtkMath.Pi())
sliderRepMinimumV.SetValue(0.0)
sliderRepMinimumV.SetTitleText('V min')
sliderRepMinimumV.GetPoint1Coordinate().SetCoordinateSystemToNormalizedDisplay()
sliderRepMinimumV.GetPoint1Coordinate().SetValue(.1, .1)
sliderRepMinimumV.GetPoint2Coordinate().SetCoordinateSystemToNormalizedDisplay()
sliderRepMinimumV.GetPoint2Coordinate().SetValue(.1, .9)
sliderRepMinimumV.SetTubeWidth(tubeWidth)
sliderRepMinimumV.SetSliderLength(sliderLength)
sliderRepMinimumV.SetTitleHeight(titleHeight)
sliderRepMinimumV.SetLabelHeight(labelHeight)
sliderWidgetMinimumV = vtkSliderWidget()
sliderWidgetMinimumV.SetInteractor(interactor)
sliderWidgetMinimumV.SetRepresentation(sliderRepMinimumV)
sliderWidgetMinimumV.SetAnimationModeToAnimate()
sliderWidgetMinimumV.EnabledOn()
sliderWidgetMinimumV.AddObserver(vtkCommand.InteractionEvent, SliderCallbackMinimumV(surface))
sliderRepMaximumV = vtkSliderRepresentation2D()
sliderRepMaximumV.SetMinimumValue(0.05)
sliderRepMaximumV.SetMaximumValue(vtkMath.Pi() - .05)
sliderRepMaximumV.SetValue(vtkMath.Pi())
sliderRepMaximumV.SetTitleText('V max')
sliderRepMaximumV.GetPoint1Coordinate().SetCoordinateSystemToNormalizedDisplay()
sliderRepMaximumV.GetPoint1Coordinate().SetValue(.9, .1)
sliderRepMaximumV.GetPoint2Coordinate().SetCoordinateSystemToNormalizedDisplay()
sliderRepMaximumV.GetPoint2Coordinate().SetValue(.9, .9)
sliderRepMaximumV.SetTubeWidth(tubeWidth)
sliderRepMaximumV.SetSliderLength(sliderLength)
sliderRepMaximumV.SetTitleHeight(titleHeight)
sliderRepMaximumV.SetLabelHeight(labelHeight)
sliderWidgetMaximumV = vtkSliderWidget()
sliderWidgetMaximumV.SetInteractor(interactor)
sliderWidgetMaximumV.SetRepresentation(sliderRepMaximumV)
sliderWidgetMaximumV.SetAnimationModeToAnimate()
sliderWidgetMaximumV.EnabledOn()
sliderWidgetMaximumV.AddObserver(vtkCommand.InteractionEvent, SliderCallbackMaximumV(surface))
surface.SetMinimumU(-4.5)
surface.SetMaximumU(4.5)
surface.SetMinimumV(0.05)
surface.SetMaximumV(vtkMath.Pi() - .05)
renderer.ResetCamera()
renderer.GetActiveCamera().Azimuth(30)
renderer.GetActiveCamera().Elevation(-30)
renderer.GetActiveCamera().Zoom(0.9)
renderer.ResetCameraClippingRange()
renderWindow.Render()
interactor.Initialize()
interactor.Start()
# These callbacks do the actual work.
# Callbacks for the interactions
class SliderCallbackMinimumU():
def __init__(self, kuen):
self.kuen = kuen
def __call__(self, caller, ev):
sliderWidget = caller
value = sliderWidget.GetRepresentation().GetValue()
if value > 0.9 * self.kuen.GetMaximumU():
value = 0.99 * self.kuen.GetMaximumU()
sliderWidget.GetRepresentation().SetValue(value)
self.kuen.SetMinimumU(value)
class SliderCallbackMaximumU():
def __init__(self, kuen):
self.kuen = kuen
def __call__(self, caller, ev):
sliderWidget = caller
value = sliderWidget.GetRepresentation().GetValue()
if value < self.kuen.GetMinimumU() + .01:
value = self.kuen.GetMinimumU() + .01
sliderWidget.GetRepresentation().SetValue(value)
self.kuen.SetMaximumU(value)
class SliderCallbackMinimumV():
def __init__(self, kuen):
self.kuen = kuen
def __call__(self, caller, ev):
sliderWidget = caller
value = sliderWidget.GetRepresentation().GetValue()
if value > 0.9 * self.kuen.GetMaximumV():
value = 0.99 * self.kuen.GetMaximumV()
sliderWidget.GetRepresentation().SetValue(value)
self.kuen.SetMinimumV(value)
class SliderCallbackMaximumV():
def __init__(self, kuen):
self.kuen = kuen
def __call__(self, caller, ev):
sliderWidget = caller
value = sliderWidget.GetRepresentation().GetValue()
if value < self.kuen.GetMinimumV() + .01:
value = self.kuen.GetMinimumV() + .01
sliderWidget.GetRepresentation().SetValue(value)
self.kuen.SetMaximumV(value)
if __name__ == '__main__':
main()
046:ParametricObjectsDemo多种参数曲面展示及面上部分点法线展示
#!/usr/bin/env python3
"""
Demonstrate all the parametric objects.
"""
from pathlib import Path
# noinspection PyUnresolvedReferences
import vtkmodules.vtkInteractionStyle
# noinspection PyUnresolvedReferences
import vtkmodules.vtkRenderingFreeType
# noinspection PyUnresolvedReferences
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonComputationalGeometry import (
vtkParametricBohemianDome,
vtkParametricBour,
vtkParametricBoy,
vtkParametricCatalanMinimal,
vtkParametricConicSpiral,
vtkParametricCrossCap,
vtkParametricDini,
vtkParametricEllipsoid,
vtkParametricEnneper,
vtkParametricFigure8Klein,
vtkParametricHenneberg,
vtkParametricKlein,
vtkParametricKuen,
vtkParametricMobius,
vtkParametricPluckerConoid,
vtkParametricPseudosphere,
vtkParametricRandomHills,
vtkParametricRoman,
vtkParametricSpline,
vtkParametricSuperEllipsoid,
vtkParametricSuperToroid,
vtkParametricTorus
)
from vtkmodules.vtkCommonCore import (
vtkMinimalStandardRandomSequence,
vtkPoints
)
from vtkmodules.vtkFiltersCore import (
vtkGlyph3D,
vtkMaskPoints
)
from vtkmodules.vtkFiltersSources import (
vtkArrowSource,
vtkParametricFunctionSource
)
from vtkmodules.vtkIOImage import (
vtkPNGWriter
)
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkActor2D,
vtkPolyDataMapper,
vtkProperty,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkRenderer,
vtkTextMapper,
vtkTextProperty,
vtkWindowToImageFilter
)
def get_program_parameters():
import argparse
description = 'Display the parametric surfaces.'
epilogue = '''
'''
parser = argparse.ArgumentParser(description=description, epilog=epilogue,
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('-s', '--surface_name', default=None, help='The name of the surface.')
parser.add_argument('-b', '--back_face', action='store_true', help='Color the back face.')
parser.add_argument('-n', '--normals', action='store_true', help='Display normals.')
parser.add_argument('-l', '--limits', action='store_true', help='Display the geometric bounds of the object..')
args = parser.parse_args()
return args.surface_name, args.back_face, args.normals, args.limits
def main():
back_face = True
normals = True
# Get the parametric functions and build the pipeline
pfn = get_parametric_functions()
renderer_size = 200
grid_column_dimensions = 5
grid_row_dimensions = 5
colors = vtkNamedColors()
# Create one text property for all.
text_property = vtkTextProperty()
text_property.SetJustificationToCentered()
text_property.SetFontSize(int(renderer_size / 12))
text_property.SetColor(colors.GetColor3d("LavenderBlush"))
# Create a parametric function source, renderer, mapper, and actor
# for each object.
pfn_srcs = []
renderers = []
mappers = []
actors = []
text_mappers = []
text_actors = []
# Glyph the normals.
mask_pts = []
arrow = []
glyph = []
glyph_mapper = []
glyph_actor = []
back_property = vtkProperty()
if back_face:
back_property.SetColor(colors.GetColor3d("Peru"))
# Now decide on the surfaces to build.
surfaces = pfn
# The bounding boxes for each object.
bounding_boxes = dict()
indexed_names = dict()
# The index of each parametric object.
obj_idx = -1
sorted_names = list()
for obj in sorted(surfaces.keys()):
obj_idx += 1
indexed_names[obj_idx] = obj
pfn_srcs.append(vtkParametricFunctionSource())
pfn_srcs[obj_idx].SetParametricFunction(surfaces[obj])
"""
下面三段代码是用来设置参数化曲面源的分辨率,会直接影响最终生成模型的光滑度和细节
三个方法共同决定了从数学公式生成的网格密度
"""
pfn_srcs[obj_idx].SetUResolution(51)
pfn_srcs[obj_idx].SetVResolution(51)
pfn_srcs[obj_idx].SetWResolution(51)
pfn_srcs[obj_idx].Update()
mappers.append(vtkPolyDataMapper())
mappers[obj_idx].SetInputConnection(pfn_srcs[obj_idx].GetOutputPort())
actors.append(vtkActor())
actors[obj_idx].SetMapper(mappers[obj_idx])
actors[obj_idx].GetProperty().SetColor(colors.GetColor3d("NavajoWhite"))
if back_face:
actors[obj_idx].SetBackfaceProperty(back_property)
text_mappers.append(vtkTextMapper())
text_mappers[obj_idx].SetInput(obj)
text_mappers[obj_idx].SetTextProperty(text_property)
text_actors.append(vtkActor2D())
text_actors[obj_idx].SetMapper(text_mappers[obj_idx])
text_actors[obj_idx].SetPosition(renderer_size / 2.0, 8)
renderers.append(vtkRenderer())
renderers[obj_idx].SetBackground(colors.GetColor3d("MidnightBlue"))
bounds = pfn_srcs[obj_idx].GetOutput().GetBounds()
bounding_boxes[obj] = bounds
if normals:
# Glyphing
"""
vtkMaskPoints()
主要作用是从一个数据集中选择(或“遮罩”)一部分点,并将它们作为新的输出。
简单来说,它就像一个“点选择器”,能让你从海量点数据中挑出你想要的那一部分
"""
mask_pts.append(vtkMaskPoints())
"""
多种选择点的模式
RandomModeOn 随机选择
SetOnRatio(10) 均匀间隔选择 每隔10个点选择一个
SetMaskOnBounds() 只选择边界框之内的点
"""
mask_pts[obj_idx].RandomModeOn()
mask_pts[obj_idx].SetMaximumNumberOfPoints(150)
mask_pts[obj_idx].SetInputConnection(pfn_srcs[obj_idx].GetOutputPort())
arrow.append(vtkArrowSource())
arrow[obj_idx].SetTipResolution(16) # 设置箭尖的分辨率,越大越平滑
arrow[obj_idx].SetTipLength(0.3) # 设置箭尖的长度, 0.3是一个相对于箭头总长度的值
arrow[obj_idx].SetTipRadius(0.1) # 设置箭杆的半径
glyph_scale = get_maximum_length(bounding_boxes[obj])
glyph.append(vtkGlyph3D())
glyph[obj_idx].SetSourceConnection(arrow[obj_idx].GetOutputPort())
glyph[obj_idx].SetInputConnection(mask_pts[obj_idx].GetOutputPort())
# SetVectorModeToUseNormal 设置向量模式 使用输入点上的法线向量来控制字形的方向
glyph[obj_idx].SetVectorModeToUseNormal()
# SetScaleFactor 设置缩放因子,决定每个箭头的大小
glyph[obj_idx].SetScaleFactor(glyph_scale / 10.0)
# OrientOn 启用定向功能,是SetVectorModeToUseNormal的一个补充, 在放置箭头时,要根据法线向量的方向来旋转箭头
glyph[obj_idx].OrientOn()
glyph[obj_idx].Update()
glyph_mapper.append(vtkPolyDataMapper())
glyph_mapper[obj_idx].SetInputConnection(
glyph[obj_idx].GetOutputPort())
glyph_actor.append(vtkActor())
glyph_actor[obj_idx].SetMapper(glyph_mapper[obj_idx])
glyph_actor[obj_idx].GetProperty().SetColor(colors.GetColor3d("GreenYellow"))
# Need a renderer even if there is no actor.
for i in range(obj_idx + 1, grid_column_dimensions * grid_row_dimensions):
renderers.append(vtkRenderer())
renderers[i].SetBackground(colors.GetColor3d("MidnightBlue"))
sorted_names.append(None)
ren_win = vtkRenderWindow()
ren_win.SetSize(renderer_size * grid_column_dimensions, renderer_size * grid_row_dimensions)
for row in range(0, grid_row_dimensions):
for col in range(0, grid_column_dimensions):
index = row * grid_column_dimensions + col
# (xmin, ymin, xmax, ymax)
viewport = [
float(col) * renderer_size / (grid_column_dimensions * renderer_size),
float(grid_row_dimensions - (row + 1)) * renderer_size / (grid_row_dimensions * renderer_size),
float(col + 1) * renderer_size / (grid_column_dimensions * renderer_size),
float(grid_row_dimensions - row) * renderer_size / (grid_row_dimensions * renderer_size)]
ren_win.AddRenderer(renderers[index])
renderers[index].SetViewport(viewport)
if index > obj_idx:
continue
renderers[index].AddActor(actors[index])
# Normals can only be computed for polygons and triangle strips.
# The Spline is a line.
if normals and indexed_names[index] != 'Spline':
renderers[index].AddActor(glyph_actor[index])
renderers[index].AddActor(text_actors[index])
renderers[index].ResetCamera()
renderers[index].GetActiveCamera().Azimuth(30)
renderers[index].GetActiveCamera().Elevation(-30)
renderers[index].GetActiveCamera().Zoom(0.9)
renderers[index].ResetCameraClippingRange()
iren = vtkRenderWindowInteractor()
iren.SetRenderWindow(ren_win)
fn = 'ParametricObjectsDemo'
ren_win.SetWindowName(fn)
print_callback = PrintCallback(iren, fn, 1, False)
iren.AddObserver('KeyPressEvent', print_callback)
iren.Initialize()
iren.Start()
def get_parametric_functions():
pfn = dict()
pfn['Boy'] = vtkParametricBoy()
pfn['ConicSpiral'] = vtkParametricConicSpiral()
pfn['CrossCap'] = vtkParametricCrossCap()
pfn['Dini'] = vtkParametricDini()
pfn['Ellipsoid'] = vtkParametricEllipsoid()
pfn['Enneper'] = vtkParametricEnneper()
pfn['Figure8Klein'] = vtkParametricFigure8Klein()
pfn['Klein'] = vtkParametricKlein()
pfn['Mobius'] = vtkParametricMobius()
pfn['RandomHills'] = vtkParametricRandomHills()
pfn['Roman'] = vtkParametricRoman()
pfn['SuperEllipsoid'] = vtkParametricSuperEllipsoid()
pfn['SuperToroid'] = vtkParametricSuperToroid()
pfn['Torus'] = vtkParametricTorus()
pfn['Spline'] = vtkParametricSpline()
# Extra parametric surfaces.
pfn['BohemianDome'] = vtkParametricBohemianDome()
pfn['Bour'] = vtkParametricBour()
pfn['CatalanMinimal'] = vtkParametricCatalanMinimal()
pfn['Henneberg'] = vtkParametricHenneberg()
pfn['Kuen'] = vtkParametricKuen()
pfn['PluckerConoid'] = vtkParametricPluckerConoid()
pfn['Pseudosphere'] = vtkParametricPseudosphere()
# 设置某些参数
pfn["Ellipsoid"].SetXRadius(0.5) # 将椭球体在 X 方向的半径设置为 0.5
pfn["Ellipsoid"].SetYRadius(2.0) # 将椭球体在 Y 方向的半径设置为 2.0
pfn["Mobius"].SetRadius(2.0) # 设置莫比乌斯带的半径为 2.0
pfn["Mobius"].SetMinimumV(-0.5) # 设置 V 参数的最小值为 -0.5
pfn["Mobius"].SetMaximumV(0.5) # 设置 V 参数的最大值为 0.5
pfn["RandomHills"].AllowRandomGenerationOn() # 启用随机山丘曲面的随机生成功能
pfn["RandomHills"].SetRandomSeed(1)
pfn["RandomHills"].SetNumberOfHills(30) # 设置生成的山丘数量为 30 个
# 通过调整 N1 和 N2 参数,可以改变超椭球体的形状,使其从立方体、球体变为星形等
pfn["SuperEllipsoid"].SetN1(0.5)
pfn["SuperEllipsoid"].SetN2(0.4)
# 调整超环面的参数 N1 和 N2,可以改变其横截面和主体的形状
pfn["SuperToroid"].SetN1(0.5)
pfn["SuperToroid"].SetN2(3.0)
# The spline needs points
spline_points = vtkPoints()
rng = vtkMinimalStandardRandomSequence()
rng.SetSeed(8775070)
for p in range(0, 10):
xyz = [None] * 3
for idx in range(0, len(xyz)):
xyz[idx] = rng.GetRangeValue(-1.0, 1.0)
rng.Next()
spline_points.InsertNextPoint(xyz)
pfn["Spline"].SetPoints(spline_points)
# 设置波西米亚圆顶的参数 A、B 和 C。这些参数控制了圆顶的形状和复杂性
pfn["BohemianDome"].SetA(5.0)
pfn["BohemianDome"].SetB(1.0)
pfn["BohemianDome"].SetC(2.0)
# 设置库恩曲面的一个参数,通常用于调整曲面在奇异点附近的平滑度
pfn["Kuen"].SetDeltaV0(0.001)
return pfn
def get_centre(bounds):
"""
Get the centre of the object from the bounding box.
:param bounds: The bounding box of the object.
:return:
"""
if len(bounds) != 6:
return None
return [bounds[i] - (bounds[i] - bounds[i - 1]) / 2.0 for i in range(1, len(bounds), 2)]
def get_maximum_length(bounds):
"""
Calculate the maximum length of the side bounding box.
:param bounds: The bounding box of the object.
:return:
"""
if len(bounds) != 6:
return None
return max([bounds[i] - bounds[i - 1] for i in range(1, len(bounds), 2)])
def display_bounding_box_and_center(name, bounds):
"""
Display the dimensions of the bounding box, maximum diagonal length
and coordinates of the centre.
:param name: The name of the object.
:param bounds: The bounding box of the object.
:return:
"""
if len(bounds) != 6:
return
max_len = get_maximum_length(bounds)
centre = get_centre(bounds)
s = '{:21s}\n'.format(name)
s += '{:21s}{:1s}'.format(' Bounds (min, max)', ':')
s += '{:s}({:6.2f}, {:6.2f})'.format(' x:', bounds[0], bounds[1])
s += '{:s}({:6.2f}, {:6.2f})'.format(' y:', bounds[2], bounds[3])
s += '{:s}({:6.2f}, {:6.2f})\n'.format(' z:', bounds[4], bounds[5])
if max_len:
s += ' Maximum side length: {:6.2f}\n'.format(max_len)
if centre:
s += ' Centre (x, y, z) : ({:6.2f}, {:6.2f}, {:6.2f})\n'.format(centre[0], centre[1], centre[2])
print(s)
class PrintCallback:
def __init__(self, caller, file_name, image_quality=1, rgba=True):
self.caller = caller
self.image_quality = image_quality
# rgba is the the buffer type,
# (if true, there is no background in the screenshot).
self.rgba = rgba
parent = Path(file_name).resolve().parent
pth = Path(parent) / file_name
self.path = Path(str(pth)).with_suffix('.png')
def __call__(self, caller, ev):
# Save the screenshot.
if caller.GetKeyCode() == "k":
w2if = vtkWindowToImageFilter()
w2if.SetInput(caller.GetRenderWindow())
w2if.SetScale(self.image_quality, self.image_quality)
if self.rgba:
w2if.SetInputBufferTypeToRGBA()
else:
w2if.SetInputBufferTypeToRGB()
# Read from the front buffer.
w2if.ReadFrontBufferOn()
w2if.Update()
writer = vtkPNGWriter()
writer.SetFileName(self.path)
writer.SetInputData(w2if.GetOutput())
writer.Write()
print('Screenshot saved to:', self.path.name)
if __name__ == '__main__':
main()
047:BooleanOperationImplicitFunctions隐式函数的布尔运算
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonDataModel import vtkBox, vtkImplicitBoolean, vtkSphere
from vtkmodules.vtkImagingHybrid import vtkSampleFunction
from vtkmodules.vtkFiltersCore import vtkContourFilter
from vtkmodules.vtkRenderingCore import vtkActor, vtkPolyDataMapper, vtkRenderer, vtkRenderWindow, \
vtkRenderWindowInteractor
def main():
colors = vtkNamedColors()
sphere = vtkSphere()
sphere.SetRadius(1)
sphere.SetCenter(1, 0, 0)
box = vtkBox()
box.SetBounds(-1, 1, -1, 1, -1, 1)
"""
布尔运算
"""
boolean = vtkImplicitBoolean()
boolean.SetOperationTypeToDifference()
boolean.AddFunction(box)
boolean.AddFunction(sphere) # 添加隐函数
"""
AddFunction 中 对于交集和并集没有影响
但是对于Difference有影响
A-B != B-A 谁先被AddFunction添加,谁就是住对象,第二个是被减去的对象
这里就是box-sphere
"""
"""
vtkSampleFunction 可以对隐函数进行采样,生成标量场
暑促的是一个规则网络vtkImageData,每个voxel都有一个标量值
"""
sample = vtkSampleFunction()
"""
SetImplicitFunction 告诉采样器要采样哪个隐式函数
这里 boolean 可能是一个 vtkImplicitBoolean,
它可以表示多个隐式函数的布尔运算(并集、交集、差集)
换句话说:它把“数学定义的几何体”作为输入
如果这里将boolean改为上面定义的box或sphere
则整段代码变为隐式函数的建模
"""
sample.SetImplicitFunction(boolean)
# 设置采样区域的边界范围
sample.SetModelBounds(-1, 2, -1, 1, -1, 1)
"""
SetSampleDimensions 设置网格的分辨率
这里设置了40*40*40个采样点
"""
sample.SetSampleDimensions(40, 40, 40)
# 不计算法向量(默认计算法向量)
sample.ComputeNormalsOff()
"""
vtkContourFilter 创建一个等值面提取器
在体数据(比如 vtkImageData)里,找到标量场等于某个值的“表面”
"""
surface = vtkContourFilter()
surface.SetInputConnection(sample.GetOutputPort())
"""
SetValue 设置要提取的等值面
第一个参数 0 是索引(因为 vtkContourFilter 可以同时提取多个不同数值的等值面)
第二个参数 0.0 表示:提取隐式函数值 = 0 的表面。
对于隐式函数,这个 等值面(f(x,y,z) = 0)就是物体的边界
"""
surface.SetValue(0, 0.0)
mapper = vtkPolyDataMapper()
mapper.SetInputConnection(surface.GetOutputPort())
mapper.ScalarVisibilityOff()
actor = vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().EdgeVisibilityOn()
actor.GetProperty().SetColor(colors.GetColor3d('AliceBlue'))
actor.GetProperty().SetEdgeColor(colors.GetColor3d('SteelBlue'))
# A renderer and render window
renderer = vtkRenderer()
renderer.SetBackground(colors.GetColor3d('Silver'))
# add the actor
renderer.AddActor(actor)
# render window
renwin = vtkRenderWindow()
renwin.AddRenderer(renderer)
renwin.SetWindowName('BooleanOperationImplicitFunctions')
# An interactor
interactor = vtkRenderWindowInteractor()
interactor.SetRenderWindow(renwin)
# Start
interactor.Initialize()
renwin.Render()
# renderer.GetActiveCamera().AddObserver('ModifiedEvent', CameraModifiedCallback)
renderer.GetActiveCamera().SetPosition(5.0, -4.0, 1.6)
renderer.GetActiveCamera().SetViewUp(0.1, 0.5, 0.9)
renderer.GetActiveCamera().SetDistance(6.7)
renwin.Render()
interactor.Start()
if __name__ == '__main__':
main()
048:ContourTriangulator从一个PNG图像中提取2D等值线(isoline)
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkFiltersCore import vtkMarchingSquares
from vtkmodules.vtkFiltersGeneral import vtkContourTriangulator
from vtkmodules.vtkIOImage import vtkPNGReader
from vtkmodules.vtkRenderingCore import vtkActor, vtkDataSetMapper, vtkRenderWindow, vtkRenderWindowInteractor, \
vtkRenderer
def main():
file_name = 'Data/fullhead15.png'
iso_value = 500
colors = vtkNamedColors()
reader = vtkPNGReader()
reader.SetFileName(file_name)
reader.Update()
iso = vtkMarchingSquares()
iso.SetInputConnection(reader.GetOutputPort())
"""
0表示等值线的索引,vtkMarchingSquares 可以同时提取多条不同数值的等值线
iso_value 就是图像里像素值 = 500 的位置所形成的等值线
"""
iso.SetValue(0, iso_value)
iso_mapper = vtkDataSetMapper()
iso_mapper.SetInputConnection(iso.GetOutputPort())
iso_mapper.ScalarVisibilityOff()
iso_actor = vtkActor()
iso_actor.SetMapper(iso_mapper)
iso_actor.GetProperty().SetColor(colors.GetColor3d("MediumOrchid"))
poly = vtkContourTriangulator()
poly.SetInputConnection(iso.GetOutputPort())
poly_mapper = vtkDataSetMapper()
poly_mapper.SetInputConnection(poly.GetOutputPort())
poly_mapper.ScalarVisibilityOff()
poly_actor = vtkActor()
poly_actor.SetMapper(poly_mapper)
poly_actor.GetProperty().SetColor(colors.GetColor3d("Gray"))
renderer = vtkRenderer()
ren_win = vtkRenderWindow()
"""
SetMultiSamples 关闭多重采样
在 OpenGL 渲染里,多重采样抗锯齿 (MSAA) 会让边缘更平滑,看起来不那么锯齿化。
默认情况下,vtkRenderWindow 可能会打开多重采样(比如 8x)。
这会增加计算量,有时候也会导致绘制一些特殊几何体(比如透明对象、点云、线框)时出现问题
SetMultiSamples(0) 的含义
0 = 关闭多重采样 → 没有抗锯齿(速度快,但可能有锯齿)。
>0 = 设置多重采样的采样数,比如 4 表示 4x MSAA。
"""
ren_win.SetMultiSamples(0)
ren_win.AddRenderer(renderer)
ren_win.SetWindowName('ContourTriangulator')
iren = vtkRenderWindowInteractor()
iren.SetRenderWindow(ren_win)
renderer.AddActor(poly_actor)
renderer.AddActor(iso_actor)
renderer.SetBackground(colors.GetColor3d('DarkSlateGray'))
ren_win.SetSize(300, 300)
camera = renderer.GetActiveCamera()
renderer.ResetCamera()
camera.Azimuth(180)
ren_win.Render()
iren.Initialize()
iren.Start()
if __name__ == '__main__':
main()
049:CutWithScalars根据标量利用vtkContourFilter得到等值线
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonCore import vtkDoubleArray
from vtkmodules.vtkCommonDataModel import vtkPlane
from vtkmodules.vtkFiltersCore import vtkContourFilter, vtkMarchingCubes
from vtkmodules.vtkIOXML import vtkXMLPolyDataReader
from vtkmodules.vtkRenderingCore import vtkActor, vtkRenderer, vtkRenderWindow, vtkPolyDataMapper, \
vtkRenderWindowInteractor
def main():
file_name = "Data/Torso.vtp"
num_of_cuts = 20
colors = vtkNamedColors()
reader = vtkXMLPolyDataReader()
reader.SetFileName(file_name)
reader.Update()
bounds = reader.GetOutput().GetBounds()
plane = vtkPlane()
plane.SetOrigin((bounds[0] + bounds[1]) / 2,
(bounds[2] + bounds[3]) / 2,
(bounds[4] + bounds[5]) / 2)
plane.SetNormal(0, 0, 1)
# 计算vtp文件各个点到平面的有向距离
scalars = vtkDoubleArray()
numberOfPoints1 = reader.GetOutput().GetNumberOfPoints()
scalars.SetNumberOfTuples(numberOfPoints1)
pts = reader.GetOutput().GetPoints()
for i in range(0, numberOfPoints1):
point = pts.GetPoint(i)
"""
EvaluateFunction 接收一个三维点作为输入,并返回一个带符号的标量值,值的大小就是输入点到平面的距离
如果点在平面的正法线方向一侧,返回值是正数
如果点在平面的负法线方向一侧,返回值是负数
"""
scalars.SetTuple1(i, plane.EvaluateFunction(point))
"""
告诉 VTK —— 模型的每个点现在有一个标量值,这个标量值就是点到平面的距离。
后面 vtkContourFilter 就会根据这些标量值来提取等值面
"""
reader.GetOutput().GetPointData().SetScalars(scalars)
reader.GetOutput().GetPointData().GetScalars().GetRange()
"""
vtkContourFilter VTK 中用于从标量场(scalar field)中提取等值面(或等值线)的核心算法
"""
cutter = vtkContourFilter()
cutter.SetInputConnection(reader.GetOutputPort())
cutter.ComputeScalarsOff()
cutter.ComputeNormalsOff()
"""
GenerateValues 指定如何生成等值线
第1个参数,指定要生成的等值线的数量
第2个参数,指定等值线的最小值
第3个参数,指定等值线爱你的最大值
"""
cutter.GenerateValues(num_of_cuts, 0.99 * reader.GetOutput().GetPointData().GetScalars().GetRange()[0],
0.99 * reader.GetOutput().GetPointData().GetScalars().GetRange()[1])
cutterMapper = vtkPolyDataMapper()
cutterMapper.SetInputConnection(cutter.GetOutputPort())
cutterMapper.ScalarVisibilityOff()
cutterActor = vtkActor()
cutterActor.SetMapper(cutterMapper)
cutterActor.GetProperty().SetColor(colors.GetColor3d("Banana"))
cutterActor.GetProperty().SetLineWidth(2)
modelMapper = vtkPolyDataMapper()
modelMapper.SetInputConnection(reader.GetOutputPort())
modelMapper.ScalarVisibilityOff()
modelActor = vtkActor()
modelActor.GetProperty().SetColor(colors.GetColor3d("Flesh"))
modelActor.SetMapper(modelMapper)
renderer = vtkRenderer()
renderer.AddActor(cutterActor)
# renderer.AddActor(modelActor)
renderWindow = vtkRenderWindow()
renderWindow.AddRenderer(renderer)
renderWindow.SetSize(600, 600)
renderWindow.SetWindowName('CutWithCutScalars')
interactor = vtkRenderWindowInteractor()
interactor.SetRenderWindow(renderWindow)
renderer.SetBackground(colors.GetColor3d('Burlywood'))
renderer.GetActiveCamera().SetPosition(0, -1, 0)
renderer.GetActiveCamera().SetFocalPoint(0, 0, 0)
renderer.GetActiveCamera().SetViewUp(0, 0, 1)
renderer.GetActiveCamera().Azimuth(30)
renderer.GetActiveCamera().Elevation(30)
renderer.ResetCamera()
renderWindow.Render()
interactor.Start()
if __name__ == '__main__':
main()
050:DiscreteMarchingCubes离散等值面提取算法
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonCore import vtkLookupTable, vtkMinimalStandardRandomSequence
from vtkmodules.vtkCommonDataModel import vtkImageData, vtkSphere
from vtkmodules.vtkFiltersGeneral import vtkDiscreteFlyingEdges3D, vtkDiscreteMarchingCubes
from vtkmodules.vtkImagingCore import vtkImageThreshold
from vtkmodules.vtkImagingHybrid import vtkSampleFunction
from vtkmodules.vtkImagingMath import vtkImageMathematics
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkPolyDataMapper,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkRenderer
)
def main():
n = 20
radius = 8
"""
这里只创建了一个空壳的vtkImageData,因为这里不需要直接设置范围
在 VTK 里,vtkImageData 自己本身可以设置 extent(索引范围) 和 spacing(体素间距),然后你才能在它里面放数据
但是在本示例代码中,sampler 已经 决定了采样范围和分辨率
SetModelBounds(-50, 50, -50, 50, -50, 50) = 空间边界
SetSampleDimensions(100, 100, 100) = 每个方向的体素数量
当 sampler.Update() 时,它会 创建一个新的 vtkImageData,范围就是 [-50,50]³
也就是说,这里的 vtkImageData 是 sampler 输出的结果,范围由 sampler 决定
"""
blob_image = vtkImageData()
max_r = 50 - 2.0 * radius
random_sequence = vtkMinimalStandardRandomSequence()
random_sequence.SetSeed(5071)
for i in range(0, n):
sphere = vtkSphere()
sphere.SetRadius(radius)
x = random_sequence.GetRangeValue(-max_r, max_r)
random_sequence.Next()
y = random_sequence.GetRangeValue(-max_r, max_r)
random_sequence.Next()
z = random_sequence.GetRangeValue(-max_r, max_r)
random_sequence.Next()
sphere.SetCenter(int(x), int(y), int(z))
"""
创建一个 采样器 (vtkSampleFunction)
它的作用是:在给定的空间范围内,均匀采样某个 隐式函数 (implicit function),
并把结果存成 vtkImageData(体素格子)
"""
sample = vtkSampleFunction()
sample.SetImplicitFunction(sphere)
"""
指定采样结果的标量类型。
这里设置为 float,表示每个体素的值是浮点数(而不是整型)。
好处是可以表示连续的函数值,而不仅仅是 0/1
"""
sample.SetOutputScalarTypeToFloat()
"""
指定采样网格的分辨率。
在 X、Y、Z 三个方向各采样 100 个点 → 得到 100万 个体素。
分辨率越高,结果越精细,但内存和计算也更大
"""
sample.SetSampleDimensions(100, 100, 100)
"""
设置采样空间的边界框(bounding box)
也就是说,它会在这个立方体范围内均匀布置采样点,然后对球体函数进行采样
"""
sample.SetModelBounds(-50, 50, -50, 50, -50, 50)
"""
整段代码的含义是
如果某个体素值 ≤ radius² → 说明它在球体内 → 标记为 i+1
如果 > radius² → 说明它在球体外 → 标记为 0
"""
thres = vtkImageThreshold()
thres.SetInputConnection(sample.GetOutputPort())
"""
表示保留所有 距离平方 ≤ radius² 的体素,也就是在球体内的体素
"""
thres.ThresholdByLower(radius * radius)
thres.ReplaceInOn()
thres.ReplaceOutOn()
thres.SetInValue(i + 1)
thres.SetOutValue(0)
thres.Update()
if i == 0:
blob_image.DeepCopy(thres.GetOutput())
# 图像的像素级处理
max_value = vtkImageMathematics()
max_value.SetInputData(0, blob_image)
max_value.SetInputData(1, thres.GetOutput())
"""
做“逐体素最大值”运算.也就是说,球体之间的标签会取最大值,避免覆盖
"""
max_value.SetOperationToMax()
"""
Modified
强制标记这个 filter 已经被修改,确保在调用 Update() 时重新执行图像运算,而不是复用旧缓存
"""
max_value.Modified()
max_value.Update()
blob_image.DeepCopy(max_value.GetOutput())
discrete = vtkDiscreteFlyingEdges3D()
discrete.SetInputData(blob_image)
"""
GenerateValues 指定要生成的等值面范围
GenerateValues(numContours, rangeStart, rangeEnd) 的含义是:
在 [rangeStart, rangeEnd] 之间 平均分布 numContours 个等值面值
"""
discrete.GenerateValues(n, 1, n)
lut = vtkLookupTable()
lut.SetNumberOfColors(n)
lut.SetTableRange(0, n - 1)
"""
指定查找表的标量映射是线性的(而不是对数)
"""
lut.SetScaleToLinear()
lut.Build()
"""
SetTableValue 把第 0 号颜色设为 (R=0, G=0, B=0, A=1),也就是 黑色,不透明
"""
lut.SetTableValue(0, 0, 0, 0, 1)
random_sequence = vtkMinimalStandardRandomSequence()
random_sequence.SetSeed(5071)
for i in range(1, n):
r = random_sequence.GetRangeValue(0.4, 1)
random_sequence.Next()
g = random_sequence.GetRangeValue(0.4, 1)
random_sequence.Next()
b = random_sequence.GetRangeValue(0.4, 1)
random_sequence.Next()
lut.SetTableValue(i, r, g, b, 1.0)
mapper = vtkPolyDataMapper()
mapper.SetInputConnection(discrete.GetOutputPort())
mapper.SetLookupTable(lut)
mapper.SetScalarRange(0, lut.GetNumberOfColors())
# Create the RenderWindow, Renderer and both Actors
#
ren = vtkRenderer()
ren_win = vtkRenderWindow()
ren_win.AddRenderer(ren)
ren_win.SetWindowName('DiscreteMarchingCubes')
iren = vtkRenderWindowInteractor()
iren.SetRenderWindow(ren_win)
actor = vtkActor()
actor.SetMapper(mapper)
ren.AddActor(actor)
colors = vtkNamedColors()
ren.SetBackground(colors.GetColor3d('Burlywood'))
ren_win.Render()
iren.Start()
if __name__ == '__main__':
main()
051:ExtractData 椭球的并集与函数的裁剪
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonDataModel import vtkSphere, vtkQuadric, vtkImplicitBoolean
from vtkmodules.vtkCommonTransforms import vtkTransform
from vtkmodules.vtkFiltersExtraction import vtkExtractGeometry
from vtkmodules.vtkFiltersGeneral import vtkShrinkFilter
from vtkmodules.vtkFiltersModeling import vtkOutlineFilter
from vtkmodules.vtkImagingHybrid import vtkSampleFunction
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkDataSetMapper,
vtkPolyDataMapper,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkRenderer
)
def main():
colors = vtkNamedColors()
"""
vtkQuadric 创建一个四次曲面的隐式函数
vtkQuadric 类代表了一个由二次方程定义的曲面。这个方程通常是以下形式:
这个类让你通过设置这个方程的系数来定义任何二次曲面,例如:球体,椭球体,圆锥体,抛物面,双曲面
"""
quadric = vtkQuadric()
"""
SetCoefficients() 方法用于设置方程的10个系数。
这个方法接受一个包含10个浮点数的列表,对应于方程中的 A 到 J。
这些系数共同定义了一个复杂的、类似于拉伸椭球体的形状
"""
quadric.SetCoefficients(0.5, 1, 0.2, 0, 0.1, 0, 0, 0.2, 0, 0)
sample = vtkSampleFunction()
sample.SetImplicitFunction(quadric)
sample.SetSampleDimensions(50, 50, 50)
sample.ComputeNormalsOff()
trans1 = vtkTransform()
trans1.Scale(1, 0.5, 0.333)
sphere1 = vtkSphere()
sphere1.SetRadius(0.25)
sphere1.SetTransform(trans1)
trans2 = vtkTransform()
trans2.Scale(0.25, 0.5, 1)
sphere2 = vtkSphere()
sphere2.SetRadius(0.25)
sphere2.SetTransform(trans2)
booleanUnion = vtkImplicitBoolean()
booleanUnion.AddFunction(sphere1)
booleanUnion.AddFunction(sphere2)
booleanUnion.SetOperationTypeToUnion() # 等价于booleanUnion.SetOperationType
"""
下面这段代码的含义是:
输入: 一个包含了四次曲面所有信息的三维体素网格
筛选规则: 一个由两个椭球体融合而成的复杂形状
输出: 四次曲面网格中所有位于两个椭球体并集内部的体素
"""
extract = vtkExtractGeometry()
extract.SetInputConnection(sample.GetOutputPort())
extract.SetImplicitFunction(booleanUnion)
# 添加下面这一行代码,得到不与椭球体并集重叠的部分
#extract.ExtractInsideOff()
"""
vtkSampleFunction和vtkExtractGeometry都有“基于隐函数做集合处理”的功能,但原理和结果差别很大
vtkSampleFunction
作用:把一个 隐函数(vtkImplicitFunction,比如 vtkSphere, vtkBox, vtkImplicitBoolean 等)采样到一个规则的三维网格(vtkImageData)上。
结果:生成的是 标量场 (volume / image),每个 voxel 存储函数值。
常用场景:
想通过隐函数生成一个 3D 标量场,再用 vtkContourFilter / vtkMarchingCubes / vtkFlyingEdges3D 去提取等值面。
例如:用 vtkSphere 生成一个标量场,然后提取球体表面。
👉 它的重点是 数值采样(离散化),生成体素数据。
vtkExtractGeometry
作用:直接从一个已有的数据集(vtkDataSet,如 vtkPolyData 或 vtkUnstructuredGrid)中,
提取出 在隐函数内部/外部 的几何体。
结果:生成的是 子集几何 (subset geometry),不改变原始拓扑,只是“筛选”。
常用场景:
已经有一个复杂模型(例如医学 CT 数据提取出来的网格,或者 CAD 模型),
只想保留其中在某个隐函数(比如球体或平面)内部的部分。
相当于“裁剪 (clipping)”或“掩模 (masking)”。
👉 它的重点是 过滤现有几何,而不是生成标量场。
"""
shrink = vtkShrinkFilter()
shrink.SetInputConnection(extract.GetOutputPort())
shrink.SetShrinkFactor(0.5)
dataMapper = vtkDataSetMapper()
dataMapper.SetInputConnection(shrink.GetOutputPort())
dataActor = vtkActor()
dataActor.SetMapper(dataMapper)
outline = vtkOutlineFilter()
outline.SetInputConnection(sample.GetOutputPort())
outlineMapper = vtkPolyDataMapper()
outlineMapper.SetInputConnection(outline.GetOutputPort())
outlineActor = vtkActor()
outlineActor.SetMapper(outlineMapper)
outlineActor.GetProperty().SetColor(0, 0, 0)
ren1 = vtkRenderer()
ren1.AddActor(outlineActor)
ren1.AddActor(dataActor)
ren1.SetBackground(colors.GetColor3d("SlateGray"))
renWin = vtkRenderWindow()
renWin.AddRenderer(ren1)
iren = vtkRenderWindowInteractor()
iren.SetRenderWindow(renWin)
renWin.SetSize(640, 480)
renWin.SetWindowName('ExtractData')
renWin.Render()
ren1.GetActiveCamera().Azimuth(30)
ren1.GetActiveCamera().Elevation(30)
renWin.Render()
iren.Start()
if __name__ == '__main__':
main()
052:ExtractLargestIsosurface 提取最大连通域
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkFiltersCore import vtkFlyingEdges3D, vtkMarchingCubes, vtkPolyDataConnectivityFilter
from vtkmodules.vtkIOLegacy import vtkStructuredPointsReader
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkPolyDataMapper,
vtkProperty,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkRenderer
)
def main():
colors = vtkNamedColors()
colors.SetColor('SkinColor', [240, 184, 160, 255])
colors.SetColor('BackfaceColor', [255, 229, 200, 255])
colors.SetColor('BkgColor', [51, 77, 102, 255])
file_name = "Data/brain.vtk"
threshold = 50
largest_surface = False # 是否提取最大连通域
use_flying_edges = True
reader = vtkStructuredPointsReader()
reader.SetFileName(file_name)
reader.Update()
if use_flying_edges:
mc = vtkFlyingEdges3D()
else:
mc = vtkMarchingCubes()
mc.SetInputConnection(reader.GetOutputPort())
mc.ComputeNormalsOff()
mc.ComputeGradientsOn()
mc.SetValue(0, threshold)
confilter = vtkPolyDataConnectivityFilter()
confilter.SetInputConnection(mc.GetOutputPort())
confilter.SetExtractionModeToLargestRegion()
mapper = vtkPolyDataMapper()
mapper.SetInputConnection(confilter.GetOutputPort())
mapper.ScalarVisibilityOff()
actor = vtkActor()
actor.GetProperty().SetColor(colors.GetColor3d('SkinColor'))
back_prop = vtkProperty()
back_prop.SetDiffuseColor(colors.GetColor3d('BackfaceColor'))
actor.SetBackfaceProperty(back_prop)
actor.SetMapper(mapper)
renderer = vtkRenderer()
renderer.AddActor(actor)
renderer.SetBackground(colors.GetColor3d('SlateGray'))
renderer.GetActiveCamera().SetViewUp(0.0, 0.0, 1.0)
renderer.GetActiveCamera().SetPosition(0.0, 1.0, 0.0)
renderer.GetActiveCamera().SetFocalPoint(0.0, 0.0, 0.0)
renderer.ResetCamera()
renderer.GetActiveCamera().Azimuth(30.0)
renderer.GetActiveCamera().Elevation(30.0)
ren_win = vtkRenderWindow()
ren_win.AddRenderer(renderer)
ren_win.SetSize(640, 480)
ren_win.SetWindowName('ExtractLargestIsosurface')
iren = vtkRenderWindowInteractor()
iren.SetRenderWindow(ren_win)
ren_win.Render()
iren.Initialize()
iren.Start()
if __name__ == '__main__':
main()
053:Hello 隐式建模
"""
主要展示了如何使用**隐式建模(implicit modelling)**技术,
将一个简单的二维线条集合(hello.vtk 文件中的文字)“加厚”成一个平滑、半透明的三维实体
"""
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkFiltersCore import vtkContourFilter
from vtkmodules.vtkFiltersHybrid import vtkImplicitModeller
from vtkmodules.vtkIOLegacy import vtkPolyDataReader
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkCamera,
vtkPolyDataMapper,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkRenderer
)
def main():
fileName = "Data/hello.vtk"
colors = vtkNamedColors()
reader = vtkPolyDataReader()
reader.SetFileName(fileName)
reader.Update()
lineMapper = vtkPolyDataMapper()
lineMapper.SetInputConnection(reader.GetOutputPort())
lineActor = vtkActor()
lineActor.SetMapper(lineMapper)
lineActor.GetProperty().SetColor(colors.GetColor3d("Tomato"))
lineActor.GetProperty().SetLineWidth(3)
"""
vtkImplicitModeller
把输入的几何(reader 读出的模型,比如一个 vtkPolyData)转换成一个 隐式函数场
(就像 vtkSampleFunction 生成标量场一样),以便后续做等值面提取、布尔运算等
"""
imp = vtkImplicitModeller()
"""
输出的几何体(比如一个三维曲面)会被转换成“体素化的标量场”
"""
imp.SetInputConnection(reader.GetOutputPort())
"""
设置体素网格的分辨率(x, y, z 三个方向上有多少采样点)
"""
imp.SetSampleDimensions(110, 40, 20)
"""
设置一个截断距离
隐式建模器会计算“每个采样点到最近输入几何的距离”
小于 0.25 的地方会存下真实距离
大于 0.25 的地方会统一设置为 0.25(避免无限大)
"""
imp.SetMaximumDistance(0.25)
imp.SetModelBounds(-1.0, 10.0, -1.0, 3.0, -1.0, 1.0)
contour = vtkContourFilter()
contour.SetInputConnection(imp.GetOutputPort())
"""
SetValue 这里的值如果大于SetMaximumDistance
vtkImplicitModeller 会自动调整并可能忽略你设定的 MaximumDistance
"""
contour.SetValue(0, 0.25)
impMapper = vtkPolyDataMapper()
impMapper.SetInputConnection(contour.GetOutputPort())
impMapper.ScalarVisibilityOff()
impActor = vtkActor()
impActor.SetMapper(impMapper)
impActor.GetProperty().SetColor(colors.GetColor3d('Peacock'))
impActor.GetProperty().SetOpacity(0.5)
# Create the usual graphics stuff.
# Create the RenderWindow, Renderer and Interactor.
#
ren1 = vtkRenderer()
renWin = vtkRenderWindow()
renWin.AddRenderer(ren1)
renWin.SetWindowName('Hello')
iren = vtkRenderWindowInteractor()
iren.SetRenderWindow(renWin)
# Add the actors to the renderer, set the background and size
#
ren1.AddActor(lineActor)
ren1.AddActor(impActor)
ren1.SetBackground(colors.GetColor3d('Wheat'))
renWin.SetSize(640, 480)
camera = vtkCamera()
camera.SetFocalPoint(4.5, 1, 0)
camera.SetPosition(4.5, 1.0, 6.73257)
camera.SetViewUp(0, 1, 0)
ren1.SetActiveCamera(camera)
ren1.ResetCamera()
camera.Dolly(1.3)
camera.SetClippingRange(1.81325, 90.6627)
renWin.Render()
iren.Start()
if __name__ == '__main__':
main()
054:IceCream 隐式建模
"""
如何使用隐式函数(implicit functions)和布尔运算来创建一个冰淇淋筒的三维模型
"""
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonDataModel import vtkCone, vtkImplicitBoolean, vtkPlane, vtkSphere
from vtkmodules.vtkFiltersCore import vtkContourFilter
from vtkmodules.vtkImagingHybrid import vtkSampleFunction
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkPolyDataMapper,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkRenderer
)
def main():
colors = vtkNamedColors()
cone = vtkCone()
# 设置其半顶角为20度。越小,圆锥越尖锐、细长
# 可以想象一个圆锥体,它有一个尖锐的顶点。
# 如果你从这个顶点沿着圆锥体的中轴线向下看,并且从这个中轴线向圆锥体的斜边
# (母线)画一条线,这两条线之间的夹角就是半顶角
cone.SetAngle(20)
vertPlane = vtkPlane()
vertPlane.SetOrigin(.1, 0, 0)
vertPlane.SetNormal(-1, 0, 0)
basePlane = vtkPlane()
basePlane.SetOrigin(1.2, 0, 0)
basePlane.SetNormal(1, 0, 0)
iceCream = vtkSphere()
iceCream.SetCenter(1.333, 0., 0)
iceCream.SetRadius(0.5)
bite = vtkSphere()
bite.SetCenter(1.5, 0, 0.5)
bite.SetRadius(0.25)
theCone = vtkImplicitBoolean()
theCone.SetOperationTypeToIntersection() # 求交集
theCone.AddFunction(cone)
theCone.AddFunction(vertPlane)
theCone.AddFunction(basePlane)
theCream = vtkImplicitBoolean()
theCream.SetOperationTypeToDifference() # 求差集
theCream.AddFunction(iceCream)
theCream.AddFunction(bite)
theConeSmple = vtkSampleFunction()
theConeSmple.SetImplicitFunction(theCone)
theConeSmple.SetModelBounds(-1, 1.5, -1.25, 1.25, -1.25, 1.25)
theConeSmple.SetSampleDimensions(128, 128, 128)
theConeSmple.ComputeNormalsOff()
theConeSurface = vtkContourFilter()
theConeSurface.SetInputConnection(theConeSmple.GetOutputPort())
theConeSurface.SetValue(0, 0.0)
coneMapper = vtkPolyDataMapper()
coneMapper.SetInputConnection(theConeSurface.GetOutputPort())
coneMapper.ScalarVisibilityOff()
coneActor = vtkActor()
coneActor.SetMapper(coneMapper)
coneActor.GetProperty().SetColor(colors.GetColor3d('Chocolate'))
theCreamSample = vtkSampleFunction()
theCreamSample.SetImplicitFunction(theCream)
theCreamSample.SetModelBounds(0, 2.5, -1.25, 1.25, -1.25, 1.25)
theConeSmple.SetSampleDimensions(128,128, 128)
theCreamSurface = vtkContourFilter()
theCreamSurface.SetInputConnection(theCreamSample.GetOutputPort())
theCreamSurface.SetValue(0, 0.0)
creamMapper = vtkPolyDataMapper()
creamMapper.SetInputConnection(theCreamSurface.GetOutputPort())
creamMapper.ScalarVisibilityOff()
creamActor = vtkActor()
creamActor.SetMapper(creamMapper)
creamActor.GetProperty().SetDiffuseColor(colors.GetColor3d('Mint'))
creamActor.GetProperty().SetSpecular(.6)
creamActor.GetProperty().SetSpecularPower(50)
# Create the usual rendering stuff.
#
ren1 = vtkRenderer()
renWin = vtkRenderWindow()
renWin.AddRenderer(ren1)
iren = vtkRenderWindowInteractor()
iren.SetRenderWindow(renWin)
# Add the actors to the renderer, set the background and size.
#
ren1.AddActor(coneActor)
ren1.AddActor(creamActor)
ren1.SetBackground(colors.GetColor3d('SlateGray'))
renWin.SetSize(640, 480)
renWin.SetWindowName('IceCream')
ren1.ResetCamera()
ren1.GetActiveCamera().Roll(90)
ren1.GetActiveCamera().Dolly(1.25)
ren1.ResetCameraClippingRange()
iren.Initialize()
# render the image
#
renWin.Render()
iren.Start()
if __name__ == '__main__':
main()
055:Lorenz 标量填充vtkStructuredPoints生成的三位体数据中
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonCore import vtkMinimalStandardRandomSequence, vtkShortArray
from vtkmodules.vtkCommonDataModel import vtkStructuredPoints
from vtkmodules.vtkFiltersCore import vtkContourFilter
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkPolyDataMapper,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkRenderer
)
def main():
colors = vtkNamedColors()
Pr = 10.0 # The Lorenz parameters
b = 2.667
r = 28.0
h = 0.01 # integration step size
resolution = 200 # slice resolution
iterations = 10000000 # number of iterations
xmin = -30.0 # x, y, z range for voxels
xmax = 30.0
ymin = -30.0
ymax = 30.0
zmin = -10.0
zmax = 60.0
# Take a stab at an integration step size.
xIncr = resolution / (xmax - xmin)
yIncr = resolution / (ymax - ymin)
zIncr = resolution / (zmax - zmin)
randomSequence = vtkMinimalStandardRandomSequence()
randomSequence.SetSeed(8775070)
x = randomSequence.GetRangeValue(xmin, xmax)
randomSequence.Next()
y = randomSequence.GetRangeValue(ymin, ymax)
randomSequence.Next()
z = randomSequence.GetRangeValue(zmin, zmax)
randomSequence.Next()
# allocate memory for the slices
sliceSize = resolution * resolution
numPts = sliceSize * resolution
scalars = vtkShortArray()
for i in range(0, numPts):
scalars.InsertTuple1(i, 0)
for j in range(0, iterations):
# Integrate to the next time step.
xx = x + h * Pr * (y - x)
yy = y + h * (x * (r - z) - y)
zz = z + h * (x * y - (b * z))
x = xx
y = yy
z = zz
# Calculate the voxel index.
if xmax > x > xmin and ymax > y > ymin and zmax > z > zmin:
xxx = int(float(xx - xmin) * xIncr)
yyy = int(float(yy - ymin) * yIncr)
zzz = int(float(zz - zmin) * zIncr)
index = xxx + yyy * resolution + zzz * sliceSize
scalars.SetTuple1(index, scalars.GetTuple1(index) + 1)
"""
使用**vtkStructuredPoints类来创建一个三维体数据(volume data)对象,
并用之前模拟洛伦兹吸引子轨迹得到的标量数据**填充它
"""
volume = vtkStructuredPoints()
volume.GetPointData().SetScalars(scalars)
volume.SetDimensions(resolution, resolution, resolution)
volume.SetOrigin(xmin, ymin, zmin)
volume.SetSpacing((xmax - xmin) / resolution, (ymax - ymin) / resolution, (zmax - zmin) / resolution)
# Create iso-surface
contour = vtkContourFilter()
contour.SetInputData(volume)
contour.SetValue(0, 50)
# Create mapper.
mapper = vtkPolyDataMapper()
mapper.SetInputConnection(contour.GetOutputPort())
mapper.ScalarVisibilityOff()
# Create actor.
actor = vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetColor(colors.GetColor3d('DodgerBlue'))
renderer = vtkRenderer()
renWin = vtkRenderWindow()
renWin.AddRenderer(renderer)
iren = vtkRenderWindowInteractor()
iren.SetRenderWindow(renWin)
renderer.AddActor(actor)
renderer.SetBackground(colors.GetColor3d('PaleGoldenrod'))
renWin.SetSize(640, 480)
# interact with data
renWin.Render()
renWin.SetWindowName('Lorenz')
camera = renderer.GetActiveCamera()
camera.SetPosition(-67.645167, -25.714343, 63.483516)
camera.SetFocalPoint(3.224902, -4.398594, 29.552112)
camera.SetViewUp(-0.232264, 0.965078, 0.121151)
camera.SetDistance(81.414176)
camera.SetClippingRange(18.428905, 160.896031)
iren.Start()
if __name__ == '__main__':
main()
056:MarchingCases marchingcubes算法15种情况的展示
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonCore import vtkFloatArray, vtkIdList, vtkPoints
from vtkmodules.vtkCommonDataModel import vtkUnstructuredGrid, VTK_HEXAHEDRON
from vtkmodules.vtkCommonTransforms import vtkTransform
from vtkmodules.vtkFiltersCore import vtkContourFilter, vtkGlyph3D, vtkThresholdPoints, vtkTubeFilter
from vtkmodules.vtkFiltersCore import vtkExtractEdges
from vtkmodules.vtkFiltersGeneral import vtkShrinkPolyData, vtkTransformPolyDataFilter
from vtkmodules.vtkFiltersSources import (
vtkCubeSource,
vtkSphereSource
)
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkPolyDataMapper,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkRenderer
)
from vtkmodules.vtkRenderingFreeType import vtkVectorText
Scalars = vtkFloatArray()
def main():
cases = [case0, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13,
case14]
colors = vtkNamedColors()
rotation = 0
renderers = list()
gridSize = ((len(cases) + 3) // 4) * 4
renWin = vtkRenderWindow()
renWin.SetSize(640, 480)
iren = vtkRenderWindowInteractor()
iren.SetRenderWindow(renWin)
for i in range(0, 16):
renderer = vtkRenderer()
renderers.append(renderer)
renderers[i].SetBackground(colors.GetColor3d("slate_grey"))
renWin.AddRenderer(renderer)
for i in range(0, len(cases)):
Scalars = vtkFloatArray()
Scalars.InsertNextValue(1.0)
Scalars.InsertNextValue(0.0)
Scalars.InsertNextValue(0.0)
Scalars.InsertNextValue(1.0)
Scalars.InsertNextValue(0.0)
Scalars.InsertNextValue(0.0)
Scalars.InsertNextValue(0.0)
Scalars.InsertNextValue(0.0)
Points = vtkPoints()
Points.InsertNextPoint(0, 0, 0)
Points.InsertNextPoint(1, 0, 0)
Points.InsertNextPoint(1, 1, 0)
Points.InsertNextPoint(0, 1, 0)
Points.InsertNextPoint(0, 0, 1)
Points.InsertNextPoint(1, 0, 1)
Points.InsertNextPoint(1, 1, 1)
Points.InsertNextPoint(0, 1, 1)
Ids = vtkIdList()
Ids.InsertNextId(0)
Ids.InsertNextId(1)
Ids.InsertNextId(2)
Ids.InsertNextId(3)
Ids.InsertNextId(4)
Ids.InsertNextId(5)
Ids.InsertNextId(6)
Ids.InsertNextId(7)
Grid = vtkUnstructuredGrid()
"""
第一个参数:插入10个单元格
第二个参数:每个单元格插入10个点
但是在这个按理中,每个立方体只有一个单元格 10>1
每个单元格只有8个点, 10 > 8
示例代码很明显是分配空间> 所需空间
"""
Grid.Allocate(10, 10)
Grid.InsertNextCell(VTK_HEXAHEDRON, Ids)
Grid.SetPoints(Points)
Grid.GetPointData().SetScalars(Scalars)
marching = vtkContourFilter()
marching.SetInputData(Grid)
marching.SetValue(0, 0.5)
marching.Update()
# 提取经由marching找到的等值面三角网格面的边缘
triangleEdges = vtkExtractEdges()
triangleEdges.SetInputConnection(marching.GetOutputPort())
# 将提取的边缘变粗,方便可视化
triangleEdgeTubes = vtkTubeFilter()
triangleEdgeTubes.SetInputConnection(triangleEdges.GetOutputPort())
triangleEdgeTubes.SetRadius(0.005)
triangleEdgeTubes.SetNumberOfSides(6)
triangleEdgeTubes.UseDefaultNormalOn() # 使用默认法线
triangleEdgeTubes.SetDefaultNormal(.577, .577, .577)
triangleEdgeMapper = vtkPolyDataMapper()
triangleEdgeMapper.SetInputConnection(triangleEdgeTubes.GetOutputPort())
triangleEdgeMapper.ScalarVisibilityOff()
triangleEdgeActor = vtkActor()
triangleEdgeActor.SetMapper(triangleEdgeMapper)
triangleEdgeActor.GetProperty().SetDiffuseColor(colors.GetColor3d("lamp_black"))
triangleEdgeActor.GetProperty().SetSpecular(.4) # 设置镜面反射
triangleEdgeActor.GetProperty().SetSpecularPower(10) # 设置镜面强度
aShrikner = vtkShrinkPolyData()
aShrikner.SetInputConnection(marching.GetOutputPort())
aShrikner.SetShrinkFactor(1.0)
aMapper = vtkPolyDataMapper()
aMapper.ScalarVisibilityOff()
aMapper.SetInputConnection(aShrikner.GetOutputPort())
Triangles = vtkActor()
Triangles.SetMapper(aMapper)
Triangles.GetProperty().SetDiffuseColor(colors.GetColor3d('banana'))
Triangles.GetProperty().SetOpacity(.6)
# 整体的立方体的构建
CubeModel = vtkCubeSource()
CubeModel.SetCenter(.5, .5, .5)
Edges = vtkExtractEdges()
Edges.SetInputConnection(CubeModel.GetOutputPort())
Tubes = vtkTubeFilter()
Tubes.SetInputConnection(Edges.GetOutputPort())
Tubes.SetRadius(.01)
Tubes.SetNumberOfSides(6)
Tubes.UseDefaultNormalOn()
Tubes.SetDefaultNormal(.577, .577, .577)
TubeMapper = vtkPolyDataMapper()
TubeMapper.SetInputConnection(Tubes.GetOutputPort())
CubeEdges = vtkActor()
CubeEdges.SetMapper(TubeMapper)
CubeEdges.GetProperty().SetDiffuseColor(
colors.GetColor3d('khaki'))
CubeEdges.GetProperty().SetSpecular(.4)
CubeEdges.GetProperty().SetSpecularPower(10)
# 新建球体,用来表示marching cubes哪里是1,哪里是0
sphere = vtkSphereSource()
sphere.SetRadius(0.04)
sphere.SetPhiResolution(20)
sphere.SetThetaResolution(20)
# 移除掉grid中点的scalar
ThresholdIn = vtkThresholdPoints()
ThresholdIn.SetInputData(Grid)
ThresholdIn.ThresholdByUpper(.5) # 只保留标量值小于或等于 0.5 的所有点
Vertices = vtkGlyph3D()
Vertices.SetInputConnection(ThresholdIn.GetOutputPort())
Vertices.SetSourceConnection(sphere.GetOutputPort())
SphereMapper = vtkPolyDataMapper()
SphereMapper.SetInputConnection(Vertices.GetOutputPort())
SphereMapper.ScalarVisibilityOff()
CubeVertices = vtkActor()
CubeVertices.SetMapper(SphereMapper)
CubeVertices.GetProperty().SetDiffuseColor(
colors.GetColor3d('tomato'))
caseLabel = vtkVectorText()
aLabelTransform = vtkTransform()
aLabelTransform.Identity()
aLabelTransform.Translate(-0.2, 0, 1.25)
aLabelTransform.Scale(.05, .05, .05)
# 将label这个数据移动到新的位置
labelTransform = vtkTransformPolyDataFilter()
labelTransform.SetTransform(aLabelTransform)
labelTransform.SetInputConnection(caseLabel.GetOutputPort())
labelMapper = vtkPolyDataMapper()
labelMapper.SetInputConnection(labelTransform.GetOutputPort())
labelActor = vtkActor()
labelActor.SetMapper(labelMapper)
# 设置一个基座,用来放置grid
baseModel = vtkCubeSource()
baseModel.SetXLength(1.5)
baseModel.SetYLength(0.01)
baseModel.SetZLength(1.5)
baseMapper = vtkPolyDataMapper()
baseMapper.SetInputConnection(baseModel.GetOutputPort())
base = vtkActor()
base.SetMapper(baseMapper)
base.SetPosition(.5, -0.09, .5)
"""
在这里,完成对于Scalars标量值的更新
"""
cases[i](Scalars, caseLabel, 1, 0)
"""
Grid对象进行数据更新
为什么Scalars这个对象的更新,能够影响到已经构建好了的Grid里面的标量的值??
Grid.GetPointData().SetScalars(Scalars) 这一行没有复制数据,只是建立起了一个引用
SetScalars() 的作用:建立联系,而非复制数据
你可以把 Scalars 想象成一个装满水的水桶,而 Grid 就像一个需要水的洒水器。
Grid.GetPointData().SetScalars(Scalars) 这句话的作用是:
“把洒水器的进水管,接到这个水桶上。”
这行代码执行之后,Grid(洒水器)就知道去哪里找它的水源(Scalars)。它本身没有自己的水,它只是引用着 Scalars 这个水桶里的数据。
当你循环中调用 caseX(Scalars, ...) 函数时,你实际上是在:
“直接往这个水桶里倒新水。”
因为 Grid 的进水管一直连接着同一个水桶,所以当你改变 Scalars 里数据的时候,Grid 立即就能“看到”这些变化。
Grid.Modified() 的作用就是:
“告诉洒水器:嘿,水桶里的水换了,记得重新检查一下!”
如果没有这句通知,洒水器可能懒得去检查,继续用它之前的水。有了这句通知,它就会去检查水桶,发现水变了,然后把新数据传递给下游的过滤器(vtkContourFilter)
"""
Grid.Modified()
renderers[i].AddActor(triangleEdgeActor)
renderers[i].AddActor(base)
renderers[i].AddActor(labelActor)
renderers[i].AddActor(CubeEdges)
renderers[i].AddActor(CubeVertices)
renderers[i].AddActor(Triangles)
renderers[i].GetActiveCamera().Azimuth(30)
renderers[i].GetActiveCamera().Elevation(20)
renderers[i].ResetCamera()
renderers[i].ResetCameraClippingRange()
"""
原本的示例代码是这样写的
if i > 0:
renderers[i].SetActiveCamera(renderers[0].GetActiveCamera())
这行代码的意思是,所有的render都使用索引为0的render的那个相机
这就意味着,窗口的所有render在鼠标进行操作时,因为是一个相机,所有的render都会一起变化
"""
rendererSize = 300
xGridDimensions = 4
yGridDimensions = (len(cases) - 1) // 4 + 1
print('Grid dimensions, (x, y): ({:d}, {:d})'.format(xGridDimensions, yGridDimensions))
renWin.SetSize(
rendererSize * xGridDimensions, rendererSize * yGridDimensions)
renWin.SetWindowName('MarchingCases')
"""
vtk的惰性执行,哪怕前面已经将render塞入到renderwindow中,这里再进行更改也是可以的
"""
for row in range(0, yGridDimensions):
for col in range(0, xGridDimensions):
index = row * xGridDimensions + col
# (xmin, ymin, xmax, ymax)
viewport = [
float(col) / xGridDimensions,
float(yGridDimensions - (row + 1)) / yGridDimensions,
float(col + 1) / xGridDimensions,
float(yGridDimensions - row) / yGridDimensions]
renderers[index].SetViewport(viewport)
iren.Initialize()
renWin.Render()
iren.Start()
def case0(scalars: vtkFloatArray,
caseLabel: vtkVectorText,
IN: float,
OUT: float):
# SetValue和InsertValue这两种写法都可以
scalars.SetValue(0, OUT)
scalars.SetValue(1, OUT)
scalars.InsertValue(2, OUT)
scalars.InsertValue(3, OUT)
scalars.InsertValue(4, OUT)
scalars.InsertValue(5, OUT)
scalars.InsertValue(6, OUT)
scalars.InsertValue(7, OUT)
if IN == 1:
caseLabel.SetText('Case 0 - 00000000')
else:
caseLabel.SetText('Case 0c - 11111111')
def case1(scalars, caseLabel, IN, OUT):
scalars.InsertValue(0, IN)
scalars.InsertValue(1, OUT)
scalars.InsertValue(2, OUT)
scalars.InsertValue(3, OUT)
scalars.InsertValue(4, OUT)
scalars.InsertValue(5, OUT)
scalars.InsertValue(6, OUT)
scalars.InsertValue(7, OUT)
if IN == 1:
caseLabel.SetText('Case 1 - 00000001')
else:
caseLabel.SetText('Case 1c - 11111110')
def case2(scalars, caseLabel, IN, OUT):
scalars.InsertValue(0, IN)
scalars.InsertValue(1, IN)
scalars.InsertValue(2, OUT)
scalars.InsertValue(3, OUT)
scalars.InsertValue(4, OUT)
scalars.InsertValue(5, OUT)
scalars.InsertValue(6, OUT)
scalars.InsertValue(7, OUT)
if IN == 1:
caseLabel.SetText('Case 2 - 00000011')
else:
caseLabel.SetText('Case 2c - 11111100')
def case3(scalars, caseLabel, IN, OUT):
scalars.InsertValue(0, IN)
scalars.InsertValue(1, OUT)
scalars.InsertValue(2, IN)
scalars.InsertValue(3, OUT)
scalars.InsertValue(4, OUT)
scalars.InsertValue(5, OUT)
scalars.InsertValue(6, OUT)
scalars.InsertValue(7, OUT)
if IN == 1:
caseLabel.SetText('Case 3 - 00000101')
else:
caseLabel.SetText('Case 3c - 11111010')
def case4(scalars, caseLabel, IN, OUT):
scalars.InsertValue(0, IN)
scalars.InsertValue(1, OUT)
scalars.InsertValue(2, OUT)
scalars.InsertValue(3, OUT)
scalars.InsertValue(4, OUT)
scalars.InsertValue(5, OUT)
scalars.InsertValue(6, IN)
scalars.InsertValue(7, OUT)
if IN == 1:
caseLabel.SetText('Case 4 - 01000001')
else:
caseLabel.SetText('Case 4c - 10111110')
def case5(scalars, caseLabel, IN, OUT):
scalars.InsertValue(0, OUT)
scalars.InsertValue(1, IN)
scalars.InsertValue(2, OUT)
scalars.InsertValue(3, OUT)
scalars.InsertValue(4, IN)
scalars.InsertValue(5, IN)
scalars.InsertValue(6, OUT)
scalars.InsertValue(7, OUT)
if IN == 1:
caseLabel.SetText('Case 5 - 00110010')
else:
caseLabel.SetText('Case 5c - 11001101')
def case6(scalars, caseLabel, IN, OUT):
scalars.InsertValue(0, OUT)
scalars.InsertValue(1, IN)
scalars.InsertValue(2, OUT)
scalars.InsertValue(3, IN)
scalars.InsertValue(4, IN)
scalars.InsertValue(5, OUT)
scalars.InsertValue(6, OUT)
scalars.InsertValue(7, OUT)
if IN == 1:
caseLabel.SetText('Case 6 - 00011010')
else:
caseLabel.SetText('Case 6c - 11100101')
def case7(scalars, caseLabel, IN, OUT):
scalars.InsertValue(0, IN)
scalars.InsertValue(1, IN)
scalars.InsertValue(2, OUT)
scalars.InsertValue(3, OUT)
scalars.InsertValue(4, OUT)
scalars.InsertValue(5, OUT)
scalars.InsertValue(6, IN)
scalars.InsertValue(7, OUT)
if IN == 1:
caseLabel.SetText('Case 7 - 01000011')
else:
caseLabel.SetText('Case 7c - 10111100')
def case8(scalars, caseLabel, IN, OUT):
scalars.InsertValue(0, IN)
scalars.InsertValue(1, IN)
scalars.InsertValue(2, OUT)
scalars.InsertValue(3, OUT)
scalars.InsertValue(4, IN)
scalars.InsertValue(5, IN)
scalars.InsertValue(6, OUT)
scalars.InsertValue(7, OUT)
if IN == 1:
caseLabel.SetText('Case 8 - 00110011')
else:
caseLabel.SetText('Case 8c - 11001100')
def case9(scalars, caseLabel, IN, OUT):
scalars.InsertValue(0, OUT)
scalars.InsertValue(1, IN)
scalars.InsertValue(2, IN)
scalars.InsertValue(3, IN)
scalars.InsertValue(4, OUT)
scalars.InsertValue(5, OUT)
scalars.InsertValue(6, IN)
scalars.InsertValue(7, OUT)
if IN == 1:
caseLabel.SetText('Case 9 - 01001110')
else:
caseLabel.SetText('Case 9c - 10110001')
def case10(scalars, caseLabel, IN, OUT):
scalars.InsertValue(0, IN)
scalars.InsertValue(1, OUT)
scalars.InsertValue(2, OUT)
scalars.InsertValue(3, IN)
scalars.InsertValue(4, OUT)
scalars.InsertValue(5, IN)
scalars.InsertValue(6, IN)
scalars.InsertValue(7, OUT)
if IN == 1:
caseLabel.SetText('Case 10 - 01101001')
else:
caseLabel.SetText('Case 10c - 10010110')
def case11(scalars, caseLabel, IN, OUT):
scalars.InsertValue(0, IN)
scalars.InsertValue(1, OUT)
scalars.InsertValue(2, OUT)
scalars.InsertValue(3, OUT)
scalars.InsertValue(4, IN)
scalars.InsertValue(5, IN)
scalars.InsertValue(6, IN)
scalars.InsertValue(7, OUT)
if IN == 1:
caseLabel.SetText('Case 11 - 01110001')
else:
caseLabel.SetText('Case 11c - 10001110')
def case12(scalars, caseLabel, IN, OUT):
scalars.InsertValue(0, OUT)
scalars.InsertValue(1, IN)
scalars.InsertValue(2, OUT)
scalars.InsertValue(3, IN)
scalars.InsertValue(4, IN)
scalars.InsertValue(5, IN)
scalars.InsertValue(6, OUT)
scalars.InsertValue(7, OUT)
if IN == 1:
caseLabel.SetText('Case 12 - 00111010')
else:
caseLabel.SetText('Case 12c - 11000101')
def case13(scalars, caseLabel, IN, OUT):
scalars.InsertValue(0, OUT)
scalars.InsertValue(1, IN)
scalars.InsertValue(2, OUT)
scalars.InsertValue(3, IN)
scalars.InsertValue(4, IN)
scalars.InsertValue(5, OUT)
scalars.InsertValue(6, IN)
scalars.InsertValue(7, OUT)
if IN == 1:
caseLabel.SetText('Case 13 - 01011010')
else:
caseLabel.SetText('Case 13c - 10100101')
def case14(scalars, caseLabel, IN, OUT):
scalars.InsertValue(0, IN)
scalars.InsertValue(1, OUT)
scalars.InsertValue(2, IN)
scalars.InsertValue(3, IN)
scalars.InsertValue(4, OUT)
scalars.InsertValue(5, IN)
scalars.InsertValue(6, IN)
scalars.InsertValue(7, IN)
if IN == 1:
caseLabel.SetText('Case 14 - 11101101')
else:
caseLabel.SetText('Case 14c - 00010010')
if __name__ == '__main__':
main()
057:MarchingCubes 网格数据体素化并提取等值面
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonDataModel import vtkImageData
from vtkmodules.vtkFiltersCore import vtkFlyingEdges3D, vtkMarchingCubes
from vtkmodules.vtkFiltersSources import vtkSphereSource
from vtkmodules.vtkImagingHybrid import vtkVoxelModeller
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkPolyDataMapper,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkRenderer
)
if __name__ == '__main__':
colors = vtkNamedColors()
iso_value = 50
volume = vtkImageData()
sphere_source = vtkSphereSource()
sphere_source.SetPhiResolution(20)
sphere_source.SetThetaResolution(20)
sphere_source.Update()
bounds = list(sphere_source.GetOutput().GetBounds())
"""
根据现有包围盒,将包围盒大小进行扩大
"""
for i in range(0, 6, 2):
dist = bounds[i + 1] - bounds[i]
bounds[i] = bounds[i] - 0.1 * dist
bounds[i + 1] = bounds[i + 1] + 0.1 * dist
"""
vtkVoxelModeller 将任意几何对象(如多边形数据)转换为体素化的三维体积数据
通过计算每个体素点到几何表面的距离场来生成体数据
"""
voxel_modeller = vtkVoxelModeller()
voxel_modeller.SetInputConnection(sphere_source.GetOutputPort())
voxel_modeller.SetModelBounds(bounds)
voxel_modeller.SetSampleDimensions(50, 50, 50)
voxel_modeller.SetScalarTypeToFloat()
"""
SetMaximumDistance 设定体素化过程中距离场的渐变范围的厚度
渐变范围的厚度的意思:
体素化的两种思路:
1)二值化(Binary)模式 每个体素(voxel)的值要么是 1(在几何体内),要么是 0(在几何体外)表面就是 内外的分界线
2) 带渐变的距离场(Signed Distance Field, SDF) 每个体素的值表示 到几何表面的距离,通常内部为负,外部为正 在表面附近的一个“带状区域”里,值会平滑过渡,而不是硬切换
vtkVoxelModeller.SetMaximumDistance(d) 控制的就是这个带状区域的厚度 d
例子:
假设我们有一个球,半径 = 10,SetMaximumDistance(0.5):
在表面上:值 ≈ 0.5 (归一化后 ≈ 0.5)。
表面以内 0.5 个体素厚度的区域:值逐渐减小到 0。
表面以外 0.5 个体素厚度的区域:值逐渐增大到 1。
超过 0.5 的距离后,值就被**截断(clamp)**了,不再渐变,而是固定为 0(内部)或 1(外部)。
这样就形成了一个 宽度 = 1.0(内外各 0.5) 的渐变区域
"""
voxel_modeller.SetMaximumDistance(0.1)
voxel_modeller.Update()
volume.DeepCopy(voxel_modeller.GetOutput())
"""
当你用 vtkFlyingEdges3D(或 vtkMarchingCubes)提取等值面时
0.5 恰好是“几何表面”的位置
< 0.5 是物体内部,> 0.5 是外部
所以 iso_value = 0.5 表示提取出原始几何体的近似表面
"""
iso_value = 0.5
surface = vtkFlyingEdges3D()
surface.SetInputData(volume)
surface.ComputeNormalsOn()
surface.SetValue(0, iso_value)
"""
iso_value 和SetMaximumDistance中的数字的关系
MaximumDistance 控制“场的模糊范围”,相当于“球体表面周围的缓冲区厚度”;
iso_value = 0.5 则是一个固定的“分界线”,永远对应原始几何表面的位置。
换句话说:
MaximumDistance 决定场数据是如何从“表面”向外/向内过渡;
但你提取几何时,仍然要用 0.5 来还原表面。
前者控制场的平滑/模糊厚度 后者是标准化后的等值阈值,固定用 0.5 才能提取原始几何表面
"""
renderer = vtkRenderer()
renderer.SetBackground(colors.GetColor3d('DarkSlateGray'))
render_window = vtkRenderWindow()
render_window.AddRenderer(renderer)
render_window.SetWindowName('MarchingCubes')
interactor = vtkRenderWindowInteractor()
interactor.SetRenderWindow(render_window)
mapper = vtkPolyDataMapper()
mapper.SetInputConnection(surface.GetOutputPort())
mapper.ScalarVisibilityOff()
actor = vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetColor(colors.GetColor3d('MistyRose'))
renderer.AddActor(actor)
render_window.Render()
interactor.Start()
058:SmoothDiscreteMarchingCubes 多边形网格数据的平滑
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonCore import vtkLookupTable, vtkMinimalStandardRandomSequence
from vtkmodules.vtkCommonDataModel import vtkImageData, vtkSphere
from vtkmodules.vtkFiltersCore import vtkWindowedSincPolyDataFilter
from vtkmodules.vtkFiltersGeneral import vtkDiscreteMarchingCubes
from vtkmodules.vtkImagingCore import vtkImageThreshold
from vtkmodules.vtkImagingHybrid import vtkSampleFunction
from vtkmodules.vtkImagingMath import vtkImageMathematics
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkPolyDataMapper,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkRenderer
)
def main():
n = 20
radius = 8
max_r = 50 - 2.0 * radius
blob_image = vtkImageData()
random_sequence = vtkMinimalStandardRandomSequence()
random_sequence.SetSeed(5071)
for i in range(0, n):
sphere = vtkSphere()
x = random_sequence.GetRangeValue(-max_r, max_r)
random_sequence.Next()
y = random_sequence.GetRangeValue(-max_r, max_r)
random_sequence.Next()
z = random_sequence.GetRangeValue(-max_r, max_r)
random_sequence.Next()
sphere.SetCenter(int(x), int(y), int(z))
"""
vtkSampleFunction
创建一个 函数采样器,它会在一个三维规则网格里对隐函数(比如球、平面、布尔组合)进行采样,
生成 vtkImageData 格式的体数据
指定要采样的隐函数,这里是一个 vtkSphere
隐函数的规则是:
点在球面上 → 函数值 = 0
点在球内 → 函数值 < 0
点在球外 → 函数值 > 0
如果设置的隐函数是一个平面呢?
在平面上:值 = 0
在平面的一边:值 = 正数
在另一边:值 = 负数
"""
sampler = vtkSampleFunction()
sampler.SetImplicitFunction(sphere)
sampler.SetOutputScalarTypeToFloat()
sampler.SetModelBounds(-50, 50, -50, 50, -50, 50)
sampler.SetSampleDimensions(100, 100, 100)
thres = vtkImageThreshold()
thres.SetInputConnection(sampler.GetOutputPort())
thres.ThresholdByLower(radius * radius)
"""
SetInValue(i+1) 所有符合阈值条件的(即球体内部的)体素值替换为当前的循环索引 i 加 1
SetOutValue(0):将所有不符合阈值条件的(即球体外部的)体素值替换为0
"""
thres.SetInValue(i + 1)
thres.SetOutValue(0)
"""
ReplaceInOn() 启用对符合阈值条件的体素的替换
ReplaceOutOn() 启用对不符合阈值条件的体素的替换
"""
thres.ReplaceInOn()
thres.ReplaceOutOn()
"""
启用对符合阈值条件的体素的替换
"""
thres.Update()
if i == 0:
blob_image.DeepCopy(thres.GetOutput())
"""
图像的体素级操作
"""
max_value = vtkImageMathematics()
max_value.SetInputData(0, blob_image)
max_value.SetInputData(1, thres.GetOutput())
max_value.SetOperationToMax()
max_value.Modified()
max_value.Update()
blob_image.DeepCopy(max_value.GetOutput())
discrete = vtkDiscreteMarchingCubes()
discrete.SetInputData(blob_image)
discrete.GenerateValues(n, 1, n)
smoothing_iterations = 15
pass_band = 0.01
feature_angle = 120.0
"""
vtkWindowedSincPolyDataFilter 多边形网格的平滑滤波器
普通的平滑算法(比如 vtkSmoothPolyDataFilter,基于 Laplacian 平滑)在迭代多次后,会让模型逐渐 变小,因为顶点会不断往邻居点的“平均位置”收缩。
而 vtkWindowedSincPolyDataFilter 使用频域滤波的思想,通过 Sinc 函数 + 窗口函数来控制平滑程度,使得模型不会过度收缩,同时还能去掉高频噪声
"""
smoother = vtkWindowedSincPolyDataFilter()
smoother.SetInputConnection(discrete.GetOutputPort())
smoother.SetNumberOfIterations(smoothing_iterations)
smoother.BoundarySmoothingOff() # 是否对边界也进行平滑
smoother.SetFeatureAngle(feature_angle)
smoother.FeatureEdgeSmoothingOff() # 是否允许锐利特征边界被平滑
"""
设置滤波器的通带宽度(0~2之间的浮点数),值越小平滑越强
"""
smoother.SetPassBand(pass_band)
"""
NonManifoldSmoothingOn 对非流行网格也进行平滑
"""
smoother.NonManifoldSmoothingOn()
"""
NormalizeCoordinatesOn
启用后,滤波器会 在内部把坐标归一化到一个标准范围([-1,1] 或 [0,1] 之类的范围) 再进行计算,
最后再还原回原始范围
"""
smoother.Update()
lut = vtkLookupTable()
lut.SetNumberOfColors(n)
lut.SetTableRange(0, n - 1)
lut.SetRampToLinear()
lut.Build() # 构建查找表
lut.SetTableValue(0, 0, 0, 0, 1) # 设置索引为0的颜色值为纯黑色+不透明
for i in range(1, n):
r = random_sequence.GetRangeValue(0.4, 1)
random_sequence.Next()
g = random_sequence.GetRangeValue(0.4, 1)
random_sequence.Next()
b = random_sequence.GetRangeValue(0.4, 1)
random_sequence.Next()
lut.SetTableValue(i, r, g, b, 1.0)
mapper = vtkPolyDataMapper()
mapper.SetInputConnection(smoother.GetOutputPort())
mapper.SetLookupTable(lut)
mapper.SetScalarRange(0, lut.GetNumberOfColors())
ren = vtkRenderer()
ren_win = vtkRenderWindow()
ren_win.AddRenderer(ren)
ren_win.SetWindowName('SmoothDiscreteMarchingCubes')
iren = vtkRenderWindowInteractor()
iren.SetRenderWindow(ren_win)
actor = vtkActor()
actor.SetMapper(mapper)
ren.AddActor(actor)
colors = vtkNamedColors()
ren.SetBackground(colors.GetColor3d('Burlywood'))
ren_win.Render()
iren.Start()
if __name__ == '__main__':
main()
059:AlignTwoPolyDatas 基于ICP算法的配准和相机视角切换
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import math
from pathlib import Path
# noinspection PyUnresolvedReferences
import vtkmodules.vtkInteractionStyle
# noinspection PyUnresolvedReferences
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonCore import (
VTK_DOUBLE_MAX,
vtkPoints
)
from vtkmodules.vtkCommonCore import (
VTK_VERSION_NUMBER,
vtkVersion
)
from vtkmodules.vtkCommonDataModel import (
vtkIterativeClosestPointTransform,
vtkPolyData
)
from vtkmodules.vtkCommonTransforms import (
vtkLandmarkTransform,
vtkTransform
)
from vtkmodules.vtkFiltersGeneral import (
vtkOBBTree,
vtkTransformPolyDataFilter
)
from vtkmodules.vtkFiltersModeling import vtkHausdorffDistancePointSetFilter
from vtkmodules.vtkIOGeometry import (
vtkBYUReader,
vtkOBJReader,
vtkSTLReader
)
from vtkmodules.vtkIOLegacy import (
vtkPolyDataReader,
vtkPolyDataWriter
)
from vtkmodules.vtkIOPLY import vtkPLYReader
from vtkmodules.vtkIOXML import vtkXMLPolyDataReader
from vtkmodules.vtkInteractionWidgets import (
vtkCameraOrientationWidget,
vtkOrientationMarkerWidget
)
from vtkmodules.vtkRenderingAnnotation import vtkAxesActor
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkDataSetMapper,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkRenderer
)
def main():
colors = vtkNamedColors()
src_fn = "Data/thingiverse/Grey_Nurse_Shark.stl"
tgt_fn = "Data/greatWhite.stl"
print('Loading source:', src_fn)
source_polydata = read_poly_data(src_fn)
# Save the source polydata in case the alignment process does not improve
# segmentation.
original_source_polydata = vtkPolyData()
original_source_polydata.DeepCopy(source_polydata)
print('Loading target:', tgt_fn)
target_polydata = read_poly_data(tgt_fn)
# If the target orientation is markedly different, you may need to apply a
# transform to orient the target with the source.
# For example, when using Grey_Nurse_Shark.stl as the source and
# greatWhite.stl as the target, you need to transform the target.
trnf = vtkTransform()
if Path(src_fn).name == 'Grey_Nurse_Shark.stl' and Path(tgt_fn).name == 'greatWhite.stl':
trnf.RotateY(90)
tpd = vtkTransformPolyDataFilter()
tpd.SetTransform(trnf)
tpd.SetInputData(target_polydata)
tpd.Update()
renderer = vtkRenderer()
render_window = vtkRenderWindow()
render_window.AddRenderer(renderer)
interactor = vtkRenderWindowInteractor()
interactor.SetRenderWindow(render_window)
"""
对集合 A 中的每个点 a,找到 集合 B 中离它最近的点 b,记录这个最近距离。然后取 这些最近距离中的最大值。
对集合 B 中的每个点 b,同样在 A 中找到最近的点 a,取最大值。
两者的最大值,就是 Hausdorff 距离
"""
distance = vtkHausdorffDistancePointSetFilter()
distance.SetInputData(0, tpd.GetOutput())
distance.SetInputData(1, source_polydata)
distance.Update()
"""
计算Hausdorff距离后,内部会算出最大Hausdorff距离,各点的距离分布,统计信息存在FieldData中
GetFieldData() 取场数据(FieldData,用来存全局统计结果)
GetArray('HausdorffDistance') 获取名字叫 HausdorffDistance 的数组
这个数组通常只有一个值:计算出的 Hausdorff 距离
"""
distance_before_align = distance.GetOutput(0).GetFieldData().GetArray('HausdorffDistance').GetComponent(0, 0)
# Get initial alignment using oriented bounding boxes.
align_bounding_boxes(source_polydata, tpd.GetOutput())
distance.SetInputData(0, tpd.GetOutput())
distance.SetInputData(1, source_polydata)
distance.Modified()
distance.Update()
distance_after_align = distance.GetOutput(0).GetFieldData().GetArray('HausdorffDistance').GetComponent(0, 0)
best_distance = min(distance_before_align, distance_after_align)
if distance_after_align > distance_before_align:
source_polydata.DeepCopy(original_source_polydata)
# Refine the alignment using IterativeClosestPoint.
icp = vtkIterativeClosestPointTransform()
icp.SetSource(source_polydata)
icp.SetTarget(tpd.GetOutput())
"""
SetModeToRigidBody 将你内部用来计算点对齐的变换模式设置为刚体变换
"""
icp.GetLandmarkTransform().SetModeToRigidBody()
icp.SetMaximumNumberOfLandmarks(100)
"""
SetMaximumMeanDistance 设置平均距离的收敛值。
如果源点与目标点之间的平均距离小于这个值,算法就会认为已经收敛,并提前停止迭代
"""
icp.SetMaximumMeanDistance(.00001)
icp.SetMaximumNumberOfIterations(500)
"""
CheckMeanDistanceOn 启用平均距离检查。
ICP 在每次迭代后都检查是否达到了上面设置的 MaximumMeanDistance 阈值
"""
icp.CheckMeanDistanceOn()
"""
StartByMatchingCentroidsOn 启用质心匹配作为初始步骤
在开始迭代之前,ICP 会首先计算源模型和目标模型的质心,并将源模型的质心平移到目标模型的质心位置
"""
icp.StartByMatchingCentroidsOn()
icp.Update()
icp_mean_distance = icp.GetMeanDistance()
# print(icp)
lm_transform = icp.GetLandmarkTransform()
transform = vtkTransformPolyDataFilter()
transform.SetInputData(source_polydata)
"""
SetTransform(icp) vtkIterativeClosestPointTransform 继承自 vtkTransform
它可以直接作为 SetTransform 的参数。这告诉过滤器:“请将ICP 算法最终计算出的所有变换(包括旋转、平移等)应用到源模型上
"""
transform.SetTransform(lm_transform)
transform.SetTransform(icp)
transform.Update()
distance.SetInputData(0, tpd.GetOutput())
distance.SetInputData(1, transform.GetOutput())
distance.Update()
# Note: If there is an error extracting eigenfunctions, then this will be zero.
distance_after_icp = distance.GetOutput(0).GetFieldData().GetArray('HausdorffDistance').GetComponent(0, 0)
# Check if ICP worked.
if not (math.isnan(icp_mean_distance) or math.isinf(icp_mean_distance)):
if distance_after_icp < best_distance:
best_distance = distance_after_icp
print('Distances:')
print(' Before aligning: {:0.5f}'.format(distance_before_align))
print(' Aligning using oriented bounding boxes: {:0.5f}'.format(distance_before_align))
print(' Aligning using IterativeClosestPoint: {:0.5f}'.format(distance_after_icp))
print(' Best distance: {:0.5f}'.format(best_distance))
# Select the source to use.
source_mapper = vtkDataSetMapper()
if best_distance == distance_before_align:
source_mapper.SetInputData(original_source_polydata)
print('Using original alignment')
elif best_distance == distance_after_align:
source_mapper.SetInputData(source_polydata)
print('Using alignment by OBB')
else:
source_mapper.SetInputConnection(transform.GetOutputPort())
print('Using alignment by ICP')
source_mapper.ScalarVisibilityOff()
writer = vtkPolyDataWriter()
if best_distance == distance_before_align:
writer.SetInputData(original_source_polydata)
elif best_distance == distance_after_align:
writer.SetInputData(source_polydata)
else:
writer.SetInputData(transform.GetOutput())
writer.SetFileName('AlignedSource.vtk')
writer.Write()
writer.SetInputData(tpd.GetOutput())
writer.SetFileName('Target.vtk')
writer.Write()
source_actor = vtkActor()
source_actor.SetMapper(source_mapper)
source_actor.GetProperty().SetOpacity(0.6)
source_actor.GetProperty().SetDiffuseColor(
colors.GetColor3d('White'))
renderer.AddActor(source_actor)
target_mapper = vtkDataSetMapper()
target_mapper.SetInputData(tpd.GetOutput())
target_mapper.ScalarVisibilityOff()
target_actor = vtkActor()
target_actor.SetMapper(target_mapper)
target_actor.GetProperty().SetDiffuseColor(
colors.GetColor3d('Tomato'))
renderer.AddActor(target_actor)
render_window.AddRenderer(renderer)
renderer.SetBackground(colors.GetColor3d("sea_green_light"))
renderer.UseHiddenLineRemovalOn()
if vtk_version_ok(9, 0, 20210718):
try:
cam_orient_manipulator = vtkCameraOrientationWidget()
cam_orient_manipulator.SetParentRenderer(renderer)
# Enable the widget.
cam_orient_manipulator.On()
except AttributeError:
pass
else:
axes = vtkAxesActor()
widget = vtkOrientationMarkerWidget()
rgba = [0.0, 0.0, 0.0, 0.0]
colors.GetColor("Carrot", rgba)
widget.SetOutlineColor(rgba[0], rgba[1], rgba[2])
widget.SetOrientationMarker(axes)
widget.SetInteractor(interactor)
widget.SetViewport(0.0, 0.0, 0.2, 0.2)
widget.EnabledOn()
widget.InteractiveOn()
render_window.SetSize(640, 480)
render_window.Render()
render_window.SetWindowName('AlignTwoPolyDatas')
interactor.Start()
def vtk_version_ok(major, minor, build):
"""
Check the VTK version.
:param major: Major version.
:param minor: Minor version.
:param build: Build version.
:return: True if the requested VTK version is greater or equal to the actual VTK version.
"""
needed_version = 10000000000 * int(major) + 100000000 * int(minor) + int(build)
try:
vtk_version_number = VTK_VERSION_NUMBER
except AttributeError: # as error:
ver = vtkVersion()
vtk_version_number = 10000000000 * ver.GetVTKMajorVersion() + 100000000 * ver.GetVTKMinorVersion() \
+ ver.GetVTKBuildVersion()
if vtk_version_number >= needed_version:
return True
else:
return False
def read_poly_data(file_name):
import os
path, extension = os.path.splitext(file_name)
extension = extension.lower()
if extension == ".ply":
reader = vtkPLYReader()
reader.SetFileName(file_name)
reader.Update()
poly_data = reader.GetOutput()
elif extension == ".vtp":
reader = vtkXMLPolyDataReader()
reader.SetFileName(file_name)
reader.Update()
poly_data = reader.GetOutput()
elif extension == ".obj":
reader = vtkOBJReader()
reader.SetFileName(file_name)
reader.Update()
poly_data = reader.GetOutput()
elif extension == ".stl":
reader = vtkSTLReader()
reader.SetFileName(file_name)
reader.Update()
poly_data = reader.GetOutput()
elif extension == ".vtk":
reader = vtkPolyDataReader()
reader.SetFileName(file_name)
reader.Update()
poly_data = reader.GetOutput()
elif extension == ".g":
reader = vtkBYUReader()
reader.SetGeometryFileName(file_name)
reader.Update()
poly_data = reader.GetOutput()
else:
# Return a None if the extension is unknown.
poly_data = None
return poly_data
def align_bounding_boxes(source, target):
# Use OBBTree to create an oriented bounding box for target and source
"""
vtkOBBtree
tkOBBTree 是一种 空间加速结构,它会递归地把几何体拆分成 包围盒(OBB)层次结构
MaxLevel 控制这个树的 最大递归深度
这里设置成1,意思是:
只构建 根节点 + 一层子节点 的 OBB 树
层数越小 → 树越“粗糙”,速度快但精度低
层数越大 → 树越“细致”,适合做精确的碰撞检测或最近点查询
"""
source_obb_tree = vtkOBBTree()
source_obb_tree.SetDataSet(source)
source_obb_tree.SetMaxLevel(1)
source_obb_tree.BuildLocator()
target_obb_tree = vtkOBBTree()
target_obb_tree.SetDataSet(target)
target_obb_tree.SetMaxLevel(1)
target_obb_tree.BuildLocator()
"""
BuildLocator 真正构建 OBB 树的层次结构
在设置了数据集(SetDataSet)和参数(SetMaxLevel)之后,你必须调用它来“编译”出树,
才能进行后续查询(比如射线相交、碰撞检测、最近点搜索)
"""
source_landmarks = vtkPolyData()
source_obb_tree.GenerateRepresentation(0, source_landmarks)
"""
GenerateRepresentation 生成包围盒的几何形状
0:这个参数指定了要生成的表示层级。vtkOBBTree 是一种分层的包围盒,0 表示最高层级,也就是整个模型的最外层包围盒
source_landmarks:GenerateRepresentation 方法会把生成的8个顶点和12条边(一个六面体)的几何数据填充到 source_landmarks 这个 vtkPolyData 对象中
"""
target_landmarks = vtkPolyData()
target_obb_tree.GenerateRepresentation(0, target_landmarks)
"""
vtkLandmarkTransform 是 VTK 里一个 点集配准(registration) 的基础类,
它根据两组 对应点(landmarks) 来计算一个最佳的变换矩阵(旋转、平移、可选缩放)
它需要两组对应点:源点集(Source Landmarks)和 目标点集(Target Landmarks)
"""
lm_transform = vtkLandmarkTransform()
lm_transform.SetModeToSimilarity()
"""
LandmarkTransform 支持 3 种模式
RigidBody(刚体变换) 只允许旋转 + 平移(保持形状和大小完全不变)
Similarity(相似变换) 允许旋转 + 平移 + 缩放(各向同性缩放,保持比例)
Affine(仿射变换) 允许旋转 + 平移 + 缩放 + 剪切(最灵活,但可能改变形状)
这里用 Similarity 模式,说明希望计算的变换能 匹配位置和大小,但不允许形状变形
"""
lm_transform.SetTargetLandmarks(target_landmarks.GetPoints())
best_distance = VTK_DOUBLE_MAX
best_points = vtkPoints()
best_distance = best_bounding_box(
"X",
target,
source,
target_landmarks,
source_landmarks,
best_distance,
best_points)
best_distance = best_bounding_box(
"Y",
target,
source,
target_landmarks,
source_landmarks,
best_distance,
best_points)
best_distance = best_bounding_box(
"Z",
target,
source,
target_landmarks,
source_landmarks,
best_distance,
best_points)
lm_transform.SetSourceLandmarks(best_points)
lm_transform.Modified()
lm_transform_pd = vtkTransformPolyDataFilter()
lm_transform_pd.SetInputData(source)
lm_transform_pd.SetTransform(lm_transform)
lm_transform_pd.Update()
source.DeepCopy(lm_transform_pd.GetOutput())
return
def best_bounding_box(axis, target, source, target_landmarks, source_landmarks, best_distance, best_points):
"""
这个函数的目的在于在粗略对齐阶段,
通过系统性地测试不同旋转角度,来找到源模型和目标模型之间最佳的初始对齐
"""
distance = vtkHausdorffDistancePointSetFilter()
test_transform = vtkTransform()
"""
vtkTransformPolyDataFilter 是将一个 vtkPolyData 数据集应用几何变换,然后生成一个新的、已变换的 vtkPolyData 对象
"""
test_transform_pd = vtkTransformPolyDataFilter()
"""
vtkLandmarkTransform 用于两个点集的配准
是vtkTransform的子类,可以根据源点集和目标点击来计算一个最佳的变换矩阵
"""
lm_transform = vtkLandmarkTransform()
lm_transform.SetModeToSimilarity()
lm_transform.SetTargetLandmarks(target_landmarks.GetPoints())
lm_transform_pd = vtkTransformPolyDataFilter()
source_center = source_landmarks.GetCenter()
delta = 90.0
for i in range(0, 4):
angle = delta * i
# Rotate about center
test_transform.Identity()
test_transform.Translate(source_center[0], source_center[1], source_center[2])
if axis == "X":
test_transform.RotateX(angle)
elif axis == "Y":
test_transform.RotateY(angle)
else:
test_transform.RotateZ(angle)
test_transform.Translate(-source_center[0], -source_center[1], -source_center[2])
test_transform_pd.SetTransform(test_transform)
test_transform_pd.SetInputData(source_landmarks)
test_transform_pd.Update()
lm_transform.SetSourceLandmarks(test_transform_pd.GetOutput().GetPoints())
lm_transform.Modified()
lm_transform_pd.SetInputData(source)
lm_transform_pd.SetTransform(lm_transform)
lm_transform_pd.Update()
distance.SetInputData(0, target)
distance.SetInputData(1, lm_transform_pd.GetOutput())
distance.Update()
test_distance = distance.GetOutput(0).GetFieldData().GetArray("HausdorffDistance").GetComponent(0, 0)
if test_distance < best_distance:
best_distance = test_distance
best_points.DeepCopy(test_transform_pd.GetOutput().GetPoints())
return best_distance
if __name__ == '__main__':
main()
三:核心概念讲解
(1)大部分示例代码的逻辑流程
(2)易混淆概念比对
1)GetOutput和GetOutputPort
GetOutput() 返回的是一个已经生成的 数据对象(比如 vtkPolyData、vtkImageData 等).本质上是reader/filter的输出数据本身。用法:当你只是想直接获取 mesh 数据对象(比如拿去做数值计算、保存、或者在 Python 里直接操作),用 GetOutput()。
GetOutputPort() 返回一个输出端口对象(vtkAlgorithmOutput)。本质:它不是数据,而是一个 管线连接点,用于把当前 filter/reader 的输出连接到另一个 filter/mapper 的输入不包含数据本身,而是一个数据流的“句柄”。用法:当你要把 reader/filter 和下游的 filter/mapper 连起来时,必须用 GetOutputPort()。
SetInputConnection(source.GetOutputPort()):这是VTK中首选的连接管道的方式。它连接的是数据源的输出端口,而不是数据本身。当管道中的下游对象(例如 mapper)需要数据时,它会向 shrink 过滤器请求,shrink 过滤器再向 source 请求,这个请求会触发 source 的 Update() 方法,从而生成数据。这个过程是自动的。
SetInputData(source.GetOutput()):这个方法直接将一个已经存在的数据对象作为输入。但问题在于,source.GetOutput() 返回的数据对象可能根本没有数据。如果你不手动调用 source.Update(),数据源就不会执行任何计算,导致 shrink 过滤器接收到的是一个空的数据集
2)Render的SetBackground()和SetBackground2()的区别
renderer.SetBackground设置 渲染器背景颜色(单一颜色)。参数是 RGB 值(范围通常是 0~1)。效果就是整个窗口背景是一个纯色。 渐变的起始颜色(通常是窗口底部或左侧)
renderer.SetBackground2(r, g, b)设置 渐变背景的第二种颜色。但要注意:仅仅调用 .SetBackground2不会生效,因为默认背景是单色。必须同时启用renderer.GradientBackgroundOn()。这样才会出现从 SetBackground() → SetBackground2() 的渐变。渐变的结束颜色(通常是窗口顶部或右侧)
renderer.SetBackground(0.1, 0.2, 0.4) # 深蓝
renderer.SetBackground2(0.8, 0.8, 0.8) # 浅灰
renderer.GradientBackgroundOn()
3)reader中UpdateInformation()和Update()的区别
eader.UpdateInformation() 和 reader.Update() 都是 VTK 数据管道中的核心方法,但它们在数据处理流中的作用和时机完全不同。
reader.UpdateInformation()
这个方法的作用是更新数据源的元数据(metadata)。它做了什么? 当你调用 reader.SetFileName() 设置好文件路径后,reader.UpdateInformation() 会去读取文件的头部信息,但不加载任何实际的数据。这些信息包括:数据的维度(例如,图像的宽度、高度、深度)。数据的类型(例如,char、float)。数据的范围(例如,图像的像素值范围)。数据的空间范围(例如,数据的 x, y, z 坐标范围)。
何时使用? 当你需要知道数据的基本属性(比如图像大小)来设置后续处理参数时,会使用它。例如,在调整窗口大小或创建数据数组之前。
reader.Update()
这个方法的作用是执行整个管道,加载所有数据并完成处理。它做了什么? 当你调用 reader.Update() 时,VTK 会执行整个可视化管线中所有待处理的过滤器和数据源,直到数据准备就绪。它会触发以下操作首先,如果元数据没有更新,它会先调用 UpdateInformation()。然后,它会读取文件中的所有数据。最后,它会执行所有与该数据源相连的下游过滤器。
何时使用? 当你真正需要使用数据(例如,将数据传递给渲染器或另一个过滤器进行处理)时,就需要调用它。
区别与联系
方法 作用 触发操作 效率
UpdateInformation() 更新元数据 只读取文件头信息 极快
Update() 更新所有数据 读取整个文件,并执行所有下游过滤器 较慢,取决于数据量
联系:Update() 包含了 UpdateInformation() 的功能。如果你直接调用 Update(),它会自动检查元数据是否需要更新。
区别:UpdateInformation() 是一个轻量级的操作,只为了获取信息。而 Update() 是一个重量级的操作,它会执行所有计算和数据加载。
简而言之,UpdateInformation() 就像是查看目录,而 Update() 则是阅读整本书
4)vtkNamedColors类中各种GetColor的区别
GetColor3d() 和 GetColor4d()
GetColor3d(): 返回一个包含三个 double 类型浮点数的列表或元组,代表R、G、B分量。每个分量的取值范围是 [0.0, 1.0]
GetColor4d(): 返回一个包含四个 double 类型浮点数的列表或元组,代表R、G、B、A(透明度)分量。每个分量的取值范围也是 [0.0, 1.0]。
GetColor3ub() 和 GetColor4ub()
GetColor3ub(): 返回一个包含三个 unsigned char(无符号字符)类型整数的列表或元组,代表R、G、B分量。每个分量的取值范围是 [0, 255]。
GetColor4ub(): 返回一个包含四个 unsigned char 类型整数的列表或元组,代表R、G、B、A分量。每个分量的取值范围也是 [0, 255]。
GetColor()
GetColor() 是一个通用的方法,它返回一个 vtkColor3d 或 vtkColor4d 对象。这些对象可以被视为 GetColor3d() 或 GetColor4d() 的面向对象版本。
如果只传入颜色名称,它返回一个3D颜色对象,等价于 GetColor3d()。
如果颜色名称是类似 "rgba(r,g,b,a)" 的格式,它则返回一个4D颜色对象。
5)插入点的方法InsertNextPoint、InsertPoint、InsertPoints和SetData
points = vtkPoints()
points.InsertNextPoint()
最常用的方法,用于在 vtkPoints 容器的末尾添加一个新点。在当前点的末尾追加一个新点,并返回新点的索引。
points.InsertPoint()
在指定的索引位置插入一个新点。在 id 索引处插入一个新点。如果 id 索引处已经有数据,这个方法会替换它;如果 id 索引处是空位,它会填充这个空位。
points.InsertPoints()
将另一个 vtkPoints 对象中的多个点一次性插入到当前对象中。将一个源 vtkPoints 对象中的所有点,从当前 vtkPoints 对象的 id 索引处开始插入
points.SetData()
是一次性将一个完整的数据数组直接赋值给 vtkPoints 对象。
vtk_data_array = numpy_to_vtk(numpy_array)
points = vtkPoints()
points.SetData(vtk_data_array)
6)vtkIntArray中的SetNumberOfTuples和SetNumberOfValues
idArray.SetNumberOfValues(num)
作用: 这个方法会直接设置数组中值的总数。它会将数组的大小调整为 num,并分配相应的内存。
概念: 这里的“值”指的就是数组中的每一个单独的整数。
如果你想创建一个包含 100 个整数的数组,通常会使用 SetNumberOfValues(100)。
idArray.SetNumberOfTuples(num)
作用: 这个方法设置的是数组中元组(tuple)的总数。它也会调整数组的大小并分配内存。
概念: 在 VTK 的数据模型中,“元组”是一个更通用的概念。
一个元组可以包含一个或多个值(分量)。例如,一个颜色元组可能包含3个分量(红、绿、蓝),
而一个三维向量元组则包含3个分量(x, y, z)。
对于 vtkIntArray 来说,如果它每个元组只包含一个值(这是常见情况),
那么 SetNumberOfTuples(100) 和 SetNumberOfValues(100) 的效果是一样的。
总结
如果你处理的是一个单一分量的数组(比如每个单元格一个 ID,或者每个点一个标量值),那么 SetNumberOfTuples() 和 SetNumberOfValues() 没有功能上的区别,你可以互换使用。
如果你处理的是一个多于一个分量的数组(比如每个点一个 RGB 颜色值),那么你需要先用 SetNumberOfComponents() 设置分量数,然后用 SetNumberOfTuples() 设置元组数。在这种情况下,SetNumberOfValues() 就不太适用了。
7)vtkIntArray中的InsertTuple,InsertTuple1,InsertTuple2等区别
它们之间的主要区别在于一次插入的数据分量(component)数量
InsertTuple1(tupleId, value1)
向数组的指定位置 tupleId 插入一个一维元组,也就是一个单一的值
InsertTuple(tupleId, tuple_data)
这是一个通用的方法,用于插入一个多维元组。它需要你提供一个 Python 列表或元组作为数据
InsertTuple2(tupleId, value1, value2)
这是一个针对二维元组的专用方法。value1, value2是插入的分量值
以此类推...InsertTuple3():用于三维元组,例如 InsertTuple3(5, 1.0, 2.0, 3.0)
8)vtkPolyData中不同拓扑关系存储的区别
SetVerts(vtkCellArray*)
只用来存储顶点的索引。即:只有单个点的单元(VTK_VERTEX 或 VTK_POLY_VERTEX)。用途:表示点云(点集),例如散点分布。每个 cell 里可能只有一个点,或者多个点(VTK_POLY_VERTEX)
SetLines(vtkCellArray*)
存储线单元。包括VTK_LINE,VTK_POLY_LINE。例如 2 个点组成一条直线,或者一堆点连起来组成折线
SetPolys(vtkCellArray*)
存储面片单元 (polygons)。包括三角形、多边形 (VTK_TRIANGLE, VTK_POLYGON 等)。
SetLinks(vtkCellLinks*)
建立点和单元的反向索引。加速查询和拓扑运算,比如需要快速找到“某个点连接到哪些单元”。
SetStrips(vtkCellArray*)
存“连续的三角带”
9)GetPointData().GetAbstractArray 和 GetPointData().GetArray的区别
GetArray(name_or_index) 返回类型:vtkDataArray(或其子类)
vtkDataArray 是数值数组,专门存 数值型标量/向量/张量,比如 float、double、int
也就是说,GetArray() 只适用于 数值数组,对非数值的数组(例如字符串)会失败或者返回 None
GetAbstractArray(name_or_index) 返回类型:vtkAbstractArray
vtkAbstractArray 是 vtkDataArray 的基类,它更泛化,可以表示 任意类型的数组,不仅仅是数值型。数值数组:vtkDoubleArray、vtkFloatArray … 字符串数组:vtkStringArray 稀疏数组等其他类型。适合在不确定数组类型 时使用,比如不知道是数值还是字符串
1578

被折叠的 条评论
为什么被折叠?



