These examples demonstrate how to integrate trueform into a larger framework like VTK, creating zero-copy views directly over VTK's data structures for real-time interactive applications.
Source: vtk/collision.cpp
Interactive VTK application demonstrating real-time collision detection. Loads meshes and arranges them in a 5×5 grid with random rotations. Users can pick and drag actors with the mouse while trueform performs real-time collision detection, highlighting colliding objects.
tf::polygons views directly over VTK data bufferstf::tree over collections of objectstf::intersects with dynamic tf::form for collision detectiontf::ray_cast for mouse pickingThe examples use a data bridge (see util/data_bridge.hpp) for zero-copy integration with VTK:
// Get points from VTK polydata (zero-copy)
inline auto get_points(vtkPolyData *poly) {
auto ptr = static_cast<float *>(
poly->GetPoints()->GetData()->GetVoidPointer(0));
return tf::make_points<3>(
tf::make_range(ptr, 3 * poly->GetNumberOfPoints()));
}
// Get triangle faces - handles both VTK versions
#ifdef VTK_CELL_ARRAY_V2
inline auto get_triangle_faces(vtkCellArray *cell_array) {
return tf::make_blocked_range<3>(tf::make_range(
static_cast<vtkIdType *>(
cell_array->GetConnectivityArray()->GetVoidPointer(0)),
3 * cell_array->GetNumberOfCells()));
}
#else
inline auto get_triangle_faces(vtkCellArray *cell_array) {
return tf::make_tag_blocked_range<3>(
tf::make_range(cell_array->GetPointer(),
cell_array->GetNumberOfConnectivityEntries()));
}
#endif
// Get triangles (zero-copy view)
inline auto get_triangles(vtkPolyData *poly) {
return tf::make_polygons(get_triangle_faces(poly), get_points(poly));
}
Usage in application:
// Load VTK mesh
vtkNew<vtkPolyData> polydata;
reader->SetFileName("bunny.obj");
reader->Update();
polydata->ShallowCopy(reader->GetOutput());
// Create trueform view (zero-copy)
auto triangles = get_triangles(polydata);
// Build spatial tree
tf::aabb_tree<int, float, 3> tree(triangles, tf::config_tree(4, 4));
// Static objects in grid
auto static_form = tf::make_form(static_tree, static_polygons);
// Moving object with dynamic transform
auto transform = get_actor_transform();
auto dynamic_form = tf::make_form(
tf::make_frame(transform), moving_tree, moving_polygons);
// Check collision
if (tf::intersects(static_form, dynamic_form)) {
// Highlight colliding actors
actor->GetProperty()->SetColor(1.0, 0.0, 0.0);
} else {
actor->GetProperty()->SetColor(1.0, 1.0, 1.0);
}
// Create picking ray from mouse position
auto ray = tf::make_ray(camera_position, ray_direction);
// Cast against all actors
if (auto hit = tf::ray_hit(ray, form, tf::make_ray_config(0.f, 1e6f))) {
auto [object_id, hit_data] = hit;
auto [status, t, point] = hit_data;
// Select the clicked actor
selected_actor = actors[object_id];
}
Source: vtk/positioning.cpp
Interactive application for precise object positioning using closest-point queries. Loads two mesh copies with random rotations, allows free movement, then computes the exact transformation to bring them into contact along the shortest path.
tf::neighbor_search between two tf::form objectstf::frame from query results// Find closest points between two meshes
auto result = tf::neighbor_search(form1, form2);
if (result) {
auto [primitive_ids, metric_point_pair] = result;
auto [id0, id1] = primitive_ids;
auto [dist2, pt_on_mesh1, pt_on_mesh2] = metric_point_pair;
std::cout << "Closest distance: " << std::sqrt(dist2) << std::endl;
std::cout << "Triangle on mesh1: " << id0 << std::endl;
std::cout << "Triangle on mesh2: " << id1 << std::endl;
}
// Get closest points
auto [dist2, pt1, pt2] = tf::closest_metric_point_pair(form1, form2);
// Compute translation to bring points into contact
auto translation = pt2 - pt1;
auto assembly_transform = tf::make_transformation_from_translation(
translation.as_vector());
// Apply to mesh
auto assembled_form = tf::make_form(
tf::make_frame(assembly_transform), tree1, polygons1);
Source: vtk/scalar_field_intersections.cpp
Interactive visualization of isocontours based on a scalar field. Loads a mesh, defines a distance field from a random plane, and continuously updates the intersection curve as the plane moves with mouse wheel scrolling.
tf::scalar_field_intersections for isocontour extractiontf::make_intersection_edges// Define distance field from plane
auto plane = tf::make_plane(
random_triangle[0], random_triangle[1], random_triangle[2]);
tf::buffer<float> scalar_field;
scalar_field.allocate(points.size());
tf::parallel_transform(points, scalar_field,
tf::distance_f(plane));
// Compute intersections at threshold
float threshold = 0.0f;
tf::scalar_field_intersections<int, float, 3> sfi;
sfi.build(polygons, scalar_field, threshold);
// Extract intersection edges
auto edges = tf::make_intersection_edges(sfi);
auto contour_segments = tf::make_segments(
edges, sfi.intersection_points());
// Convert to VTK for visualization
update_vtk_polyline(contour_segments);
// Mouse wheel callback
void OnMouseWheel(int delta) {
// Adjust plane position
plane_offset += delta * 0.01f;
// Recompute scalar field
tf::parallel_transform(points, scalar_field, [&](auto pt) {
return tf::distance(pt, plane) - plane_offset;
});
// Update isocontour
sfi.build(polygons, scalar_field, 0.0f);
update_visualization();
}
Source: vtk/forms_intersections.cpp
Interactive visualization of intersection curves between two moving meshes. Users can grab and move meshes; intersection curves update in real-time when meshes collide.
tf::intersections_between_polygons between two tf::form objectstf::face_membership and tf::manifold_edge_linkpoints.as<double>()// Build topology for both meshes
tf::face_membership<int> fm1, fm2;
fm1.build(mesh1.polygons());
fm2.build(mesh2.polygons());
tf::manifold_edge_link<int, 3> mel1, mel2;
mel1.build(mesh1.faces(), fm1);
mel2.build(mesh2.faces(), fm2);
// Create forms with topology tags
auto form1 = tf::make_form(tree1,
mesh1.polygons() | tf::tag(mel1) | tf::tag(fm1));
auto form2 = tf::make_form(tree2,
mesh2.polygons() | tf::tag(mel2) | tf::tag(fm2));
// Update callback (called during interaction)
void OnInteraction() {
// Get current transform from VTK actor
auto transform = get_actor_transform();
// Create dynamic form
auto dynamic_form = tf::make_form(
tf::make_frame(transform), tree2,
mesh2.polygons() | tf::tag(mel2) | tf::tag(fm2));
// Compute intersections
tf::intersections_between_polygons<int, double, 3> ibp;
ibp.build(form1, dynamic_form);
if (ibp.intersections().size() > 0) {
// Extract intersection curves
auto edges = tf::make_intersection_edges(ibp);
auto curves = tf::make_segments(edges,
ibp.intersection_points());
// Visualize
update_vtk_intersection_curves(curves);
}
}
Source: vtk/isobands.cpp
Interactive visualization of isobands (regions between isocontours). Users can adjust the scalar field by changing the reference plane (press n), and move isocontour levels by scrolling with Shift held.
tf::make_isobands to extract regions between threshold levels// Define multiple threshold levels
std::vector<float> levels = {-1.0f, -0.5f, 0.0f, 0.5f, 1.0f};
// Extract only specific bands
std::vector<int> selected_bands = {1, 3}; // 2nd and 4th bands
// Extract isobands
auto [result_mesh, labels] = tf::make_isobands<int>(
polygons, scalar_field, tf::make_range(levels),
tf::make_range(selected_bands));
// Color by band
tf::parallel_apply(tf::zip(labels, result_mesh.polygons()),
[](auto pair) {
auto [label, polygon] = pair;
set_color_by_label(polygon, label);
});
Source: vtk/boolean.cpp
Interactive boolean operations between two moving meshes. Press n to randomize orientations, and watch real-time boolean computation (union, intersection, difference).
tf::make_boolean for set operations// Setup forms with topology
auto form1 = tf::make_form(tree1,
mesh1.polygons() | tf::tag(mel1) | tf::tag(fm1));
auto form2 = tf::make_form(
tf::make_frame(transform), tree2,
mesh2.polygons() | tf::tag(mel2) | tf::tag(fm2));
// Compute union
auto [union_mesh, union_labels, union_curves] = tf::make_boolean(
form1, form2, tf::boolean_op::merge, tf::return_curves);
// Compute intersection
auto [inter_mesh, inter_labels, inter_curves] = tf::make_boolean(
form1, form2, tf::boolean_op::intersection, tf::return_curves);
// Compute difference
auto [diff_mesh, diff_labels, diff_curves] = tf::make_boolean(
form1, form2, tf::boolean_op::left_difference, tf::return_curves);
// Keyboard callback for operation switching
void OnKeyPress(char key) {
tf::boolean_op operation;
switch (key) {
case 'u': operation = tf::boolean_op::merge; break;
case 'i': operation = tf::boolean_op::intersection; break;
case 'd': operation = tf::boolean_op::left_difference; break;
case 'D': operation = tf::boolean_op::right_difference; break;
default: return;
}
// Recompute with new operation
auto [result, labels, curves] = tf::make_boolean(
form1, form2, operation, tf::return_curves);
update_visualization(result, curves);
}
Using the data bridge helpers from util/data_bridge.hpp:
// Extract points (zero-copy)
inline auto get_points(vtkPolyData *poly) {
auto ptr = static_cast<float *>(
poly->GetPoints()->GetData()->GetVoidPointer(0));
return tf::make_points<3>(
tf::make_range(ptr, 3 * poly->GetNumberOfPoints()));
}
// Extract faces - VTK 8+ version (new cell array format)
#ifdef VTK_CELL_ARRAY_V2
inline auto get_triangle_faces(vtkCellArray *cell_array) {
return tf::make_blocked_range<3>(tf::make_range(
static_cast<vtkIdType *>(
cell_array->GetConnectivityArray()->GetVoidPointer(0)),
3 * cell_array->GetNumberOfCells()));
}
#else
// VTK 7 version (legacy format with size prefix)
inline auto get_triangle_faces(vtkCellArray *cell_array) {
return tf::make_tag_blocked_range<3>(
tf::make_range(cell_array->GetPointer(),
cell_array->GetNumberOfConnectivityEntries()));
}
#endif
// Complete integration
auto triangles = get_triangles(polydata); // Zero-copy
auto to_vtk_polydata(const tf::polygons_buffer<int, float, 3, 3> &polys) {
auto cells = vtk_make_unique<vtkCellArray>();
cells->Initialize();
auto offsets = cells->GetOffsetsArray();
offsets->SetNumberOfComponents(1);
offsets->SetNumberOfTuples(polys.faces().size() + 1);
auto offsets_r =
tf::make_range(static_cast<vtkIdType *>(offsets->GetVoidPointer(0)),
offsets->GetNumberOfValues());
tf::parallel_apply(tf::enumerate(offsets_r), [](auto pair) {
auto &&[id, offset] = pair;
offset = 3 * id;
});
auto ids = cells->GetConnectivityArray();
ids->SetNumberOfComponents(3);
ids->SetNumberOfTuples(polys.faces().size());
auto ids_r = tf::make_range(static_cast<vtkIdType *>(ids->GetVoidPointer(0)),
offsets->GetNumberOfValues());
tf::parallel_copy(polys.faces_buffer().data_buffer(), ids_r);
auto points = vtk_make_unique<vtkPoints>();
points->SetNumberOfPoints(polys.points().size());
tf::parallel_copy(polys.points(), get_points(points.get()));
auto out = vtk_make_unique<vtkPolyData>();
out->SetPoints(points.get());
out->SetPolys(cells.get());
return out;
}