Modules | C++

Clean

Remove duplicates, degenerates, and unreferenced elements.

The Clean module removes duplicate, degenerate, and unreferenced elements from geometric data. It operates on trueform's primitive ranges, maintains referential integrity, and provides optional index maps for reindexing associated data.

Include the module with:

#include <trueform/clean.hpp>

What Gets Removed

GeometryRemoves
PointsDuplicate vertices
SegmentsDuplicate vertices, duplicate edges, degenerate edges, unreferenced vertices
PolygonsDuplicate vertices, duplicate faces, degenerate faces, unreferenced vertices
CurvesDuplicate vertices, degenerate edges, unreferenced vertices

Definitions:

  • Duplicate vertices: Points at the same location (exact or within tolerance)
  • Duplicate edges: Edges connecting the same pair of vertices (either direction)
  • Duplicate faces: Faces with the same vertex set (any cyclic rotation, either winding)
  • Degenerate edges: Zero-length edges (both endpoints are the same vertex)
  • Degenerate faces: Faces with fewer than 3 unique vertices (zero area)
  • Unreferenced vertices: Vertices not used by any edge or face

When duplicates are removed, the element with the smallest index is kept.

Points

Remove duplicate points from point collections:

point_cleaning.cpp
// Exact duplicate removal
auto clean_points = tf::cleaned(points);

// Tolerance-based: merge points within distance
auto clean_points = tf::cleaned(points, 1e-6f);

// Get index map for reindexing associated data
auto [clean_points, point_map] = tf::cleaned(points, tolerance, tf::return_index_map);
auto clean_normals = tf::reindexed(point_normals, point_map);

For points (and soups), the index type defaults to int. Specify explicitly for large data:

point_index_type.cpp
auto clean_points = tf::cleaned<int64_t>(points);

Segments

Clean segment collections:

segment_cleaning.cpp
// Clean segments
auto clean_segments = tf::cleaned(segments);

// With tolerance
auto clean_segments = tf::cleaned(segments, 1e-5f);

// Get both edge and point index maps
auto [clean_segments, edge_map, point_map] =
    tf::cleaned(segments, tolerance, tf::return_index_map);

// Reindex associated data
auto clean_edge_attrs = tf::reindexed(tf::make_edges(edge_attrs), edge_map);
auto clean_point_attrs = tf::reindexed(point_attrs, point_map);

The index type is deduced from segments.edges()[0][0].

Polygons

Clean polygon meshes:

polygon_cleaning.cpp
// Clean polygons
auto clean_polygons = tf::cleaned(polygons);

// With tolerance
auto clean_polygons = tf::cleaned(polygons, 1e-8);

// Get both face and point index maps
auto [clean_polygons, face_map, point_map] =
    tf::cleaned(polygons, tolerance, tf::return_index_map);

// Reindex associated data
auto clean_face_normals = tf::reindexed(face_normals, face_map);
auto clean_vertex_attrs = tf::reindexed(vertex_attrs, point_map);

The index type is deduced from polygons.faces()[0][0]. Specify explicitly for large meshes:

polygon_index_type.cpp
auto clean_polygons = tf::cleaned<int64_t>(polygons);

Curves

Clean curve collections (connected paths):

curve_cleaning.cpp
// Clean curves
auto clean_curves = tf::cleaned(curves);

// With tolerance
auto clean_curves = tf::cleaned(curves, 1e-5f);

// Get point index map (edge map not available since paths are reconnected)
auto [clean_curves, point_map] = tf::cleaned(curves, tolerance, tf::return_index_map);
Unlike segments, curve cleaning only returns a point index map. Edge mapping is not available because degenerate edge removal changes path topology.

Soups

Polygon and segment soups (unindexed geometry) can be cleaned, converting them to indexed geometry:

soup_cleaning.cpp
// Triangle soup: each element stores its own vertices
auto soup = tf::make_polygons(tf::make_blocked_range<3>(raw_points));

// Clean converts to indexed geometry with shared vertices
auto clean_mesh = tf::cleaned(soup);

// With tolerance
auto clean_mesh = tf::cleaned(soup, 1e-6f);
Cleaning a soup returns indexed geometry. This converts raw triangle data into a proper mesh with shared vertices.

Index Maps

All cleaning operations can return index maps for tracking how elements were remapped:

index_maps.cpp
auto [clean_polygons, face_map, point_map] =
    tf::cleaned(polygons, tf::return_index_map);

// Use maps to reindex associated data
auto clean_face_colors = tf::reindexed(face_colors, face_map);
auto clean_vertex_uvs = tf::reindexed(vertex_uvs, point_map);
See Core for index map details, and Reindex for applying them to geometric data.

Low-Level API

For more control, use tf::make_clean_index_map directly:

make_clean_index_map.cpp
// Points
auto point_map = tf::make_clean_index_map(points);
auto point_map = tf::make_clean_index_map(points, tolerance);

// Segments
auto [edge_map, point_map] = tf::make_clean_index_map(segments);
auto [edge_map, point_map] = tf::make_clean_index_map(segments, tolerance);

// Polygons
auto [face_map, point_map] = tf::make_clean_index_map(polygons);
auto [face_map, point_map] = tf::make_clean_index_map(polygons, tolerance);

// Explicit index type
auto [face_map, point_map] = tf::make_clean_index_map<int64_t>(polygons);
All cleaning operations maintain referential integrity: after removing elements, all edge/face indices are correctly updated to reference the new vertex positions.