The Reindex module provides efficient tools for extracting, filtering, and reorganizing geometric data while maintaining referential integrity. It operates on arrays and forms, supporting both simple data extraction and complex multi-level reindexing with automatic index map generation.
Reindexing in trueform revolves around two key concepts:
The module provides both immediate reindexing operations and optional index map returns for downstream processing.
Filter data using boolean masks:
import trueform as tf
import numpy as np
# Create mesh with mask
faces = np.array([[0, 1, 2], [1, 3, 2], [2, 3, 4]], dtype=np.int32)
points = np.array([[0, 0], [1, 0], [0.5, 1], [1.5, 1], [1, 2]], dtype=np.float32)
face_mask = np.array([True, False, True], dtype=bool)
# Reindex by mask - automatically filters unused points
filtered_faces, filtered_points = tf.reindex_by_mask((faces, points), face_mask)
# With Mesh object
mesh = tf.Mesh(faces, points)
filtered_faces, filtered_points = tf.reindex_by_mask(mesh, face_mask)
# Filter by geometric criteria
areas = np.array([compute_triangle_area(points[f]) for f in faces])
large_faces = areas > 0.1
filtered_mesh = tf.reindex_by_mask(mesh, large_faces)
With index maps:
# Get index maps for downstream processing
(filtered_faces, filtered_points), (f_faces, kept_faces), (f_points, kept_points) = tf.reindex_by_mask(
(faces, points), face_mask, return_index_map=True
)
# Use maps to reindex associated data
filtered_face_normals = face_normals[kept_faces]
filtered_point_attributes = point_attributes[kept_points]
Extract specific elements by their IDs:
# Extract specific faces by ID
face_ids = np.array([0, 5, 10, 15], dtype=np.int32)
subset_faces, subset_points = tf.reindex_by_ids((faces, points), face_ids)
# With Mesh object
subset_mesh = tf.reindex_by_ids(mesh, face_ids)
# Extract specific edges
edge_ids = np.array([2, 4, 6], dtype=np.int32)
subset_edges = tf.reindex_by_ids(edge_mesh, edge_ids)
# Extract specific points
point_ids = np.array([10, 25, 30, 45], dtype=np.int32)
extracted_points = tf.reindex_by_ids(point_cloud, point_ids)
With index maps:
# Get index maps for consistency
(subset_faces, subset_points), (f_faces, kept_faces), (f_points, kept_points) = tf.reindex_by_ids(
(faces, points), face_ids, return_index_map=True
)
# Reindex associated attributes
subset_face_attrs = face_attrs[kept_faces]
subset_point_attrs = point_attrs[kept_points]
Filter faces/edges based on point criteria rather than face/edge criteria.
# Create point mask based on spatial criteria
point_mask = points[:, 2] > height_threshold # Keep points above certain height
# Filter polygons - keep only those with ALL vertices in the mask
filtered_faces, filtered_points = tf.reindex_by_mask_on_points((faces, points), point_mask)
# With Mesh object
filtered_faces, filtered_points = tf.reindex_by_mask_on_points(mesh, point_mask)
# Filter segments similarly
filtered_edges, filtered_points = tf.reindex_by_mask_on_points(edge_mesh, point_mask)
With index maps:
(filtered_faces, filtered_points), (f_faces, kept_faces), (f_points, kept_points) = tf.reindex_by_mask_on_points(
(faces, points), point_mask, return_index_map=True
)
# Reindex associated attributes
filtered_face_normals = face_normals[kept_faces]
filtered_point_attrs = point_attrs[kept_points]
# Select specific point IDs (e.g., feature points)
selected_point_ids = np.array([10, 25, 30, 45], dtype=np.int32)
# Extract faces that use only the selected points
filtered_faces, filtered_points = tf.reindex_by_ids_on_points((faces, points), selected_point_ids)
# With Mesh object
filtered_faces, filtered_points = tf.reindex_by_ids_on_points(mesh, selected_point_ids)
With index maps:
(filtered_faces, filtered_points), (f_faces, kept_faces), (f_points, kept_points) = tf.reindex_by_ids_on_points(
(faces, points), selected_point_ids, return_index_map=True
)
Merge multiple geometric structures into a single unified structure:
# Concatenate meshes
mesh1 = tf.Mesh(faces1, points1)
mesh2 = tf.Mesh(faces2, points2)
mesh3 = tf.Mesh(faces3, points3)
combined_faces, combined_points = tf.concatenated([mesh1, mesh2, mesh3])
combined_mesh = tf.Mesh(combined_faces, combined_points)
# Concatenate edge meshes
edges1 = tf.EdgeMesh(e1, p1)
edges2 = tf.EdgeMesh(e2, p2)
combined_edges, combined_points = tf.concatenated([edges1, edges2])
# Also works with tuples
data = [(faces1, points1), (faces2, points2)]
combined_faces, combined_points = tf.concatenated(data)
Index offsetting is handled automatically. If edges1 references points 0-99 and edges2 references points 0-49, the result will have edges1 as-is and edges2 offset to reference 100-149.
Mesh or EdgeMesh objects that have transformations set, the transformations are automatically applied to the points before concatenation. This enables assembly workflows where parts are positioned independently.OffsetBlockedArray).Split a mesh with per-element labels into multiple separate geometries:
# Create labels (one per face)
labels = np.array([0, 0, 1, 1, 2, 2], dtype=np.int32)
# Split into separate components
components, comp_labels = tf.split_into_components(mesh, labels)
# components: list of (faces, points) tuples
# comp_labels: array of unique label values (sorted)
print(f"Split into {len(components)} components")
print(f"Component labels: {comp_labels}") # e.g., [0, 1, 2]
# Process each component independently
for (comp_faces, comp_points), label in zip(components, comp_labels):
print(f"Component {label}: {len(comp_faces)} faces, {len(comp_points)} points")
# Create Mesh from component if needed
comp_mesh = tf.Mesh(comp_faces, comp_points)
# Works with edge meshes too
edge_components, edge_labels = tf.split_into_components(edge_mesh, edge_labels)
for (comp_edges, comp_points), label in zip(edge_components, edge_labels):
print(f"Edge component {label}: {len(comp_edges)} edges")
concatenated: Multiple meshes → Single meshsplit_into_components: Single mesh + labels → Multiple meshesAll reindex functions work with dynamic meshes (n-gons via OffsetBlockedArray):
| Input Type | Returns connectivity as |
|---|---|
Fixed-size indices (N, V) | np.ndarray (M, V) |
Dynamic indices OffsetBlockedArray | OffsetBlockedArray |
# Reindex dynamic mesh by IDs
quads = tf.as_offset_blocked(np.array([[0,1,2,3], [4,5,6,7]], dtype=np.int32))
ids = np.array([0], dtype=np.int32)
new_faces, new_points = tf.reindex_by_ids((quads, points), ids)
# new_faces is OffsetBlockedArray
# Split dynamic mesh into components
components, labels = tf.split_into_components((quads, points), face_labels)
# Each component's faces is OffsetBlockedArray
When return_index_map=True, reindexing operations return index maps (f, kept_ids) that track element transformations. These maps enable reindexing of associated attributes to match the reindexed geometry:
# Example with reindex_by_mask
(new_faces, new_points), (f_faces, kept_faces), (f_points, kept_points) = tf.reindex_by_mask(
mesh, face_mask, return_index_map=True
)
# Reindex all associated data
new_face_normals = face_normals[kept_faces]
new_point_colors = point_colors[kept_points]
f and kept_ids), see Understanding Index Maps in the Clean module documentation.