【VTK+增材CAM】模型切片

目的

模型切片在3D打印喷头路径规划中是非常重要的一环。实现切片的算法有很多,主流的思想是平面与模型三角面片求交得到相交的线段,再将这些线段组织连接成折线轮廓,以便接下来进行切片层内的路径规划。下文将使用VTK简单实现模型Z轴方向的平面切片。

关键函数

vtkCutte是一个用于剪切数据的过滤器。在代码中用于得到平面和模型面片求交得到的线段。

1
2
3
4
5
cutter = vtk.vtkCutter()
cutter.SetInputData(polydata)
cutter.SetCutFunction(plane)
cutter.SetSortByToSortByCell() # 按切片层数进行排序
cutter.SetValue(i, z) # 设置轮廓的值

vtkStripper是一个用于从输入线段生成折线的过滤器。在代码中用于将散乱的线段组织连接起来成为折线轮廓。

1
2
3
4
stripper = vtk.vtkStripper()
stripper.SetInputConnection(cutter.GetOutputPort())
stripper.JoinContiguousSegmentsOn() # 将输出的折线首尾相连成封闭轮廓
stripper.Update()

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import vtk


def slice_vtk(polydata, layerThk):
"""slice model with VTK into layers

:param polydata: vtkPolyData,输入模型
:param layerThk: float,层高
:return: 切片列表
"""
# 平面相交切片
plane = vtk.vtkPlane()
cutter = vtk.vtkCutter()
cutter.SetInputData(polydata)
cutter.SetCutFunction(plane)
cutter.SetSortByToSortByCell() # 按切片层数进行排序
_, _, _, _, zMin, zMax = polydata.GetBounds()
i, z = 0, zMin + layerThk
while z < zMax:
cutter.SetValue(i, z)
z += layerThk
i += 1
# 提取轮廓
stripper = vtk.vtkStripper()
stripper.SetInputConnection(cutter.GetOutputPort())
stripper.JoinContiguousSegmentsOn()
stripper.Update()

# numberOfLines = stripper.GetOutput().GetNumberOfLines()
points = stripper.GetOutput().GetPoints()
cells = stripper.GetOutput().GetLines()
cells.InitTraversal()
indices = vtk.vtkIdList()
lineCount = 0
contours = []
while cells.GetNextCell(indices):
# print("Line {0}:".format(lineCount))
contour = []
for i in range(indices.GetNumberOfIds()):
point = points.GetPoint(indices.GetId(i))
contour.append((point[0], point[1], point[2]))
# print("\t({0:0.6f} ,{1:0.6f}, {2:0.6f})".format(point[0], point[1], point[2]))
contours.append(contour)
lineCount += 1

return stripper, contours2Layers(contours)


def contours2Layers(contours):
layers = []
for contour in contours:
layer = next((c for c in layers if (contour[0][2] == c[0][0][2])), [])
if not layer:
layers.append(layer)
layer.append(contour)
return layers


def main():
colors = vtk.vtkNamedColors()
lineColor = colors.GetColor3d("black")
modelColor = colors.GetColor3d("silver")
backgroundColor = colors.GetColor3d("wheat")

# 加载STL模型
reader = vtk.vtkSTLReader()
reader.SetFileName(modelFileName)
reader.Update()

stripper, layers = slice_vtk(reader.GetOutput(), 2)

modelMapper = vtk.vtkPolyDataMapper()
modelMapper.SetInputConnection(reader.GetOutputPort())

model = vtk.vtkActor()
model.SetMapper(modelMapper)
model.GetProperty().SetDiffuseColor(modelColor)
model.GetProperty().SetInterpolationToFlat()

linesMapper = vtk.vtkPolyDataMapper()
linesMapper.SetInputConnection(stripper.GetOutputPort())

lines = vtk.vtkActor()
lines.SetMapper(linesMapper)
lines.GetProperty().SetDiffuseColor(lineColor)
lines.GetProperty().SetLineWidth(3.)

renderer = vtk.vtkRenderer()
renderWindow = vtk.vtkRenderWindow()

renderWindow.AddRenderer(renderer)
renderWindow.SetSize(640, 480)

interactor = vtk.vtkRenderWindowInteractor()
interactor.SetRenderWindow(renderWindow)

# Add the actors to the renderer.
renderer.AddActor(model)
renderer.AddActor(lines)
renderer.SetBackground(backgroundColor)

# This starts the event loop and as a side effect causes an
# initial render.
renderWindow.Render()
interactor.Start()


if __name__ == "__main__":
main()

在上面代码中,为了将得到的轮廓按照所属切片层进行归类管理,使用如下函数进行实现。

1
2
3
4
5
6
7
8
def contours2Layers(contours):
layers = []
for contour in contours:
layer = next((c for c in layers if (contour[0][2] == c[0][0][2])), [])
if not layer:
layers.append(layer)
layer.append(contour)
return layers

测试结果

image-20221201131742669

总结

对于平面切片计算,VTK官方文档中提到了vtkPlaneCutter。vtkPlaneCutter专门用于平面切割,相较于vtkCutter速度更快,且是多线程。为了提高切片算法效率,后期可尝试vtkPlaneCutter。