The Clean module provides high-performance tools for removing duplicate, degenerate, and unreferenced elements from geometric data. It operates directly on arrays and forms, automatically maintaining referential integrity while providing optional index maps for downstream processing.
Cleaning in trueform addresses several common geometric data quality issues:
The module provides both immediate cleaning operations and optional index map returns for tracking transformations.
Remove duplicate points from point collections:
import trueform as tf
import numpy as np
# Basic point cleaning - removes exact duplicates
points = np.array([[0, 0, 0], [1, 0, 0], [0, 0, 0]], dtype=np.float32)
cleaned_points = tf.cleaned(points)
# Tolerance-based cleaning - merges points within distance
tolerance = 1e-6
tolerance_cleaned_points = tf.cleaned(points, tolerance=tolerance)
# Get index map for downstream processing
cleaned_points, (f, kept_ids) = tf.cleaned(
points, tolerance=tolerance, return_index_map=True
)
# Reindex associated data using the map
clean_normals = point_normals[kept_ids]
clean_attributes = point_attributes[kept_ids]
Clean segment collections by removing duplicates, degenerates, and unreferenced points:
# Clean segments - removes duplicate edges and unused points
edges = np.array([[0, 1], [1, 2], [0, 1]], dtype=np.int32)
points = np.array([[0, 0], [1, 0], [1, 1]], dtype=np.float32)
clean_edges, clean_points = tf.cleaned((edges, points))
# Tolerance-based segment cleaning
tolerance = 1e-5
clean_edges, clean_points = tf.cleaned((edges, points), tolerance=tolerance)
# Get both edge and point index maps
(clean_edges, clean_points), (f_edges, kept_edges), (f_points, kept_points) = tf.cleaned(
(edges, points), tolerance=tolerance, return_index_map=True
)
# Reindex edge and point attributes
clean_edge_properties = edge_properties[kept_edges]
clean_point_properties = point_properties[kept_points]
Clean mesh data by removing duplicates, degenerate faces, and unreferenced vertices:
# Clean polygons - removes duplicate faces and unused vertices
faces = np.array([[0, 1, 2], [1, 3, 2], [0, 1, 2]], dtype=np.int32)
points = np.array([[0, 0, 0], [1, 0, 0], [0.5, 1, 0], [1.5, 1, 0]], dtype=np.float32)
clean_faces, clean_points = tf.cleaned((faces, points))
# Tolerance-based polygon cleaning
tolerance = 1e-8
clean_faces, clean_points = tf.cleaned((faces, points), tolerance=tolerance)
# Get both face and point index maps
(clean_faces, clean_points), (f_faces, kept_faces), (f_points, kept_points) = tf.cleaned(
(faces, points), tolerance=tolerance, return_index_map=True
)
# Reindex all associated mesh data
clean_face_normals = face_normals[kept_faces]
clean_vertex_attributes = vertex_attributes[kept_points]
When return_index_map=True, cleaning operations return index maps (f, kept_ids) that track element transformations:
f: Forward mapping array of length N (original element count)
f[old_id] gives the new index for kept elements (0 to M-1)f[old_id] == f.size (equals N) for removed elements (sentinel value)kept_ids: Array of old indices that were kept, length M (new element count)Example with point cleaning:
import numpy as np
# 5 points, where point 2 duplicates point 0
points = np.array([[0, 0], [1, 0], [0, 0], [2, 0], [3, 0]], dtype=np.float32)
colors = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1], [1, 1, 0], [1, 0, 1]])
clean_points, (f, kept_ids) = tf.cleaned(points, return_index_map=True)
# f maps old indices to new (or sentinel for removed)
print(f) # [0, 1, 5, 2, 3]
# Interpretation:
# point 0 -> new index 0 (kept)
# point 1 -> new index 1 (kept)
# point 2 -> 5 (= f.size, removed as duplicate)
# point 3 -> new index 2 (kept)
# point 4 -> new index 3 (kept)
# kept_ids lists which old indices survived
print(kept_ids) # [0, 1, 3, 4]
# Check if element was removed
is_removed = (f == f.size)
print(is_removed) # [False, False, True, False, False]
# Reindex attributes using kept_ids
clean_colors = colors[kept_ids]
print(clean_colors.shape) # (4, 3) - matches clean_points
Example with indexed geometry:
# Face 2 is duplicate of face 0, point 2 duplicates point 1
faces = np.array([[0, 1, 2], [1, 3, 2], [0, 1, 2]], dtype=np.int32)
points = np.array([[0, 0], [1, 0], [1, 0], [2, 0]], dtype=np.float32)
(clean_faces, clean_points), (f_faces, kept_faces), (f_points, kept_points) = tf.cleaned(
(faces, points), return_index_map=True
)
print(f_faces) # [0, 1, 3] - face 2 removed (f_faces[2] == 3 == f_faces.size)
print(kept_faces) # [0, 1] - faces 0 and 1 kept
print(f_points) # [0, 1, 4, 2] - point 2 removed (f_points[2] == 4 == f_points.size)
print(kept_points) # [0, 1, 3] - points 0, 1, 3 kept
# Reindex attributes
face_attrs_clean = face_attrs[kept_faces]
point_attrs_clean = point_attrs[kept_points]