"Соединяйте" актеров, удаляя перекрытия или "заполняя" трюк. Обнаружение столкновения
Я пытаюсь визуализировать структурную раму с полым цилиндрическим поперечным сечением (позже я хотел бы обобщить поперечное сечение, но пока это будет сделано), используя VTK версии 8.1.0 и Python. Отказ от ответственности, я довольно новичок в программировании на VTK и Python.
Проблема в том, что я получаю пересекающихся актеров, что, честно говоря, очевидно. Как показано на рисунке 1.
Я хотел бы избавиться от этого и фактически "связать" соседних актеров. Я думаю, что в первую очередь мне нужна какая-то форма обнаружения столкновений. Затем я рассмотрел вопрос об экстраполяции одного из действующих лиц внутри другого, а затем обрезке оставшихся с помощью плоскости, которую, я надеюсь, смогу создать из пересечения между действующими лицами, как объяснено в этом примере: пересечение между действующими лицами.
Я также подумал, что на самом деле можно "выдавливать на поверхность", но я не знаю, выполнимо ли это в ВТК, но я просматриваю столько примеров, сколько смогу найти.
Итак, прежде всего, какие идеи я думал о реалистичных? Если выдавливание выполнимо, может кто-нибудь подсказать мне, как начать?
Если ни одна из идей не годится для использования, у кого-нибудь из вас есть другая идея, как этого добиться?
Я создал минимальный фрагмент кода, который показывает проблему, которую я хотел бы исправить:
import vtk
def main():
colors = vtk.vtkNamedColors()
disk = vtk.vtkDiskSource()
disk.SetCircumferentialResolution(128)
disk.SetRadialResolution(1)
disk.SetOuterRadius(2)
disk.SetInnerRadius(2 - 0.1)
extrude_disk = vtk.vtkLinearExtrusionFilter()
extrude_disk.SetInputConnection(disk.GetOutputPort())
extrude_disk.SetExtrusionTypeToNormalExtrusion()
extrude_disk.SetVector(0, 0, 1)
extrude_disk.SetScaleFactor(1)
extrude_disk.Update()
disk2 = vtk.vtkDiskSource()
disk2.SetCircumferentialResolution(128)
disk2.SetRadialResolution(1)
disk2.SetOuterRadius(1.5)
disk2.SetInnerRadius(1.5 - 0.1)
extrude_disk2 = vtk.vtkLinearExtrusionFilter()
extrude_disk2.SetInputConnection(disk2.GetOutputPort())
extrude_disk2.SetExtrusionTypeToNormalExtrusion()
extrude_disk2.SetVector(0, 0, 1)
extrude_disk2.SetScaleFactor(1)
extrude_disk2.Update()
start_point = [0] * 3
start_point2 = [-10, 0, 0]
end_point = [0, 0, 10]
end_point2 = [0, 0, 7]
# Computing a basis
normalized_x = [0] * 3
normalized_x2 = [0] * 3
normalized_y = [0] * 3
normalized_y2 = [0] * 3
normalized_z = [0] * 3
normalized_z2 = [0] * 3
# The X axis is a vector from start to end
vtk.vtkMath.Subtract(end_point, start_point, normalized_x)
vtk.vtkMath.Subtract(end_point2, start_point2, normalized_x2)
length = vtk.vtkMath.Norm(normalized_x)
length2 = vtk.vtkMath.Norm(normalized_x2)
vtk.vtkMath.Normalize(normalized_x)
vtk.vtkMath.Normalize(normalized_x2)
# The Z axis is an arbitrary vector cross X
rng = vtk.vtkMinimalStandardRandomSequence()
rng.SetSeed(8775070) # For testing.
arbitrary = [0] * 3
for i in range(0, 3):
rng.Next()
arbitrary[i] = rng.GetRangeValue(-10, 10)
vtk.vtkMath.Cross(normalized_x, arbitrary, normalized_z)
vtk.vtkMath.Cross(normalized_x2, arbitrary, normalized_z2)
vtk.vtkMath.Normalize(normalized_z)
vtk.vtkMath.Normalize(normalized_z2)
# The Y axis is Z cross X
vtk.vtkMath.Cross(normalized_z, normalized_x, normalized_y)
vtk.vtkMath.Cross(normalized_z2, normalized_x2, normalized_y2)
matrix = vtk.vtkMatrix4x4()
matrix2 = vtk.vtkMatrix4x4()
# Create the direction cosine matrix
matrix.Identity()
matrix2.Identity()
for i in range(3):
matrix.SetElement(i, 0, normalized_x[i])
matrix2.SetElement(i, 0, normalized_x2[i])
matrix.SetElement(i, 1, normalized_y[i])
matrix2.SetElement(i, 1, normalized_y2[i])
matrix.SetElement(i, 2, normalized_z[i])
matrix2.SetElement(i, 2, normalized_z2[i])
# Apply the transforms
transform = vtk.vtkTransform()
transform.Translate(start_point) # translate to starting point
transform.Concatenate(matrix) # apply direction cosines
transform.RotateY(90.0) # align cylinder
transform.Scale(1.0, 1.0, length) # scale along the height vector
transform2 = vtk.vtkTransform()
transform2.Translate(start_point2) # translate to starting point
transform2.Concatenate(matrix2) # apply direction cosines
transform2.RotateY(90.0) # align cylinder
transform2.Scale(1.0, 1.0, length2) # scale along the height vector
# Create a mapper and actor for the disks
mapper = vtk.vtkPolyDataMapper()
mapper.SetInputConnection(extrude_disk.GetOutputPort())
actor = vtk.vtkActor()
actor.SetUserMatrix(transform.GetMatrix())
actor.SetMapper(mapper)
actor.GetProperty().SetColor(colors.GetColor3d("yellow"))
mapper2 = vtk.vtkPolyDataMapper()
mapper2.SetInputConnection(extrude_disk2.GetOutputPort())
actor2 = vtk.vtkActor()
actor2.SetUserMatrix(transform2.GetMatrix())
actor2.SetMapper(mapper2)
actor2.GetProperty().SetColor(colors.GetColor3d("yellow"))
# Create a renderer, render window, and interactor
renderer = vtk.vtkRenderer()
render_window = vtk.vtkRenderWindow()
render_window.AddRenderer(renderer)
render_window.SetWindowName("Overlapping cylinders example")
render_window_interactor = vtk.vtkRenderWindowInteractor()
render_window_interactor.SetRenderWindow(render_window)
# Add the actors to the scene
renderer.AddActor(actor)
renderer.AddActor(actor2)
renderer.SetBackground(colors.GetColor3d("BkgColor"))
# Render and interact
render_window.Render()
render_window_interactor.Start()
if __name__ == '__main__':
main()
Это конечный результат, который я ищу! просто без актеров, пересекающихся / пересекающихся / сталкивающихся, я не знаю правильного термина для поведения, которое я извиняюсь, как они делают в примере, который я имею здесь.
Я надеюсь, что у кого-то есть идеи для этой проблемы. Заранее спасибо!
С наилучшими пожеланиями, Мартин
1 ответ
Позвольте мне начать с небольшого уточнения. Актеры в VTK - это простые контейнерные объекты, которые объединяют геометрические данные и свойства визуализации для рендеринга. ВТК не предлагает большой функциональности на уровне актеров. Если вы хотите обнаружить сталкивающиеся объекты, вам нужно решить это для геометрии (vtkPolyData
).
В VTK нет универсального двигателя обнаружения столкновений или пересечения кузова. В VTK вы можете применять логические операции. Однако этого нелегко достичь. Есть два основных подхода:
Метод A: Булевы операции над сеткой
использование [vtkBooleanOperationPolyDataFilter][1]
применять логические операции непосредственно к сетке. Смотрите здесь для примера. К сожалению, это не удастся в вашем случае из-за свойств сетки ваших поверхностей (нажатие клавиши W
когда вы смотрите на поверхность в RenderWindow, чтобы проверить каркас вашей сетки). vtkBooleanOperationPolyDataFilter
будет работать лучше всего, если треугольники меша маленькие и имеют приличное число условий, то есть, если треугольники не слишком колючие. (См. Руководство к набору инструментов Verdict для некоторых метрик треугольника.) Однако сетка ваших вытесненных дисков состоит из очень длинных колючих треугольников. То, что вам нужно будет сделать, это сначала очистить поверхность. VTK не предлагает возможности перекомпоновки из коробки, но связанные наборы инструментов, такие как VMTK, делают.
Получить подход (A) правильно для общих геометрий сложно, потому что вы можете в конечном итоге получить немногообразующие или негерметичные поверхности. Также, vtkBooleanOperationPolyDataFilter
Известно, что есть некоторые ошибки (см. здесь или здесь). Будем надеяться, что когда-нибудь эти проблемы будут исправлены.
Метод B: Булевы операции над неявными функциями
Второй подход заключается в работе с неявными функциями. Например, вы можете представить свои трубы как неявные цилиндры и пересечь их, используя [vtkImplicitBoolean][7]
, Смотрите здесь для примера. Проблема этого подхода заключается в том, что вам нужно преобразовать неявное представление объектов, чтобы получить результирующую поверхностную сетку. Алгоритм марширующих кубов в VTK довольно медленный, поэтому вам нужно долго ждать высоких разрешений. И сохранить острые края невозможно. С другой стороны, он более надежен и с ним легче иметь дело.
Образец кода
Код ниже демонстрирует оба случая. Я не могу поделиться с вами здесь функцией переопределения, поэтому функционирует только неявный логический тип. На скриншоте показано, как выглядит результат. (Желтый: входные поверхности, красный: результат)
Для получения более подробной информации о терминологии и альтернативных формулировках проблемы см. "Обнаружение столкновений между геометрическими моделями: опрос", Ming and Gottschalk, 1999.
Булева операция над неявными функциями
# This code has been written by normanius under the CC BY-SA 4.0 license.
# License: https://creativecommons.org/licenses/by-sa/4.0/
# Author: https://stackru.com/users/3388962/normanius
# Date: July 2018
import vtk
import numpy as np
def compute_transform(start, end):
# Better compute the matrix in numpy!
normalized_x = [0]*3
normalized_y = [0]*3
normalized_z = [0]*3
# The X axis is a vector from start to end
vtk.vtkMath.Subtract(end, start, normalized_x)
length = vtk.vtkMath.Norm(normalized_x)
vtk.vtkMath.Normalize(normalized_x)
# The Z axis is an arbitrary vector cross X
rng = vtk.vtkMinimalStandardRandomSequence()
rng.SetSeed(8775070) # For testing.
arbitrary = [0]*3
for i in range(0, 3):
rng.Next()
arbitrary[i] = rng.GetRangeValue(-10, 10)
vtk.vtkMath.Cross(normalized_x, arbitrary, normalized_z)
vtk.vtkMath.Normalize(normalized_z)
# The Y axis is Z cross X
vtk.vtkMath.Cross(normalized_z, normalized_x, normalized_y)
matrix = vtk.vtkMatrix4x4()
# Create the direction cosine matrix
matrix.Identity()
for i in range(3):
matrix.SetElement(i, 0, normalized_x[i])
matrix.SetElement(i, 1, normalized_y[i])
matrix.SetElement(i, 2, normalized_z[i])
transform = vtk.vtkTransform()
transform.Translate(start) # translate to starting point
transform.Concatenate(matrix) # apply direction cosines
transform.RotateY(90.0) # align cylinder
# Don't scale! This changes mesh properties (e.g. aspect ratio)
#transform.Scale(1.0, 1.0, length) # scale along the height vector
return transform
def transform_item(item, transform):
transformed = vtk.vtkTransformPolyDataFilter()
transformed.SetInputConnection(item.GetOutputPort())
transformed.SetTransform(transform)
transformed.Update()
return transformed
def create_pipe(radius, thickness, height):
# This type of pipe is not suited for remeshing, because remeshing does not
# preserve (feature-) edges. See create_pipe2
assert(radius>thickness)
disk = vtk.vtkDiskSource()
disk.SetCircumferentialResolution(128)
disk.SetRadialResolution(1)
disk.SetOuterRadius(radius)
disk.SetInnerRadius(radius - thickness)
pipe = vtk.vtkLinearExtrusionFilter()
pipe.SetInputConnection(disk.GetOutputPort())
pipe.SetExtrusionTypeToNormalExtrusion()
pipe.SetVector(0, 0, 1)
pipe.SetScaleFactor(height)
pipe.Update()
return pipe
def create_pipe_implicit(radius, thickness, height):
center = np.array([0,0,0])
axis = np.array([0,0,1])
centerTop = center + height*axis
centerBottom = center
# Outer cylinder.
outer = vtk.vtkCylinder()
outer.SetCenter(center)
outer.SetAxis(axis)
outer.SetRadius(radius)
# Inner cylinder.
inner = vtk.vtkCylinder()
inner.SetCenter(center)
inner.SetAxis(axis)
inner.SetRadius(radius-thickness)
# Top face.
plane1 = vtk.vtkPlane()
plane1.SetOrigin(centerTop)
plane1.SetNormal(np.array(outer.GetAxis()))
# Bottom face.
plane2 = vtk.vtkPlane()
plane2.SetOrigin(centerBottom)
plane2.SetNormal(-np.array(outer.GetAxis()))
# Put things together.
difference = vtk.vtkImplicitBoolean()
difference.AddFunction(outer)
difference.AddFunction(inner)
difference.SetOperationTypeToDifference()
intersection = vtk.vtkImplicitBoolean()
intersection.AddFunction(difference)
intersection.AddFunction(plane1)
intersection.AddFunction(plane2)
intersection.SetOperationTypeToIntersection()
pipe = intersection
# Also return inner and outer cylinder.
intersection = vtk.vtkImplicitBoolean()
intersection.AddFunction(inner)
intersection.AddFunction(plane1)
intersection.AddFunction(plane2)
intersection.SetOperationTypeToIntersection()
inner = intersection
intersection = vtk.vtkImplicitBoolean()
intersection.AddFunction(outer)
intersection.AddFunction(plane1)
intersection.AddFunction(plane2)
intersection.SetOperationTypeToIntersection()
outer = intersection
return pipe, inner, outer
def add_to_renderer(renderer, item, color, opacity=1., translate=None):
colors = vtk.vtkNamedColors()
mapper = vtk.vtkPolyDataMapper()
mapper.SetScalarVisibility(False)
mapper.SetInputConnection(item.GetOutputPort())
actor = vtk.vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetColor(colors.GetColor3d(color))
actor.GetProperty().SetOpacity(opacity)
if translate:
trafo = vtk.vtkTransform()
trafo.Translate(translate)
actor.SetUserTransform(trafo)
renderer.AddActor(actor)
return mapper, actor
def evaluate_implicit(implicit_function, resolution, bounds):
sampled = vtk.vtkSampleFunction()
sampled.SetSampleDimensions(resolution, resolution, resolution)
sampled.SetModelBounds(bounds)
sampled.SetImplicitFunction(implicit_function)
iso = vtk.vtkMarchingCubes()
iso.SetValue(0,0.)
iso.SetInputConnection(sampled.GetOutputPort())
iso.Update()
return iso
def main():
colors = vtk.vtkNamedColors()
# Params.
radius = 2.
thickness = 0.5
start_point = np.array([0] * 3)
end_point = np.array([0, 0, 10])
length = np.linalg.norm(start_point-end_point)
radius2 = 2.
thickness2 = 0.5
start_point2 = np.array([-10, 0, 0])
end_point2 = np.array([0, 0, 7])
length2 = np.linalg.norm(start_point2-end_point2)
# Compute transforms.
transform = compute_transform(start_point, end_point)
transform2 = compute_transform(start_point2, end_point2)
############################################################################
# BOOLEAN OPERATIONS ON MESHES
############################################################################
if False:
pipe, inner, outer = create_pipe2(radius=radius, thickness=thickness, height=length)
pipe2, inner2, outer2 = create_pipe2(radius=radius2, thickness=thickness2, height=length2)
# Apply the transforms.
pipe = transform_item(pipe, transform)
inner = transform_item(inner, transform)
outer = transform_item(outer, transform)
pipe2 = transform_item(pipe2, transform2)
inner2 = transform_item(inner2, transform2)
outer2 = transform_item(outer2, transform2)
#pipe_2m1 = boolean_combine(pipe2, pipe, 'difference')
pipe_2m1 = boolean_combine(pipe2, pipe, 'union') # Ugly! There is a bug in vtk!
result_bool = pipe_2m1
#result_bool = boolean_combine(pipe, pipe_2m1, 'union')
#result_bool = remeshSurface(result_bool, targetArea=.1, iterations=10)
# Add items to renderer.
renderer = vtk.vtkRenderer()
opacity=1.0
#add_to_renderer(renderer=renderer, item=pipe, color='yellow', opacity=opacity)
#add_to_renderer(renderer=renderer, item=pipe2, color='yellow', opacity=opacity)
add_to_renderer(renderer=renderer, item=result_bool, color='red')
############################################################################
# IMPLICIT BOOLEAN
############################################################################
else:
# We need to know the domain where the implicit function will be
# evaulated. There is certainly other ways to achieve this. Here,
# we simply get the bounds from the meshes. Also, we add a margin
# to avoid artifacts close to the domain boundary.
pipe = create_pipe(radius=radius, thickness=thickness, height=length)
pipe2 = create_pipe(radius=radius2, thickness=thickness2, height=length2)
pipe = transform_item(pipe, transform)
pipe2 = transform_item(pipe2, transform2)
bounds = pipe.GetOutput().GetBounds()
bounds2 = pipe2.GetOutput().GetBounds()
def applyMargin(bounds, margin):
extent = [ bounds[1]-bounds[0],
bounds[3]-bounds[2],
bounds[5]-bounds[4] ]
bounds = [ bounds[0]-extent[0]*margin, bounds[1]+extent[0]*margin,
bounds[2]-extent[1]*margin, bounds[3]+extent[1]*margin,
bounds[4]-extent[2]*margin, bounds[5]+extent[2]*margin ]
return bounds
bounds = applyMargin(bounds, margin=0.1)
bounds2 = applyMargin(bounds2, margin=0.1)
# The bounds of the combined object pipe+pipe2
boundsCombo = [min(bounds[0], bounds2[0]),
max(bounds[1], bounds2[1]),
min(bounds[2], bounds2[2]),
max(bounds[3], bounds2[3]),
min(bounds[4], bounds2[4]),
max(bounds[5], bounds2[5])]
# Let's create implicit functions for the pipes.
pipeImp, innerImp, outerImp = create_pipe_implicit(radius=radius, thickness=thickness, height=length)
pipeImp2, innerImp2, outerImp2 = create_pipe_implicit(radius=radius2, thickness=thickness2, height=length2)
pipeImp.SetTransform(transform.GetInverse())
pipeImp2.SetTransform(transform2.GetInverse())
innerImp.SetTransform(transform.GetInverse())
innerImp2.SetTransform(transform2.GetInverse())
outerImp.SetTransform(transform.GetInverse())
outerImp2.SetTransform(transform2.GetInverse())
# Apply the intersection.
difference = vtk.vtkImplicitBoolean()
difference.AddFunction(pipeImp2)
difference.AddFunction(outerImp)
difference.SetOperationTypeToDifference()
union = vtk.vtkImplicitBoolean()
union.AddFunction(difference)
union.AddFunction(pipeImp)
union.SetOperationTypeToUnion()
# This last operation is required to "cut through" the first pipe.
difference = vtk.vtkImplicitBoolean()
difference.AddFunction(union)
difference.AddFunction(innerImp2)
difference.SetOperationTypeToDifference()
# Convert the implicit functions into surfaces.
pipe = evaluate_implicit(implicit_function=pipeImp,
resolution=100,
bounds=bounds)
pipe2 = evaluate_implicit(implicit_function=pipeImp2,
resolution=100,
bounds=bounds2)
result = evaluate_implicit(implicit_function=difference,
resolution=100,
bounds=boundsCombo)
# Add items to renderer.
renderer = vtk.vtkRenderer()
opacity=1.
add_to_renderer(renderer=renderer, item=pipe, color='yellow', opacity=opacity, translate=[0,5,0])
add_to_renderer(renderer=renderer, item=pipe2, color='yellow', opacity=opacity, translate=[0,5,0])
add_to_renderer(renderer=renderer, item=result, color='red')
# Create a renderer, render window, and interactor.
render_window = vtk.vtkRenderWindow()
render_window.AddRenderer(renderer)
render_window.SetWindowName("Overlapping cylinders example")
render_window.SetSize(1000,1000)
render_window_interactor = vtk.vtkRenderWindowInteractor()
render_window_interactor.SetRenderWindow(render_window)
# Add the actors to the scene.
renderer.SetBackground(colors.GetColor3d("Gray"))
# Render and interact.
render_window.Render()
render_window_interactor.Start()
if __name__ == '__main__':
main()
Булева операция над сетками
# This code has been written by normanius under the CC BY-SA 4.0 license.
# License: https://creativecommons.org/licenses/by-sa/4.0/
# Author: https://stackru.com/users/3388962/normanius
# Date: July 2018
import vtk
import numpy as np
try:
# Remesher based on VMTK. Sorry, cannot share this with you.
from geometry.remesher import remeshSurface
from vtkutils.misc import extractEdges
from geometry.capper import capSurface
except:
remeshSurface = None
extractEdges = None
capSurface = None
def compute_transform(start, end):
# Better compute the matrix in numpy!
normalized_x = [0]*3
normalized_y = [0]*3
normalized_z = [0]*3
# The X axis is a vector from start to end
vtk.vtkMath.Subtract(end, start, normalized_x)
length = vtk.vtkMath.Norm(normalized_x)
vtk.vtkMath.Normalize(normalized_x)
# The Z axis is an arbitrary vector cross X
rng = vtk.vtkMinimalStandardRandomSequence()
rng.SetSeed(8775070) # For testing.
arbitrary = [0]*3
for i in range(0, 3):
rng.Next()
arbitrary[i] = rng.GetRangeValue(-10, 10)
vtk.vtkMath.Cross(normalized_x, arbitrary, normalized_z)
vtk.vtkMath.Normalize(normalized_z)
# The Y axis is Z cross X
vtk.vtkMath.Cross(normalized_z, normalized_x, normalized_y)
matrix = vtk.vtkMatrix4x4()
# Create the direction cosine matrix
matrix.Identity()
for i in range(3):
matrix.SetElement(i, 0, normalized_x[i])
matrix.SetElement(i, 1, normalized_y[i])
matrix.SetElement(i, 2, normalized_z[i])
transform = vtk.vtkTransform()
transform.Translate(start) # translate to starting point
transform.Concatenate(matrix) # apply direction cosines
transform.RotateY(90.0) # align cylinder
# Don't scale! This changes mesh properties (e.g. aspect ratio)
#transform.Scale(1.0, 1.0, length) # scale along the height vector
return transform
def transform_item(item, transform):
transformed = vtk.vtkTransformPolyDataFilter()
transformed.SetInputConnection(item.GetOutputPort())
transformed.SetTransform(transform)
transformed.Update()
return transformed
def create_pipe(radius, thickness, height):
# This type of pipe is not suited for remeshing, because remeshing does not
# preserve (feature-) edges. See create_pipe2
assert(radius>thickness)
disk = vtk.vtkDiskSource()
disk.SetCircumferentialResolution(128)
disk.SetRadialResolution(1)
disk.SetOuterRadius(radius)
disk.SetInnerRadius(radius - thickness)
pipe = vtk.vtkLinearExtrusionFilter()
pipe.SetInputConnection(disk.GetOutputPort())
pipe.SetExtrusionTypeToNormalExtrusion()
pipe.SetVector(0, 0, 1)
pipe.SetScaleFactor(height)
pipe.Update()
return pipe
def create_pipe2(radius, thickness, height):
# Create pipes with decently meshed surfaces, if remeshSurface is
# availaable.
# Align the cylinder in the same way as create_pipe() does.
transform = vtk.vtkTransform()
transform.RotateX(90.0)
transform.Translate(0,height/2,0)
outer = vtk.vtkCylinderSource()
outer.SetRadius(radius)
outer.SetResolution(128)
outer.SetHeight(height)
outer.CappingOff()
outer.Update()
outer = transform_item(outer, transform)
inner = vtk.vtkCylinderSource()
inner.SetRadius(radius-thickness)
inner.SetResolution(128)
inner.SetHeight(height)
inner.CappingOff()
inner.Update()
inner = transform_item(inner, transform)
# remeshSurface, extractEdges and capSurface are not available, sorry!
if remeshSurface:
outer = remeshSurface(outer, targetArea=.1, iterations=10, smoothing=False)
inner = remeshSurface(inner, targetArea=.1, iterations=10, smoothing=False)
# So far, we have two concentric cylinders.
# Close the upper and lower caps using vtkContourTriangulator.
result = combine_polydata(outer, inner)
edges1 = extractEdges(outer, mode='separated')
edges2 = extractEdges(inner, mode='separated')
assert(len(edges1)==len(edges2)==2)
for i in range(2):
edgesBottom = combine_polydata(edges1[i], edges2[i])
bottom = vtk.vtkContourTriangulator()
bottom.SetInputConnection(edgesBottom.GetOutputPort())
bottom.Update()
result = combine_polydata(result, bottom)
# Return also the inner and outer cylinders.
#return result, inner, outer
inner = capSurface(inner, remesh=True, returnCaps=False)
outer = capSurface(outer, remesh=True, returnCaps=False)
return result, inner, outer
def clean_mesh(source):
clean = vtk.vtkCleanPolyData()
#clean.ConvertPolysToLinesOff()
clean.SetInputData(source.GetOutput())
clean.Update()
return clean
def fill_holes(source):
fill = vtk.vtkFillHolesFilter()
fill.SetInputConnection(source.GetOutputPort())
fill.SetHoleSize(100)
fill.Update()
return fill
def combine_polydata(source1, source2):
if source2 is None:
return source1
if source1 is None:
return source2
combo = vtk.vtkAppendPolyData()
combo.AddInputData(source1.GetOutput())
combo.AddInputData(source2.GetOutput())
combo.Update()
return clean_mesh(combo)
def boolean_combine(source1, source2, method='union'):
assert(method.lower() in ['union', 'or',
'intersection', 'and',
'difference', 'subtract', 'minus'])
source1 = source1 if source1 is not None else None
source2 = source2 if source2 is not None else None
method = method.lower()
if source1 is None and source2 is None:
return None
# vtkBooleanOperationPolyDataFilter cannot handle empty sources!
if source2 is None or source2.GetOutput().GetNumberOfPoints() == 0:
return source1
if source1 is None or source1.GetOutput().GetNumberOfPoints() == 0:
return source2
boolean = vtk.vtkBooleanOperationPolyDataFilter()
if method in ['union', 'or']:
boolean.SetOperationToUnion()
elif method in ['intersection', 'and']:
boolean.SetOperationToIntersection()
elif method in ['difference', 'subtract', 'minus']:
boolean.SetOperationToDifference()
boolean.SetInputData(0, source1.GetOutput())
boolean.SetInputData(1, source2.GetOutput())
boolean.Update()
result = boolean
if result.GetOutput().GetNumberOfPoints() == 0:
# No intersection betweeen source1 and source2.
if method in ['union', 'or']:
result = combine_polydata(source1, source2)
elif method in ['intersection', 'and']:
result = vtk.vtkPolyData()
elif method in ['difference', 'subtract', 'minus']:
result = vtk.vtkPolyData()
result.DeepCopy(source1.GetOutput())
pt = vtk.vtkPassThroughFilter()
pt.SetInputData(result)
pt.Update()
result = pt
return clean_mesh(result)
def add_to_renderer(renderer, item, color, opacity=1., translate=None):
colors = vtk.vtkNamedColors()
mapper = vtk.vtkPolyDataMapper()
mapper.SetScalarVisibility(False)
mapper.SetInputConnection(item.GetOutputPort())
actor = vtk.vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetColor(colors.GetColor3d(color))
actor.GetProperty().SetOpacity(opacity)
if translate:
trafo = vtk.vtkTransform()
trafo.Translate(translate)
actor.SetUserTransform(trafo)
renderer.AddActor(actor)
return mapper, actor
def main():
colors = vtk.vtkNamedColors()
# Params.
radius = 2.
thickness = 0.5
start_point = np.array([0] * 3)
end_point = np.array([0, 0, 10])
length = np.linalg.norm(start_point-end_point)
radius2 = 2.
thickness2 = 0.5
start_point2 = np.array([-10, 0, 0])
end_point2 = np.array([0, 0, 7])
length2 = np.linalg.norm(start_point2-end_point2)
# Compute transforms.
transform = compute_transform(start_point, end_point)
transform2 = compute_transform(start_point2, end_point2)
############################################################################
# BOOLEAN OPERATIONS ON MESHES
############################################################################
if remeshSurface and False:
pipe, inner, outer = create_pipe2(radius=radius,
thickness=thickness,
height=length)
pipe2, inner2, outer2 = create_pipe2(radius=radius2,
thickness=thickness2,
height=length2)
# Apply the transforms.
pipe = transform_item(pipe, transform)
inner = transform_item(inner, transform)
outer = transform_item(outer, transform)
pipe2 = transform_item(pipe2, transform2)
inner2 = transform_item(inner2, transform2)
outer2 = transform_item(outer2, transform2)
# Ugly! There is a bug in vtk!
result_bool = boolean_combine(pipe2, pipe, 'union')
else:
pipe = create_pipe(radius=radius, thickness=thickness, height=length)
pipe2 = create_pipe(radius=radius2, thickness=thickness2, height=length2)
pipe = transform_item(pipe, transform)
pipe2 = transform_item(pipe2, transform2)
# A warning is printed: "No Intersection between objects"...
# This has something to do with the mesh properties.
result_bool = boolean_combine(pipe2, pipe, 'difference')
# Add items to renderer.
renderer = vtk.vtkRenderer()
opacity=1.0
add_to_renderer(renderer=renderer, item=pipe, color='yellow', opacity=opacity, translate=[0,5,0])
add_to_renderer(renderer=renderer, item=pipe2, color='yellow', opacity=opacity, translate=[0,5,0])
add_to_renderer(renderer=renderer, item=result_bool, color='red')
# Create a renderer, render window, and interactor.
render_window = vtk.vtkRenderWindow()
render_window.AddRenderer(renderer)
render_window.SetWindowName("Overlapping cylinders example")
render_window.SetSize(1000,1000)
render_window_interactor = vtk.vtkRenderWindowInteractor()
render_window_interactor.SetRenderWindow(render_window)
# Add the actors to the scene.
renderer.SetBackground(colors.GetColor3d("Gray"))
# Render and interact.
render_window.Render()
render_window_interactor.Start()
if __name__ == '__main__':
main()