Crate mesh_repair

Crate mesh_repair 

Source
Expand description

Triangle mesh repair and processing utilities.

This crate provides comprehensive tools for loading, validating, repairing, and transforming triangle meshes. It’s designed for 3D printing pipelines, mesh processing, and geometry operations.

§Features

  • File I/O: Load and save STL, OBJ, 3MF, and PLY formats
  • Validation: Check for non-manifold edges, holes, self-intersections, winding issues
  • Repair: Fill holes, fix winding, remove degenerates, weld vertices
  • Analysis: Component detection, wall thickness measurement, volume/surface area
  • Transformation: Decimation, subdivision, isotropic remeshing

§Units and Scale

This library assumes millimeter (mm) units.

  • Default hole filling skips holes larger than 500 edges (adjustable via params)
  • Default vertex welding tolerance is 1e-6 (sub-micron precision)
  • Wall thickness analysis defaults: 1mm minimum for FDM, 0.4mm for SLA
  • Ray casting max distance defaults to 1000mm (1 meter)

If your mesh uses different units, scale accordingly:

  • Meters → mm: Multiply all coordinates by 1000
  • Inches → mm: Multiply all coordinates by 25.4
  • Microns → mm: Divide all coordinates by 1000

§Coordinate System

The library uses a right-handed coordinate system:

  • X: typically width (left/right)
  • Y: typically depth (front/back)
  • Z: typically height (up/down)

Face winding is counter-clockwise (CCW) when viewed from outside the mesh. This means normals point outward by the right-hand rule.

§Quick Start

use mesh_repair::Mesh;

// Load a mesh from any supported format
let mut mesh = Mesh::load("model.stl").unwrap();

// Validate and check for issues
let report = mesh.validate();
println!("{}", report);

// Repair common issues
mesh.repair().unwrap();

// Save to any supported format
mesh.save("repaired.3mf").unwrap();

§Common Workflows

§3D Printing Pipeline

use mesh_repair::{Mesh, RepairParams, ThicknessParams};

let mut mesh = Mesh::load("scan.stl").unwrap();

// Use printing-optimized repair settings
mesh.repair_with_config(&RepairParams::for_printing()).unwrap();

// Check printability requirements
let report = mesh.validate();
if report.is_printable() {
    println!("Mesh is ready for printing!");
} else {
    if !report.is_watertight {
        println!("Has {} boundary edges", report.boundary_edge_count);
    }
    if !report.is_manifold {
        println!("Has {} non-manifold edges", report.non_manifold_edge_count);
    }
    if report.is_inside_out {
        println!("Normals are inverted");
    }
}

// Check wall thickness for FDM printing
let thickness = mesh.analyze_thickness(&ThicknessParams::for_printing());
if thickness.has_thin_regions() {
    println!("Warning: {} thin regions below 0.8mm", thickness.thin_regions.len());
}

mesh.save("print_ready.3mf").unwrap();

§Processing 3D Scans (with RepairBuilder)

use mesh_repair::{Mesh, RepairBuilder};

let mesh = Mesh::load("scan.ply").unwrap();

// Use fluent builder API for repair operations
let result = RepairBuilder::new(mesh)
    .for_scans()                        // Use scan-optimized settings
    .remove_small_components(100)       // Remove debris < 100 faces
    .build()
    .unwrap();

println!("Welded {} vertices, removed {} degenerates",
    result.vertices_welded, result.degenerates_removed);

result.mesh.save("processed_scan.obj").unwrap();

§Processing 3D Scans (with params)

use mesh_repair::{Mesh, RepairParams};

let mut mesh = Mesh::load("scan.ply").unwrap();

// Remove small debris/noise components
let removed = mesh.remove_small_components(100); // Remove components < 100 faces
println!("Removed {} noise components", removed);

// Use scan-optimized repair (smaller hole filling, more aggressive cleanup)
mesh.repair_with_config(&RepairParams::for_scans()).unwrap();

// Remesh for uniform triangle quality
let remeshed = mesh.remesh_with_edge_length(2.0); // 2mm target edge length

remeshed.mesh.save("processed_scan.obj").unwrap();

§CAD Model Cleanup

use mesh_repair::{Mesh, RepairParams};

let mut mesh = Mesh::load("cad_export.stl").unwrap();

// CAD models often have precise vertices that shouldn't be welded aggressively
mesh.repair_with_config(&RepairParams::for_cad()).unwrap();

// Check for self-intersections (common in boolean operation results)
let intersections = mesh.detect_self_intersections();
if !intersections.is_clean() {
    println!("Warning: {} self-intersecting triangle pairs", intersections.intersection_count);
}

mesh.save("cleaned.stl").unwrap();

§Mesh Simplification

use mesh_repair::{Mesh, DecimateParams};

let mesh = Mesh::load("high_poly.obj").unwrap();

// Decimate to 25% of original triangles
let result = mesh.decimate_with_params(&DecimateParams::with_target_ratio(0.25));
println!("Reduced from {} to {} triangles", result.original_triangles, result.final_triangles);

// Or decimate to a specific count
let result = mesh.decimate_to_count(10000);

result.mesh.save("low_poly.obj").unwrap();

§Error Handling

Most operations return MeshResult<T>, which is Result<T, MeshError>.

use mesh_repair::{Mesh, MeshError};

fn process_mesh(path: &str) -> Result<(), MeshError> {
    let mut mesh = Mesh::load(path)?;
    mesh.repair()?;
    mesh.save("output.stl")?;
    Ok(())
}

// Handle specific errors
match Mesh::load("nonexistent.stl") {
    Ok(_) => println!("Loaded successfully"),
    Err(MeshError::IoRead { path, source }) => {
        println!("Failed to read {:?}: {}", path, source);
    }
    Err(MeshError::ParseError { path, details }) => {
        println!("Failed to parse {:?}: {}", path, details);
    }
    Err(MeshError::UnsupportedFormat { extension }) => {
        println!("Unsupported format: {:?}", extension);
    }
    Err(e) => println!("Other error: {}", e),
}

§Troubleshooting

§“Mesh appears inside-out”

This means face normals point inward instead of outward. Fix with:

use mesh_repair::Mesh;
let mut mesh = Mesh::new();
// ... load or create mesh
mesh.fix_winding().unwrap();

§“Mesh has holes / not watertight”

Boundary edges indicate gaps in the surface. Fill holes with:

use mesh_repair::Mesh;
let mut mesh = Mesh::new();
// ... load or create mesh
let filled = mesh.fill_holes().unwrap();
println!("Filled {} holes", filled);

§“Non-manifold edges detected”

This means some edges have more than 2 adjacent faces. Use full repair:

use mesh_repair::{Mesh, RepairParams};
let mut mesh = Mesh::new();
// ... load or create mesh
let mut params = RepairParams::default();
params.fix_non_manifold = true;
mesh.repair_with_config(&params).unwrap();

§“Scale seems wrong”

Check the mesh dimensions and scale if needed:

use mesh_repair::Mesh;
let mesh = Mesh::new();
// ... load or create mesh
if let Some((min, max)) = mesh.bounds() {
    let dims = max - min;
    println!("Dimensions: {:.1} x {:.1} x {:.1} mm", dims.x, dims.y, dims.z);
}
// If dimensions are in meters, they'll be 1000x too small
// If dimensions are in inches, they'll be ~25x too small

§“Multiple disconnected parts”

Keep only the main component or split into separate meshes:

use mesh_repair::Mesh;
let mut mesh = Mesh::new();
// ... load or create mesh
// Option 1: Keep only largest component
let removed = mesh.keep_largest_component();
println!("Removed {} small components", removed);

// Option 2: Split into separate meshes
let parts = mesh.split_components();
for (i, part) in parts.iter().enumerate() {
    println!("Component {}: {} faces", i, part.face_count());
}

§Supported Formats

FormatExtensionLoadSaveIndex PreservationNotes
STL.stlBinary & ASCII, common for printing
OBJ.objASCII, preserves vertex order
3MF.3mfZIP-compressed XML, modern standard
PLY.plyASCII & binary, supports colors/normals

Note: STL format does not preserve vertex indices because it stores triangles independently. OBJ, 3MF, and PLY use indexed storage and preserve vertex order.

Re-exports§

pub use adjacency::MeshAdjacency;
pub use io::Beam;
pub use io::BeamCap;
pub use io::BeamLatticeData;
pub use io::BeamSet;
pub use io::ColorGroup;
pub use io::MeshFormat;
pub use io::ThreeMfExportParams;
pub use io::ThreeMfLoadResult;
pub use io::TriangleColors;
pub use io::load_3mf_with_materials;
pub use io::load_mesh;
pub use io::save_3mf;
pub use io::save_3mf_extended;
pub use io::save_3mf_with_materials;
pub use io::save_mesh;
pub use io::save_obj;
pub use io::save_ply;
pub use io::save_ply_ascii;
pub use io::save_stl;
pub use repair::RepairParams;
pub use repair::compute_vertex_normals;
pub use repair::fix_inverted_faces;
pub use repair::fix_non_manifold_edges;
pub use repair::remove_degenerate_triangles;
pub use repair::remove_degenerate_triangles_enhanced;
pub use repair::remove_duplicate_faces;
pub use repair::remove_unreferenced_vertices;
pub use repair::repair_mesh;
pub use repair::repair_mesh_with_config;
pub use repair::weld_vertices;
pub use components::ComponentAnalysis;
pub use components::find_connected_components;
pub use components::keep_largest_component;
pub use components::remove_small_components;
pub use components::split_into_components;
pub use decimate::DecimateParams;
pub use decimate::DecimateResult;
pub use decimate::decimate_mesh;
pub use decimate::decimate_mesh_with_progress;
pub use holes::BoundaryLoop;
pub use holes::detect_holes;
pub use holes::fill_holes;
pub use holes::fill_holes_with_max_edges;
pub use intersect::IntersectionParams;
pub use intersect::SelfIntersectionResult;
pub use intersect::detect_self_intersections;
pub use remesh::CurvatureResult;
pub use remesh::FeatureEdge;
pub use remesh::FeatureEdgeResult;
pub use remesh::RemeshParams;
pub use remesh::RemeshResult;
pub use remesh::VertexCurvature;
pub use remesh::compute_curvature;
pub use remesh::detect_feature_edges;
pub use remesh::remesh_adaptive;
pub use remesh::remesh_anisotropic;
pub use remesh::remesh_isotropic;
pub use remesh::remesh_isotropic_with_progress;
pub use subdivide::SubdivideParams;
pub use subdivide::SubdivideResult;
pub use subdivide::subdivide_mesh;
pub use thickness::ThicknessParams;
pub use thickness::ThicknessResult;
pub use thickness::ThinRegion;
pub use thickness::analyze_thickness;
pub use validate::DataValidationResult;
pub use validate::MeshReport;
pub use validate::ValidationOptions;
pub use validate::validate_mesh;
pub use validate::validate_mesh_data;
pub use validate::validate_mesh_data_strict;
pub use winding::fix_winding_order;
pub use morph::Constraint;
pub use morph::MorphAlgorithm;
pub use morph::MorphParams;
pub use morph::MorphResult;
pub use morph::RbfKernel;
pub use morph::morph_mesh;
pub use registration::Landmark;
pub use registration::NonRigidParams;
pub use registration::NonRigidRegistrationResult;
pub use registration::RegistrationAlgorithm;
pub use registration::RegistrationParams;
pub use registration::RegistrationResult;
pub use registration::RigidTransform;
pub use registration::align_meshes;
pub use registration::non_rigid_align;
pub use template::ControlRegion;
pub use template::FitParams;
pub use template::FitResult;
pub use template::FitStage;
pub use template::FitTemplate;
pub use template::Measurement;
pub use template::MeasurementType;
pub use template::RegionDefinition;
pub use region::FloodFillCriteria;
pub use region::MaterialProperties;
pub use region::MaterialZone;
pub use region::MeshRegion;
pub use region::RegionMap;
pub use region::RegionSelector;
pub use region::ThicknessMap;
pub use assembly::Assembly;
pub use assembly::AssemblyExportFormat;
pub use assembly::AssemblyValidation;
pub use assembly::BillOfMaterials;
pub use assembly::BomItem;
pub use assembly::ClearanceResult;
pub use assembly::Connection;
pub use assembly::ConnectionParams;
pub use assembly::ConnectionType;
pub use assembly::InterferenceResult;
pub use assembly::Part;
pub use lattice::DensityMap;
pub use lattice::InfillParams;
pub use lattice::InfillResult;
pub use lattice::LatticeParams;
pub use lattice::LatticeResult;
pub use lattice::LatticeType;
pub use lattice::generate_infill;
pub use lattice::generate_lattice;
pub use boolean::BooleanOp;
pub use boolean::BooleanParams;
pub use boolean::BooleanResult;
pub use boolean::BooleanStats;
pub use boolean::CoplanarStrategy;
pub use boolean::boolean_operation;
pub use boolean::boolean_operation_with_progress;
pub use scan::DenoiseMethod;
pub use scan::DenoiseParams;
pub use scan::DenoiseResult;
pub use scan::HoleFillParams;
pub use scan::HoleFillResult;
pub use scan::HoleFillStrategy;
pub use scan::OutlierRemovalParams;
pub use scan::ScanCleanupParams;
pub use scan::ScanCleanupResult;
pub use scan::cleanup_scan;
pub use scan::denoise_mesh;
pub use scan::fill_holes_advanced;
pub use scan::remove_outliers;
pub use multiscan::MergeParams;
pub use multiscan::MergeResult;
pub use multiscan::MultiAlignmentParams;
pub use multiscan::MultiAlignmentResult;
pub use multiscan::OverlapHandling;
pub use multiscan::OverlapRegion;
pub use multiscan::align_multiple_scans;
pub use multiscan::align_multiple_scans_with_params;
pub use multiscan::merge_scans;
pub use printability::IssueSeverity as PrintIssueSeverity;
pub use printability::OrientParams;
pub use printability::OrientResult;
pub use printability::OverhangRegion;
pub use printability::PrintIssue;
pub use printability::PrintIssueType;
pub use printability::PrintTechnology;
pub use printability::PrintValidation;
pub use printability::PrinterConfig;
pub use printability::SupportAnalysis;
pub use printability::SupportRegion;
pub use printability::ThinWallRegion;
pub use printability::auto_orient_for_printing;
pub use printability::detect_support_regions;
pub use printability::validate_for_printing;
pub use measure::CrossSection;
pub use measure::Dimensions;
pub use measure::DistanceMeasurement;
pub use measure::OrientedBoundingBox;
pub use measure::circumference_at_height;
pub use measure::closest_point_on_mesh;
pub use measure::cross_section;
pub use measure::cross_sections;
pub use measure::dimensions;
pub use measure::measure_distance;
pub use measure::oriented_bounding_box;
pub use slice::Contour;
pub use slice::FdmParams;
pub use slice::FdmValidationResult;
pub use slice::GapIssue;
pub use slice::Layer;
pub use slice::LayerBounds;
pub use slice::LayerStats;
pub use slice::SlaParams;
pub use slice::SlaValidationResult;
pub use slice::SliceParams;
pub use slice::SliceResult;
pub use slice::SmallFeatureIssue;
pub use slice::SvgExportParams;
pub use slice::ThinWallIssue;
pub use slice::calculate_layer_stats;
pub use slice::export_3mf_slices;
pub use slice::export_layer_svg;
pub use slice::export_slices_svg;
pub use slice::slice_mesh;
pub use slice::slice_preview;
pub use slice::validate_for_fdm;
pub use slice::validate_for_sla;
pub use pointcloud::CloudPoint;
pub use pointcloud::PointCloud;
pub use pointcloud::PointCloudFormat;
pub use pointcloud::ReconstructionAlgorithm;
pub use pointcloud::ReconstructionParams;
pub use pointcloud::ReconstructionResult;
pub use progress::OperationEstimate;
pub use progress::OperationType;
pub use progress::Progress;
pub use progress::ProgressCallback;
pub use progress::ProgressReporter;
pub use progress::ProgressTracker;
pub use progress::SharedProgressTracker;
pub use progress::estimate_operation_time;
pub use tracing_ext::OperationTimer;
pub use tracing_ext::log_io_operation;
pub use tracing_ext::log_mesh_stats;
pub use tracing_ext::log_mesh_stats_detailed;
pub use tracing_ext::log_perf_section;
pub use tracing_ext::log_progress;
pub use tracing_ext::log_repair_result;
pub use tracing_ext::log_validation_result;

Modules§

adjacency
Mesh topology queries via adjacency structures.
assembly
Multi-part assembly management.
boolean
Mesh boolean operations.
components
Connected component analysis for meshes.
decimate
Mesh decimation using edge collapse with quadric error metrics.
holes
Hole detection and filling for mesh repair.
intersect
Self-intersection detection for meshes.
io
Mesh file I/O for STL, OBJ, and 3MF formats.
lattice
Lattice and infill structure generation.
measure
Measurement and dimensioning tools.
morph
Mesh morphing and deformation algorithms.
multiscan
Multi-scan alignment and merging.
pointcloud
Point cloud data structures and surface reconstruction.
printability
Print validation and manufacturing analysis.
progress
Progress reporting and operation estimation for long-running operations.
region
Mesh region definition and management.
registration
Mesh registration and alignment algorithms.
remesh
Isotropic and adaptive remeshing for uniform edge lengths and improved triangle quality.
repair
Mesh repair operations: degenerate removal, welding, compaction.
scan
Scan processing and cleanup utilities.
slice
Slicing and layer preview for 3D printing.
subdivide
Mesh subdivision for surface smoothing.
template
Template-based mesh fitting.
thickness
Wall thickness analysis for meshes.
tracing_ext
Tracing extensions for mesh operations.
validate
Mesh validation and reporting.
winding
Normal consistency and winding order correction.

Macros§

mesh_span
Macro for creating instrumented mesh operation spans.

Structs§

FittingBuilder
Fluent builder for mesh fitting workflows.
FittingResult
Result from FittingBuilder containing the fitted mesh and statistics.
Mesh
A triangle mesh with indexed vertices and faces.
Pipeline
A mesh processing pipeline.
PipelineResult
Result of a pipeline execution.
RepairBuilder
Fluent builder for mesh repair operations.
RepairResult
Result from RepairBuilder containing the repaired mesh and statistics.
Triangle
A triangle with concrete vertex positions.
Vertex
A vertex in the mesh with optional computed attributes.
VertexColor
RGB color with 8-bit components.

Enums§

ErrorCode
Machine-readable error codes for mesh operations.
IssueSeverity
Severity levels for validation issues.
MeshError
Errors that can occur during mesh operations.
RecoverySuggestion
Recovery suggestions for mesh errors.
ValidationIssue
Validation issues that can be collected during mesh validation.

Traits§

IntoPipeline
Trait for types that can be converted into a Pipeline.

Type Aliases§

MeshResult
Result type alias for mesh operations.