These examples demonstrate how to integrate trueform into VTK applications for real-time interactive geometry processing.
The examples use helper functions (see util/geometry.py) for VTK integration:
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))
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
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 ...]
mesh.build_tree()tf.intersects(mesh1, mesh2)mesh.transformationimport 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)
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
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])
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]
tf.boolean_differencereturn_curves=Trueimport 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)
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()
Source: vtk/intersection_curves.py
Real-time visualization of intersection curves between two moving meshes.
python intersection_curves.py mesh1.stl [mesh2.stl]
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)
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
tf.distance_fieldtf.isobandsimport 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)
# 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)
Source: vtk/isocontours.py
Extracts contour curves at specific threshold values.
python isocontours.py mesh.stl
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)
OffsetBlockedArray returned by isocontours uses the same offset/data format as VTK cell arrays, enabling efficient conversion.