Modules | C++

Remesh

Decimation, isotropic remeshing, and edge collapse.

The Remesh module provides tools for modifying triangle mesh resolution: reducing face count through decimation and edge collapse, or redistributing vertices through isotropic remeshing. All functions operate on triangle meshes and return a new mesh along with the resulting half-edge structure.

Include the module with:

#include <trueform/remesh.hpp>
All remesh functions require triangle meshes (static face size of 3). Polygonal meshes must be triangulated first using tf::triangulated from the Geometry module.
All remesh operations use parallel execution by default. Set config.parallel = false for sequential execution (e.g. when processing many meshes in parallel externally).
All edge length decisions (split thresholds, collapse thresholds, max edge length checks) respect tagged transformations. When the input polygons have a tagged frame, lengths are measured in the transformed coordinate space. This allows remeshing a scaled or rotated mesh without modifying vertex data.

Decimation

Reduce face count using quadric error metrics. The algorithm collapses edges in priority order, placing the new vertex at the position that minimizes geometric error.

Basic Usage

decimated.cpp
auto mesh = tf::read_stl("model.stl");
auto polys = mesh.polygons();

// Decimate to 10% of original faces
auto [result, he] = tf::decimated(polys, 0.1f);

// result is a tf::polygons_buffer, he is a tf::half_edges
for (auto tri : result.polygons()) {
    auto [pt0, pt1, pt2] = tri;
}

With Configuration

decimated_config.cpp
tf::decimate_config<float> config;
config.preserve_boundary = true;
config.max_aspect_ratio = 20;
config.parallel = false; // sequential execution

auto [result, he] = tf::decimated(polys, 0.1f, config);

decimate_config parameters:

ParameterDefaultDescription
max_aspect_ratio40Maximum triangle aspect ratio after collapse. Set negative to disable
preserve_boundaryfalseIf true, boundary edges are never collapsed
stabilizer1e-3Tikhonov stabilizer for quadric solve
paralleltrueIf true, use parallel partitioned collapse

With Index Maps

Pass tf::return_index_map to also receive face and vertex index maps that track which elements survived the decimation:

decimated_index_map.cpp
auto [result, he, face_map, vertex_map] =
    tf::decimated(polys, 0.1f, tf::return_index_map);

// Or with config:
auto [result, he, face_map, vertex_map] =
    tf::decimated(polys, 0.1f, config, tf::return_index_map);

With Pre-Computed Half-Edges

When half-edges are already available (e.g. from a previous operation), tag them onto the polygons to skip rebuilding:

decimated_precompute.cpp
tf::half_edges<int> he(polys);

// Tag half-edges onto polygons - decimated reuses them
auto [result, he_out] = tf::decimated(polys | tf::tag(he), 0.1f);

Isotropic Remeshing

Redistribute vertices to achieve uniform edge lengths. Each iteration splits long edges, collapses short edges, flips edges to improve valence, and relaxes vertex positions tangentially.

Basic Usage

isotropic_remeshed.cpp
auto mesh = tf::read_stl("model.stl");
auto polys = mesh.polygons();

// Remesh to target edge length
float mel = tf::mean_edge_length(polys);
auto [result, he] = tf::isotropic_remeshed(polys, 2.0f * mel);

With Configuration

isotropic_remeshed_config.cpp
tf::remesh_config<float> config;
config.target_length = 2.0f * mel;
config.iterations = 5;
config.relaxation_iters = 5;
config.preserve_boundary = true;

auto [result, he] = tf::isotropic_remeshed(polys, config);

remesh_config parameters:

ParameterDefaultDescription
target_lengthTarget edge length. Edges longer are split, shorter are collapsed
iterations3Number of outer iterations (split + collapse + flip + relax)
relaxation_iters3Number of tangential relaxation iterations per outer iteration
max_aspect_ratio-1Maximum triangle aspect ratio after collapse. Set negative to disable
lambda0.5Damping factor for tangential relaxation in (0, 1]
preserve_boundaryfalseIf true, boundary edges are never split or collapsed
use_quadricfalseIf true, use quadric error metric for collapse vertex placement
paralleltrueIf true, use parallel collapse during the collapse step

With Pre-Computed Half-Edges

isotropic_remeshed_precompute.cpp
tf::half_edges<int> he(polys);
auto [result, he_out] = tf::isotropic_remeshed(polys | tf::tag(he), 2.0f * mel);

Edge Collapse

Collapse edges shorter than a threshold. Unlike decimation which uses quadric error ordering, this collapses by edge length — shortest first.

Basic Usage

collapsed_short_edges.cpp
auto mesh = tf::read_stl("model.stl");
auto polys = mesh.polygons();

float mel = tf::mean_edge_length(polys);

// Collapse edges shorter than 3/4 mel
auto [result, he] = tf::collapsed_short_edges(polys, 0.75f * mel);

With Configuration

collapsed_short_edges_config.cpp
tf::length_collapse_config<float> config;
config.max_length = 1.25f * mel;
config.preserve_boundary = true;
config.use_quadric = true;

auto [result, he] = tf::collapsed_short_edges(polys, 0.75f * mel, config);

length_collapse_config parameters:

ParameterDefaultDescription
max_lengthmaxMaximum edge length allowed after a collapse
preserve_boundaryfalseIf true, boundary edges are never collapsed
use_quadrictrueIf true, use quadric error metric for vertex placement
max_aspect_ratio40Maximum triangle aspect ratio after collapse. Set negative to disable
stabilizer1e-6Tikhonov stabilizer for quadric solve
paralleltrueIf true, use parallel partitioned collapse

With Index Maps

Pass tf::return_index_map to also receive face and vertex index maps:

collapsed_short_edges_index_map.cpp
auto [result, he, face_map, vertex_map] =
    tf::collapsed_short_edges(polys, 0.75f * mel, tf::return_index_map);

// Or with config:
auto [result, he, face_map, vertex_map] =
    tf::collapsed_short_edges(polys, 0.75f * mel, config, tf::return_index_map);

With Pre-Computed Half-Edges

collapsed_short_edges_precompute.cpp
tf::half_edges<int> he(polys);
auto [result, he_out] = tf::collapsed_short_edges(polys | tf::tag(he), 0.75f * mel);