Examples | PY

VTK Integration

Interactive applications demonstrating real-time collision detection, mesh booleans, and curve extraction.

These examples demonstrate how to integrate trueform into VTK applications for real-time interactive geometry processing.

Integration Pattern

The examples use helper functions (see util/geometry.py) for VTK integration:

From VTK to trueform

from_vtk.py
import vtk
from vtk.util import numpy_support
import numpy as np
import trueform as tf

def polydata_to_numpy(polydata: vtk.vtkPolyData):
    """Extract numpy arrays from VTK PolyData"""
    # Get points (zero-copy view)
    points = numpy_support.vtk_to_numpy(polydata.GetPoints().GetData())

    # Get triangle connectivity
    cells = polydata.GetPolys()
    connectivity = numpy_support.vtk_to_numpy(cells.GetConnectivityArray())
    faces = connectivity.reshape(-1, 3).astype(np.int32)

    return faces, points


# Usage
polydata = reader.GetOutput()
faces, points = polydata_to_numpy(polydata)
mesh = tf.Mesh(faces, points.astype(np.float32))

From trueform to VTK

geometry.py
import vtk
from vtk.util import numpy_support
import numpy as np
import trueform as tf

def numpy_to_polydata(points: np.ndarray, triangles: np.ndarray) -> vtk.vtkPolyData:
    """Convert numpy arrays to VTK PolyData"""
    vtk_points = vtk.vtkPoints()
    vtk_points.SetData(numpy_support.numpy_to_vtk(
        num_array=points, deep=True,
        array_type=vtk.VTK_FLOAT if points.dtype == np.float32 else vtk.VTK_DOUBLE
    ))

    n_tris = triangles.shape[0]
    connectivity = triangles.astype(np.int64).ravel()
    offsets = np.arange(0, 3 * n_tris + 1, 3, dtype=np.int64)

    cell_array = vtk.vtkCellArray()
    cell_array.SetData(
        numpy_support.numpy_to_vtkIdTypeArray(offsets, deep=True),
        numpy_support.numpy_to_vtkIdTypeArray(connectivity, deep=True)
    )

    poly = vtk.vtkPolyData()
    poly.SetPoints(vtk_points)
    poly.SetPolys(cell_array)
    return poly


def curves_to_polydata(paths, points: np.ndarray) -> vtk.vtkPolyData:
    """Convert trueform curves to VTK PolyData"""
    vtk_points = vtk.vtkPoints()
    vtk_points.SetData(numpy_support.numpy_to_vtk(
        num_array=points, deep=False,
        array_type=vtk.VTK_FLOAT if points.dtype == np.float32 else vtk.VTK_DOUBLE
    ))

    # paths.offsets and paths.data are already in VTK format
    lines = vtk.vtkCellArray()
    lines.SetData(
        numpy_support.numpy_to_vtkIdTypeArray(paths.offsets.astype(np.int64), deep=True),
        numpy_support.numpy_to_vtkIdTypeArray(paths.data.astype(np.int64), deep=True)
    )

    poly = vtk.vtkPolyData()
    poly.SetPoints(vtk_points)
    poly.SetLines(lines)
    return poly

Collision Detection

Source: vtk/collision.py

Interactive application with a 5x5 grid of meshes. Hover to highlight, click and drag to move. Colliding meshes turn cyan.

python collision.py mesh.stl [mesh2.stl ...]

Features Showcased

  • Building spatial trees with mesh.build_tree()
  • Collision detection with tf.intersects(mesh1, mesh2)
  • Dynamic transformations via mesh.transformation

Loading and Positioning

loading.py
import trueform as tf
import numpy as np

# Load mesh
faces, points = tf.read_stl("mesh.stl")
mesh = tf.Mesh(faces, points)
mesh.build_tree()

# Create transformation (center, scale, rotate, position)
transform = np.eye(4, dtype=np.float32)
transform[:3, :3] = rotation_matrix
transform[:3, 3] = position
mesh.transformation = transform

# Sync VTK actor with trueform transformation
matrix = vtk.vtkMatrix4x4()
for i in range(4):
    for j in range(4):
        matrix.SetElement(i, j, transform[i, j])
actor.SetUserMatrix(matrix)

Collision Detection Loop

collision_loop.py
def check_collisions(meshes, actors):
    for i, mesh_i in enumerate(meshes):
        colliding = False
        for j, mesh_j in enumerate(meshes):
            if i != j and tf.intersects(mesh_i, mesh_j):
                colliding = True
                break

        if colliding:
            actors[i].GetProperty().SetColor(0.0, 1.0, 1.0)  # Cyan
        else:
            actors[i].GetProperty().SetColor(1.0, 1.0, 1.0)  # White

Updating Transform During Drag

drag_update.py
def on_mouse_move(mesh, actor, delta):
    # Get current transform
    transform = mesh.transformation.copy()

    # Update position
    transform[0, 3] += delta[0]
    transform[1, 3] += delta[1]
    transform[2, 3] += delta[2]

    # Apply to trueform (tree is reused)
    mesh.transformation = transform

    # Sync VTK actor
    for i in range(4):
        for j in range(4):
            actor.GetUserMatrix().SetElement(i, j, transform[i, j])
The spatial tree is built once and reused across all transformation updates, enabling real-time collision detection.

Boolean Difference

Source: vtk/boolean_difference.py

Dual viewport showing input meshes with intersection curves (left) and boolean difference result (right). Drag meshes to interact, press n to randomize orientations.

python boolean_difference.py mesh1.stl [mesh2.stl]

Features Showcased

  • Boolean operations with tf.boolean_difference
  • Extracting intersection curves with return_curves=True
  • Real-time updates during interaction

Boolean Computation

boolean.py
import trueform as tf

# Compute difference with curves
(result_faces, result_points), labels, (paths, curve_points) = (
    tf.boolean_difference(mesh1, mesh2, return_curves=True)
)

# Convert result to VTK
result_polydata = numpy_to_polydata(result_points, result_faces)
curve_polydata = curves_to_polydata(paths, curve_points)

Real-time Update Callback

boolean_update.py
def on_interaction():
    # Recompute boolean with current transforms
    (faces, points), labels, (paths, curve_pts) = (
        tf.boolean_difference(mesh1, mesh2, return_curves=True)
    )

    # Update VTK visualization
    result_polydata = numpy_to_polydata(points, faces)
    result_mapper.SetInputData(result_polydata)

    curve_polydata = curves_to_polydata(paths, curve_pts)
    curve_mapper.SetInputData(curve_polydata)

    render_window.Render()

Intersection Curves

Source: vtk/intersection_curves.py

Real-time visualization of intersection curves between two moving meshes.

python intersection_curves.py mesh1.stl [mesh2.stl]

Computing Curves

curves.py
import trueform as tf

# Compute intersection curves
paths, curve_points = tf.intersection_curves(mesh1, mesh2)

# paths: OffsetBlockedArray of vertex indices
# curve_points: numpy array of 3D points

# Iterate over curves
for path_ids in paths:
    pts = curve_points[path_ids]
    # Process curve

# Convert to VTK for visualization
curve_polydata = curves_to_polydata(paths, curve_points)

Isobands

Source: vtk/isobands.py

Dual viewport showing original mesh with boundary curves (left) and extracted isobands (right). Press n to randomize the cutting plane, hold Shift and scroll to move isoband levels.

python isobands.py mesh.stl

Features Showcased

  • Scalar field from plane distance with tf.distance_field
  • Isoband extraction with tf.isobands
  • Boundary curve extraction

Isoband Extraction

isobands.py
import trueform as tf
import numpy as np

# Define scalar field (signed distance from plane)
plane = tf.Plane(normal=[0, 0, 1], origin=[0, 0, 0])
scalars = tf.distance_field(mesh.points, plane)

# Extract isobands with boundary curves
cut_values = np.array([-1.0, -0.5, 0.0, 0.5, 1.0], dtype=np.float32)
selected_bands = np.array([0, 2, 4], dtype=np.int32)  # Every other band

(band_faces, band_points), labels, (paths, curve_points) = tf.isobands(
    mesh, scalars, cut_values, selected_bands, return_curves=True
)

# Convert to VTK
band_polydata = numpy_to_polydata(band_points, band_faces)
curve_polydata = curves_to_polydata(paths, curve_points)

Coloring by Band Label

color_bands.py
# Create color array from labels
colors = vtk.vtkUnsignedCharArray()
colors.SetNumberOfComponents(3)
colors.SetName("Colors")

color_map = [
    (255, 100, 100),  # Band 0 - red
    (100, 255, 100),  # Band 1 - green
    (100, 100, 255),  # Band 2 - blue
]

for label in labels:
    colors.InsertNextTuple3(*color_map[label % len(color_map)])

band_polydata.GetCellData().SetScalars(colors)

Isocontours

Source: vtk/isocontours.py

Extracts contour curves at specific threshold values.

python isocontours.py mesh.stl

Isocontour Extraction

isocontours.py
import trueform as tf
import numpy as np

# Define scalar field
plane = tf.Plane(normal=[0, 0, 1], origin=[0, 0, 0])
scalars = tf.distance_field(mesh.points, plane)

# Single threshold
paths, curve_points = tf.isocontours(mesh, scalars, 0.0)

# Multiple thresholds
thresholds = np.array([-0.5, 0.0, 0.5], dtype=np.float32)
paths, curve_points = tf.isocontours(mesh, scalars, thresholds)

# Visualize as tubes
curve_polydata = curves_to_polydata(paths, curve_points)
tubes = vtk.vtkTubeFilter()
tubes.SetInputData(curve_polydata)
tubes.SetRadius(0.05)
tubes.SetNumberOfSides(20)
The OffsetBlockedArray returned by isocontours uses the same offset/data format as VTK cell arrays, enabling efficient conversion.