VTK | C++

Functions

Standalone mesh operations wrapping trueform algorithms.

The functions module provides VTK-friendly wrappers for trueform's algorithms. Pass in tf::vtk::polydata, get back results as VTK objects or trueform primitives. Many functions accept an optional vtkMatrix4x4* transform for operating on meshes in world space without modifying the underlying geometry.

Include the module with:

#include <trueform/vtk/functions.hpp>

Spatial Queries

These functions wrap trueform's Spatial module queries. They automatically select the appropriate primitive type based on what the polydata contains: polygons first, then lines, then points.

Find the nearest point on a mesh to a query point. Returns neighbor_result, an alias for tf::nearest_neighbor<vtkIdType, float, 3>:

MemberTypeDescription
elementvtkIdTypeCell index of nearest element
info.pointtf::point<float, 3>Closest point on the mesh
info.metricfloatSquared distance
operator bool()True if a neighbor was found
auto result = tf::vtk::neighbor_search(mesh, point);
if (result) {
    auto closest = result.info.point;   // tf::point<float, 3>
    auto dist2 = result.info.metric;    // squared distance
    auto cell_id = result.element;      // vtkIdType
}

// With radius constraint (returns false if nothing within radius)
auto result = tf::vtk::neighbor_search(mesh, point, 10.0f);

// With transform
auto result = tf::vtk::neighbor_search({mesh, matrix}, point);

For mesh-vs-mesh queries, returns neighbor_pair_result, an alias for tf::nearest_neighbor_pair<vtkIdType, vtkIdType, float, 3>:

MemberTypeDescription
elementsstd::pair<vtkIdType, vtkIdType>Cell indices on each mesh
info.metricfloatSquared distance
info.firsttf::point<float, 3>Closest point on first mesh
info.secondtf::point<float, 3>Closest point on second mesh
operator bool()True if a pair was found
auto pair = tf::vtk::neighbor_search(mesh0, mesh1);
if (pair) {
    auto [dist2, pt0, pt1] = pair.info;
    auto [id0, id1] = pair.elements;
}

// With transforms
auto pair = tf::vtk::neighbor_search({mesh0, matrix0}, {mesh1, matrix1});

neighbor_search_k

Find the k nearest points on a mesh. Returns std::vector<neighbor_result> sorted by squared distance. See kNN Queries.

auto results = tf::vtk::neighbor_search_k(mesh, point, 5);
for (const auto& r : results) {
    // r.element, r.info.point, r.info.metric
}

// With radius constraint
auto results = tf::vtk::neighbor_search_k(mesh, point, 5, 10.0f);

neighbor_search_batch

Find nearest neighbors for multiple query points in parallel. Returns std::vector<neighbor_result>.

auto results = tf::vtk::neighbor_search_batch(mesh, query_points);

// With radius
auto results = tf::vtk::neighbor_search_batch(mesh, query_points, 10.0f);

neighbor_search_k_batch

Find k nearest neighbors for multiple query points. Returns tf::offset_block_vector<std::size_t, neighbor_result> with variable results per query.

auto results = tf::vtk::neighbor_search_k_batch(mesh, query_points, 5);
for (const auto& pt_results : results) {
    for (const auto& r : pt_results) {
        // r.element, r.info.point, r.info.metric
    }
}

// With radius
auto results = tf::vtk::neighbor_search_k_batch(mesh, query_points, 5, 10.f);

distance and distance2

Convenience wrappers around neighbor_search that return only the distance.

// Squared distance (faster, avoids sqrt)
float d2 = tf::vtk::distance2(mesh, point);
float d2 = tf::vtk::distance2(mesh0, mesh1);

// Euclidean distance
float d = tf::vtk::distance(mesh, point);
float d = tf::vtk::distance({mesh0, matrix0}, {mesh1, matrix1});

intersects

Test whether two meshes intersect. See Distance and Intersection.

if (tf::vtk::intersects(mesh0, mesh1)) {
    // meshes overlap
}

// With transforms
if (tf::vtk::intersects({mesh0, matrix0}, {mesh1, matrix1})) {
    // meshes overlap in world space
}

Ray Operations

Cast rays against meshes for picking and visibility testing. These wrap trueform's Ray Casting with VTK polydata.

ray_cast

Cast a tf::ray and get parametric hit information. Returns ray_cast_result, an alias for tf::tree_ray_info<vtkIdType, tf::ray_cast_info<float>>:

MemberTypeDescription
elementvtkIdTypeCell index of hit element
info.statustf::intersect_statusIntersection status
info.tfloatParametric distance along ray
operator bool()True if intersection found
auto result = tf::vtk::ray_cast(ray, mesh);
if (result) {
    auto t = result.info.t;           // parametric distance along ray
    auto cell_id = result.element;    // hit cell
}

// With ray config (min/max t)
auto result = tf::vtk::ray_cast(ray, mesh, {0.1f, 100.0f});

// With transform
auto result = tf::vtk::ray_cast(ray, {mesh, matrix});

ray_hit

Cast a ray and get the 3D hit position. Returns ray_hit_result, an alias for tf::tree_ray_info<vtkIdType, tf::ray_hit_info<float, 3>>:

MemberTypeDescription
elementvtkIdTypeCell index of hit element
info.statustf::intersect_statusIntersection status
info.tfloatParametric distance along ray
info.pointtf::point<float, 3>Hit position
operator bool()True if intersection found
auto result = tf::vtk::ray_hit(ray, mesh);
if (result) {
    auto hit_point = result.info.point;  // tf::point<float, 3>
    auto t = result.info.t;
    auto cell_id = result.element;
}

// With transform
auto result = tf::vtk::ray_hit(ray, {mesh, matrix});

pick

Pick the closest actor along a ray from a collection of actors. Automatically handles actor transforms via GetUserMatrix().

std::vector<vtkActor*> actors = {...};
auto ray = tf::vtk::make_world_ray(renderer, mouse_x, mouse_y);

if (auto result = tf::vtk::pick(ray, actors)) {
    vtkActor* hit_actor = result.actor;
    vtkIdType cell_id = result.cell_id;
    auto position = result.position;  // tf::point<float, 3>
    float t = result.t;
}

Bounding Boxes

Compute bounding boxes from polydata geometry. See AABB for the underlying types.

aabb_from

Compute the axis-aligned bounding box. Returns tf::aabb<float, 3>.

auto box = tf::vtk::aabb_from(polydata);
auto center = box.center();
auto size = box.size();

obb_from

Compute the oriented bounding box (minimum-volume enclosing box). Returns tf::obb<float, 3>.

auto box = tf::vtk::obb_from(polydata);

Boolean Operations

Combine meshes using constructive solid geometry. See the Cut module for details on boolean operations and input requirements.

make_boolean

Compute boolean operations between two meshes. Returns vtkSmartPointer<polydata> with "Labels" cell scalars indicating face origin (positive for mesh0, negative for mesh1).

auto result = tf::vtk::make_boolean(mesh0, mesh1, tf::boolean_op::merge);
auto result = tf::vtk::make_boolean(mesh0, mesh1, tf::boolean_op::intersection);
auto result = tf::vtk::make_boolean(mesh0, mesh1, tf::boolean_op::difference);

// With transforms
auto result = tf::vtk::make_boolean(
    {mesh0, matrix0}, {mesh1, matrix1}, tf::boolean_op::merge);

With tf::return_curves, returns std::pair<vtkSmartPointer<polydata>, vtkSmartPointer<polydata>> — the result mesh and intersection curves.

auto [result, curves] = tf::vtk::make_boolean(
    mesh0, mesh1, tf::boolean_op::merge, tf::return_curves);

embedded_intersection_curves

Embed intersection curves from mesh1 into mesh0 without performing a boolean operation. Returns vtkSmartPointer<polydata> containing mesh0 with faces split along intersection curves. No faces from mesh1 are included. See Embedded Intersection Curves.

auto result = tf::vtk::embedded_intersection_curves(mesh0, mesh1);

// With transforms
auto result = tf::vtk::embedded_intersection_curves(
    {mesh0, matrix0}, {mesh1, matrix1});

With tf::return_curves, returns std::pair<vtkSmartPointer<polydata>, vtkSmartPointer<polydata>> — the result mesh and intersection curves.

auto [result, curves] = tf::vtk::embedded_intersection_curves(
    mesh0, mesh1, tf::return_curves);

make_intersection_curves

Compute intersection curves between two meshes without performing a boolean operation. Returns vtkSmartPointer<polydata> with line cells. See Intersect module for the underlying algorithm.

auto curves = tf::vtk::make_intersection_curves(mesh0, mesh1);

// With transforms
auto curves = tf::vtk::make_intersection_curves(
    {mesh0, matrix0}, {mesh1, matrix1});

resolved_self_intersections

Find where a mesh intersects itself and embed the intersection curves as edges. Returns vtkSmartPointer<polydata>. See Embedded Self-Intersection Curves.

auto result = tf::vtk::resolved_self_intersections(mesh);

With tf::return_curves, returns std::tuple<vtkSmartPointer<polydata>, vtkSmartPointer<polydata>> — the resolved mesh and intersection curves.

auto [result, curves] = tf::vtk::resolved_self_intersections(
    mesh, tf::return_curves);

Topology Analysis

Analyze and modify mesh connectivity. See the Topology module for the underlying structures.

make_connected_components

Label connected components in a mesh. Returns std::pair<vtkSmartPointer<polydata>, int> — a polydata with "ComponentLabel" cell scalars and the number of components found.

auto [labeled, n_components] = tf::vtk::make_connected_components(
    mesh, tf::connectivity_type::edge);
std::cout << "Found " << n_components << " components\n";

Connectivity types:

  • tf::connectivity_type::manifold_edge — faces sharing a manifold edge are connected
  • tf::connectivity_type::edge — faces sharing an edge are connected
  • tf::connectivity_type::vertex — faces sharing a vertex are connected

split_into_components

Split a labeled polydata into separate meshes, one per unique label. Returns std::pair<std::vector<vtkSmartPointer<polydata>>, std::vector<vtkIdType>> — a vector of polydata objects and their corresponding labels.

auto [labeled, n] = tf::vtk::make_connected_components(
    mesh, tf::connectivity_type::edge);
auto [components, labels] = tf::vtk::split_into_components(labeled);
for (const auto &[polydata, label] : tf::zip(components, labels)) {
}

// Split by a named cell data array
auto [components, labels] = tf::vtk::split_into_components(polydata, "RegionId");

make_boundary_paths

Extract boundary edges connected into continuous paths. Returns vtkSmartPointer<polydata> with line cells. The output shares points with the input. See Boundary Detection.

auto boundaries = tf::vtk::make_boundary_paths(mesh);
std::cout << "Found " << boundaries->GetNumberOfLines() << " boundary paths\n";

make_boundary_edges

Extract individual boundary edges (edges belonging to only one face). Returns vtkSmartPointer<polydata> with line cells.

auto edges = tf::vtk::make_boundary_edges(mesh);

make_non_manifold_edges

Extract edges belonging to more than two faces. Returns vtkSmartPointer<polydata> with line cells. See Non-Manifold Edge Detection.

auto edges = tf::vtk::make_non_manifold_edges(mesh);

make_non_simple_edges

Extract both boundary and non-manifold edges. Returns vtkSmartPointer<polydata> with line cells and "EdgeType" cell scalars (0=boundary, 1=non-manifold). See Non-Simple Edge Detection.

auto edges = tf::vtk::make_non_simple_edges(mesh);

orient_faces_consistently

Orient face winding so adjacent faces have compatible normals. Modifies the input in place. Uses flood-fill through manifold edges; non-manifold edges act as barriers. See Face Orientation.

tf::vtk::orient_faces_consistently(mesh);

Scalar Field Operations

Extract features from scalar fields defined on mesh vertices. See the Cut module for the underlying algorithms.

make_isocontours

Extract curves at specified scalar values. Returns vtkSmartPointer<polydata> with line cells representing isocontours.

auto contours = tf::vtk::make_isocontours(mesh, "height", {0.1f, 0.2f, 0.3f});

// nullptr for active scalars
auto contours = tf::vtk::make_isocontours(mesh, nullptr, {0.5f});

make_isobands

Extract regions between scalar values. Returns vtkSmartPointer<polydata> with polygon cells and "BandLabel" cell scalars. See Isobands.

// cut_values define band boundaries
// selected_bands: 0=first band, 1=second, etc.
auto bands = tf::vtk::make_isobands(mesh, "height",
    {0.0f, 0.5f, 1.0f},  // cut values
    {1, 2});             // select bands 1 and 2

With tf::return_curves, returns std::pair<vtkSmartPointer<polydata>, vtkSmartPointer<polydata>> — the bands and their boundary curves.

auto [bands, curves] = tf::vtk::make_isobands(mesh, "height",
    {0.0f, 0.5f, 1.0f}, {1, 2}, tf::return_curves);

Geometry

Geometric computations and transformations. See the Geometry module for the underlying algorithms.

compute_cell_normals

Compute face normals and store as "Normals" cell data array. Modifies the input in place. See Polygon Normals.

tf::vtk::compute_cell_normals(polydata);

compute_point_normals

Compute vertex normals by averaging adjacent face normals and store as "Normals" point data array. Modifies the input in place. Computes cell normals first if not present. See Point Normals.

tf::vtk::compute_point_normals(mesh);

compute_principal_curvatures

Compute principal curvatures at each vertex using quadric fitting on k-ring neighborhoods. Stores "K1" and "K2" scalar arrays on point data. Optionally computes principal directions as "D1" and "D2" vector arrays. Modifies the input in place. See Principal Curvatures.

tf::vtk::compute_principal_curvatures(mesh);

// With custom k-ring size (default: 2)
tf::vtk::compute_principal_curvatures(mesh, 3);

// With principal directions
tf::vtk::compute_principal_curvatures(mesh, 2, true);
// Now mesh has K1, K2, D1, D2 arrays

signed_volume

Compute the signed volume of a closed mesh. Positive indicates outward-facing normals, negative indicates inward-facing.

double vol = tf::vtk::signed_volume(mesh);

area

Compute the total surface area of a mesh.

double surface_area = tf::vtk::area(mesh);

mean_edge_length

Compute the mean edge length of a mesh.

float mel = tf::vtk::mean_edge_length(mesh);

ensure_positive_orientation

Ensure faces are oriented with outward-pointing normals. First orients faces consistently, then flips all if signed volume is negative. Modifies the input in place. See Ensure Positive Orientation.

tf::vtk::ensure_positive_orientation(mesh);

// Skip consistency step if already consistent
tf::vtk::ensure_positive_orientation(mesh, true);

triangulated

Triangulate all polygons using ear-cutting. Returns a new polydata with triangle cells. See Triangulation.

auto triangles = tf::vtk::triangulated(mesh);

// Without preserving point data
auto triangles = tf::vtk::triangulated(mesh, false);
Cell data is not preserved since face count changes during triangulation.

Smoothing

laplacian_smoothed

Smooth point positions by iteratively moving vertices towards their neighbors' centroid. See Laplacian Smoothing. Returns vtkSmartPointer<vtkPoints>.

// Smooth: 100 iterations, lambda=0.5
auto smoothed = tf::vtk::laplacian_smoothed(mesh, 100, 0.5f);

// Apply smoothed points to polydata
mesh->SetPoints(smoothed);
ParameterDefaultDescription
inputInput polydata
iterationsNumber of smoothing passes
lambda0.5Movement factor in 0,1
Laplacian smoothing shrinks the mesh. For volume-preserving smoothing, use taubin_smoothed below.

taubin_smoothed

Smooth point positions while preserving volume by alternating shrink (positive λ) and inflate (negative μ) passes. See Taubin Smoothing. Returns vtkSmartPointer<vtkPoints>.

// Smooth: 50 iterations, lambda=0.5, pass-band frequency kpb=0.1
auto smoothed = tf::vtk::taubin_smoothed(mesh, 50, 0.5f, 0.1f);

// Apply smoothed points to polydata
mesh->SetPoints(smoothed);
ParameterDefaultDescription
inputInput polydata
iterationsNumber of smoothing passes (each = shrink + inflate)
lambda0.5Shrink factor in (0,1]
kpb0.1Pass-band frequency in (0,1)
Unlike Laplacian smoothing, Taubin smoothing preserves mesh volume by alternating contraction and expansion. Use this when volume preservation is important.

Point Cloud Alignment

Functions for aligning point sets. See Point Cloud Alignment for the underlying algorithms.

All alignment functions automatically detect point normals and select the best variant:

  • Point-to-Point (no normals) — minimizes Euclidean distance between correspondences
  • Point-to-Plane (target has normals) — minimizes distance along normal direction, converges 2-3x faster
  • Point-to-Plane with Normal Weighting (both have normals) — weights correspondences by normal agreement for better accuracy on surfaces with varying detail

chamfer_error

Compute one-way Chamfer error (mean nearest-neighbor distance) from source to target. See Chamfer Error. This is an asymmetric measure; for symmetric Chamfer distance, compute both directions and average.

float error = tf::vtk::chamfer_error(source, target);

// With transforms
float error = tf::vtk::chamfer_error({source, matrix0}, {target, matrix1});

fit_rigid_alignment

Fit a rigid transformation (rotation + translation) between corresponding point sets using the Kabsch/Procrustes algorithm. See Rigid Alignment. Requires same vertex count in same order. Returns vtkSmartPointer<vtkMatrix4x4>.

auto T = tf::vtk::fit_rigid_alignment(source, target);

// With transforms
auto T = tf::vtk::fit_rigid_alignment({source, matrix0}, {target, matrix1});

fit_obb_alignment

Fit a rigid transformation by matching oriented bounding boxes. See Coarse Alignment. No correspondences needed. The 180° ambiguity is resolved by testing orientations and selecting the one with lowest chamfer distance. Returns vtkSmartPointer<vtkMatrix4x4>.

auto T = tf::vtk::fit_obb_alignment(source, target);

// With sample size for disambiguation (default: 100)
auto T = tf::vtk::fit_obb_alignment(source, target, 200);

// With transforms
auto T = tf::vtk::fit_obb_alignment({source, matrix0}, {target, matrix1});

fit_icp_alignment

Fit a rigid transformation using iterative closest point with automatic convergence detection. See Iterative Refinement. This is the recommended entry point for ICP registration. Returns a DELTA transformation (vtkSmartPointer<vtkMatrix4x4>) mapping source world coordinates to target world coordinates.

// Default configuration
auto T_delta = tf::vtk::fit_icp_alignment(source, target);

// With configuration
tf::icp_config config;
config.max_iterations = 50;
config.n_samples = 1000;
config.k = 1;
auto T_delta = tf::vtk::fit_icp_alignment(source, target, config);

// With initial transform (returns delta, compose for total)
auto T_init = tf::vtk::fit_obb_alignment(source, target);
auto T_delta = tf::vtk::fit_icp_alignment({source, T_init}, target, config);
auto T_total = vtkSmartPointer<vtkMatrix4x4>::New();
vtkMatrix4x4::Multiply4x4(T_delta, T_init, T_total);

tf::icp_config parameters:

ParameterDefaultDescription
max_iterations100Maximum iterations
min_relative_improvement1e-6Convergence threshold
n_samples1000Subsample size (0 = all)
k1Nearest neighbors
sigma-1Gaussian width (-1 = adaptive)
outlier_proportion0Outlier rejection ratio

fit_knn_alignment

Fit a rigid transformation using k-nearest neighbor correspondences (single ICP step). See Single ICP Step. For custom ICP pipelines with special convergence logic. Returns vtkSmartPointer<vtkMatrix4x4>.

// Default (k=1, classic ICP step)
auto T = tf::vtk::fit_knn_alignment(source, target);

// With configuration
auto T = tf::vtk::fit_knn_alignment(source, target, {.k = 5, .sigma = -1.f});

// With transforms
auto T = tf::vtk::fit_knn_alignment({source, matrix0}, {target, matrix1});

tf::knn_alignment_config parameters:

ParameterDefaultDescription
k1Nearest neighbors (k=1 is classic ICP)
sigma-1Gaussian width (-1 = adaptive)
outlier_proportion0Outlier rejection ratio

Recommended pipeline:

// 1. Coarse alignment with OBB
auto T = tf::vtk::fit_obb_alignment(source, target);

// 2. Refine with ICP (computes normals automatically if available)
tf::vtk::compute_point_normals(target);  // Enable point-to-plane
auto T_delta = tf::vtk::fit_icp_alignment({source, T}, target);

// 3. Compose delta with initial transform for total
auto T_total = vtkSmartPointer<vtkMatrix4x4>::New();
vtkMatrix4x4::Multiply4x4(T_delta, T, T_total);

Remeshing

Modify triangle mesh resolution through decimation, isotropic remeshing, and edge collapse. These functions wrap the Remesh module. All assume triangle input and return a new polydata with cached half-edges.

All remesh functions require triangle meshes. Polygonal meshes must be triangulated first using triangulated.

decimated

Reduce face count using quadric error metrics. Returns vtkSmartPointer<polydata> with cached half-edges. See Decimation.

// Decimate to 10% of original faces
auto result = tf::vtk::decimated(mesh, 0.1f);

// With configuration
tf::decimate_config<float> config;
config.preserve_boundary = true;
auto result = tf::vtk::decimated(mesh, 0.1f, config);

See decimate_config for all parameters.

isotropic_remeshed

Redistribute vertices to achieve uniform edge lengths. Returns vtkSmartPointer<polydata> with cached half-edges. See Isotropic Remeshing.

// Remesh to target edge length
auto result = tf::vtk::isotropic_remeshed(mesh, 2.0f);

// With configuration
tf::remesh_config<float> config;
config.target_length = 2.0f;
config.iterations = 5;
config.use_quadric = true;
auto result = tf::vtk::isotropic_remeshed(mesh, config);

See remesh_config for all parameters.

collapsed_short_edges

Collapse edges shorter than a threshold. Unlike decimation which uses quadric error ordering, this collapses by edge length — shortest first. Returns vtkSmartPointer<polydata> with cached half-edges. See Edge Collapse.

float mel = tf::vtk::mean_edge_length(mesh);
auto result = tf::vtk::collapsed_short_edges(mesh, 0.75f * mel);

// With configuration
tf::length_collapse_config<float> config;
config.use_quadric = true;
auto result = tf::vtk::collapsed_short_edges(mesh, 0.75f * mel, config);

See length_collapse_config for all parameters.

Cleaning

Remove degeneracies and duplicates from mesh data. See the Clean module for the underlying algorithms.

cleaned_polygons

Remove duplicate points and degenerate faces from polygon data. Returns vtkSmartPointer<polydata>. See Polygon Cleaning.

auto clean = tf::vtk::cleaned_polygons(mesh);

// With tolerance for merging nearby points
auto clean = tf::vtk::cleaned_polygons(mesh, 0.001f);

// Without preserving point/cell data
auto clean = tf::vtk::cleaned_polygons(mesh, 0.0f, false);

cleaned_lines

Clean line data: merge duplicate points, remove degenerate edges, and reconnect edges into continuous paths. Returns vtkSmartPointer<polydata>. See Curve Cleaning.

auto clean = tf::vtk::cleaned_lines(mesh);
auto clean = tf::vtk::cleaned_lines(mesh, 0.001f);
Cell data cannot be preserved because edges are reconnected into paths.

cleaned_points

Remove duplicate points. Returns vtkSmartPointer<vtkPoints>. See Point Cleaning.

auto clean = tf::vtk::cleaned_points(points);
auto clean = tf::vtk::cleaned_points(points, 0.001f);

File I/O

Read and write mesh files directly to/from tf::vtk::polydata. See the I/O module for the underlying algorithms.

read_stl

Read an STL file. Returns vtkSmartPointer<polydata> with cached acceleration structures ready to use.

auto mesh = tf::vtk::read_stl("model.stl");
Normals are not read from the file.

write_stl

Write polydata to binary STL. Returns bool indicating success. Input must contain triangles.

bool ok = tf::vtk::write_stl(polydata, "output.stl");

// With transform applied
bool ok = tf::vtk::write_stl({polydata, matrix}, "output.stl");

read_obj

Read an OBJ file. Returns vtkSmartPointer<polydata>. Only vertices and faces are read.

auto mesh = tf::vtk::read_obj("model.obj");

write_obj

Write polydata to OBJ format. Returns bool indicating success.

bool ok = tf::vtk::write_obj(polydata, "output.obj");

// With transform applied
bool ok = tf::vtk::write_obj({polydata, matrix}, "output.obj");