The Reindex module provides efficient tools for extracting, filtering, and reorganizing geometric data while maintaining referential integrity. It operates on trueform's range-based primitives and supports both simple data extraction and complex multi-level reindexing with automatic index map generation.
Include the module with:
#include <trueform/reidx.hpp>
Reindexing in trueform revolves around two key concepts:
The module provides both immediate reindexing operations and optional index map returns for downstream processing.
Reindex point and vector collections using index maps:
// Basic point reindexing with an existing index map
tf::index_map_buffer<int> point_map;
auto reindexed_points = tf::reindexed(points, point_map);
// Reindex vectors similarly
auto reindexed_vectors = tf::reindexed(vectors, point_map);
auto reindexed_unit_vectors = tf::reindexed(unit_vectors, point_map);
// Reindex into pre-allocated buffers
tf::points_buffer<float, 3> point_buffer;
tf::reindexed(points, point_map, point_buffer);
// Reindex generic ranges
auto reindexed_attributes = tf::reindexed(tf::make_range(attribute_range), point_map);
For primitives that reference other data (like polygons and segments), reindexing requires both element and point index maps:
// Polygon reindexing requires both face and point index maps
tf::index_map_buffer<int> face_map = /* ... */;
tf::index_map_buffer<int> point_map = /* ... */;
auto reindexed_polygons = tf::reindexed(polygons, face_map, point_map);
// Segment reindexing works similarly
tf::index_map_buffer<int> edge_map = /* ... */;
auto reindexed_segments = tf::reindexed(segments, edge_map, point_map);
// Reindex into pre-allocated buffers for performance
tf::polygons_buffer<int, float, 3, 3> poly_buffer; // triangles
tf::reindexed(polygons, face_map, point_map, poly_buffer);
// Dynamic polygon sizes
tf::polygons_buffer<int, float, 3, tf::dynamic_size> dynamic_buffer;
tf::reindexed(polygons, face_map, point_map, dynamic_buffer);
Filter data using boolean masks, automatically generating index maps:
// Create a mask for filtering
tf::buffer<bool> point_mask;
point_mask.allocate(points.size());
// Mark specific points to keep
tf::parallel_apply(tf::zip(point_mask, points), [&](auto pair) {
auto &&[masked, point] = pair;
masked = meets_criteria(point);
});
// Reindex points by mask
auto filtered_points = tf::reindexed_by_mask<int>(points, point_mask);
// Get index map for downstream processing
auto [filtered_points, index_map] =
tf::reindexed_by_mask<int>(points, point_mask, tf::return_index_map);
For complex primitives, mask-based reindexing automatically handles dependent point filtering:
// Face mask for polygon filtering
tf::buffer<bool> face_mask;
face_mask.allocate(polygons.size());
tf::parallel_transform(polygons, face_mask, [&](auto poly) {
return tf::area(poly) > min_area_threshold;
});
// Reindex polygons - automatically filters unused points
auto [filtered_polygons, face_map, point_map] =
tf::reindexed_by_mask<int>(polygons, face_mask, tf::return_index_map);
// Use maps to reindex associated data
auto filtered_normals = tf::reindexed(face_normals, face_map);
auto filtered_attributes = tf::reindexed(point_attributes, point_map);
Extract specific elements by their IDs:
// Extract specific points by ID
std::vector<int> desired_point_ids = {10, 25, 30, 45};
auto extracted_points = tf::reindexed_by_ids<int>(points, desired_point_ids);
// Get index map for consistency
auto [extracted_points, point_map] =
tf::reindexed_by_ids<int>(points, desired_point_ids, tf::return_index_map);
// Extract specific polygons
std::vector<int> face_ids = {0, 2, 5, 8};
auto [extracted_polygons, face_map, generated_point_map] =
tf::reindexed_by_ids<int>(polygons, face_ids, tf::return_index_map);
Filter complex primitives based on point criteria:
// Create point mask based on spatial criteria
tf::buffer<bool> point_mask;
point_mask.allocate(points.size());
tf::parallel_transform(points, point_mask, [&](auto point) {
return point[2] > height_threshold; // Keep points above certain height
});
// Filter polygons - keep only those with ALL vertices in the mask
auto [filtered_polygons, face_map, point_map] =
tf::reindexed_by_mask_on_points<int>(polygons, point_mask, tf::return_index_map);
// Filter segments similarly
auto [filtered_segments, edge_map, point_map_segments] =
tf::reindexed_by_mask_on_points<int>(segments, point_mask, tf::return_index_map);
// Select specific point IDs
tf::buffer<int> selected_point_ids;
tf::generic_generate(tf::enumerate(points), selected_point_ids, [&](auto pair, auto &buffer) {
auto [point_id, point] = pair;
if (is_feature_point(point)) {
buffer.push_back(point_id);
}
});
// Extract polygons that use only the selected points
auto [feature_polygons, face_map, point_map] =
tf::reindexed_by_ids_on_points<int>(polygons, selected_point_ids, tf::return_index_map);
The tf::concatenated function efficiently merges multiple geometric structures into a single unified structure, automatically handling index offsetting to maintain referential integrity.
// Works with fixed-size polygons (e.g., all triangles)
tf::polygons_buffer<int, float, 3, 3> triangles1, triangles2;
auto combined_triangles = tf::concatenated(triangles1.polygons(),
triangles2.polygons());
// Also works with mixed polygon sizes (dynamic)
tf::polygons_buffer<int, float, 3, tf::dynamic_size> quads, tris;
auto mixed_mesh = tf::concatenated(quads.polygons(), tris.polygons());
// Result automatically uses dynamic_size when inputs differ
// Merge multiple line segment collections
auto edges1 = extract_boundary_edges(mesh1);
auto edges2 = extract_boundary_edges(mesh2);
auto all_edges = tf::concatenated<int>(edges1, edges2);
// Each segment collection has its own points - concatenated handles the offsets
// 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]
The tf::split_into_components function performs the inverse operation of concatenation—it takes a single mesh with per-element labels and splits it into multiple separate meshes, one for each unique label.
This is particularly useful after topology analysis operations like tf::label_connected_components, allowing you to extract individual components for separate processing, visualization, or export.
// Build topology and label connected components
tf::face_membership<int> fm;
fm.build(polygons);
tf::face_link<int> fl;
fl.build(polygons.faces(), fm);
tf::buffer<int> labels;
labels.allocate(polygons.size());
int n_components = tf::label_connected_components<int>(
labels, tf::make_applier(fl));
// Split mesh into separate components
auto [components, component_labels] = tf::split_into_components(polygons, labels);
// Returns std::pair<std::vector<tf::polygons_buffer<...>>, std::vector<label_type>>
std::cout << "Split into " << components.size() << " meshes" << std::endl;
// Process each component independently
for (auto [component, label] : tf::zip(components, component_labels)) {
// component is tf::polygons_buffer<Index, Real, Dims, N>
// Each component is a complete, self-contained mesh
auto bbox = tf::aabb_from(component.polygons());
}
// Also works for segments
auto [edge_components, edge_labels] = tf::split_into_components(segments, edge_labels);
// Returns std::pair<std::vector<tf::segments_buffer<...>>, std::vector<label_type>>
tf::concatenated: Multiple meshes → Single meshtf::split_into_components: Single mesh + labels → Multiple meshes