"Соединяйте" актеров, удаляя перекрытия или "заполняя" трюк. Обнаружение столкновения

Я пытаюсь визуализировать структурную раму с полым цилиндрическим поперечным сечением (позже я хотел бы обобщить поперечное сечение, но пока это будет сделано), используя 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()
Другие вопросы по тегам