runmat-meshing 0.6.0

Topology-first meshing pipeline orchestration for RunMat
Documentation
use runmat_geometry_core::{CadCurveEvaluationSample, GeometryAsset};
use runmat_meshing_cad::{
    build_cad_evaluation_model, build_cad_topology, extract_source_topology, project_to_face,
    CadEvaluationModel,
};
use runmat_meshing_core::{
    quality::predicate::Point3, AnalysisMeshArtifact, MeshBackendKind, MeshSizingField,
    TopologyEntityId, VolumeMeshingOptions,
};
use runmat_meshing_curve::{
    build_curve_mesh_contract, discretize_cad_topology_curves_with_sizing_and_provider,
    CadCurveEvaluationRequest, CadCurveEvaluatorProvider, CurveValidationOptions,
};
use runmat_meshing_plc::build::build_protected_boundary_complex;
use runmat_meshing_surface::{
    build_surface_mesh_contract, discretize_cad_topology_surfaces_with_cad_curves_and_sizing,
    validate_cad_topology_surface_discretization, SurfaceValidationOptions,
};
use runmat_meshing_tetrahedron::{
    optimize::{
        optimize_recovered_tetrahedron_mesh, RecoveredTetrahedronMeshOptimizationOptions,
        TetrahedronBoundarySmoothingProjection, TetrahedronBoundarySmoothingProjector,
    },
    recover::recover_tetrahedron_mesh_from_plc,
    structured_grid,
};

mod artifact;
mod error;
mod options;
mod sizing;
mod stage_options;
mod tetrahedron_stage;

use artifact::{
    analysis_artifact_from_tetrahedron_mesh, backend_quality_evidence_from_tetrahedron_mesh,
};
pub use error::SolidMeshingError;
use options::validate_solid_options;
use sizing::sizing_with_application_evidence;
use stage_options::{curve_discretization_options, surface_discretization_options};
use tetrahedron_stage::generate_solid_tetrahedron_mesh;

pub fn generate_analysis_mesh(
    geometry: &GeometryAsset,
    options: VolumeMeshingOptions,
) -> Result<AnalysisMeshArtifact, SolidMeshingError> {
    match options.backend {
        MeshBackendKind::Auto | MeshBackendKind::Solid => generate_solid_analysis_mesh(
            geometry,
            &VolumeMeshingOptions {
                backend: MeshBackendKind::Solid,
                ..options
            },
        ),
        MeshBackendKind::StructuredGridTetrahedron => {
            structured_grid::generate_analysis_mesh(geometry, options)
                .map_err(SolidMeshingError::StructuredGrid)
        }
    }
}

pub fn generate_analysis_mesh_with_sizing(
    geometry: &GeometryAsset,
    options: VolumeMeshingOptions,
    sizing: &MeshSizingField,
) -> Result<AnalysisMeshArtifact, SolidMeshingError> {
    match options.backend {
        MeshBackendKind::Auto | MeshBackendKind::Solid => generate_solid_analysis_mesh_with_sizing(
            geometry,
            &VolumeMeshingOptions {
                backend: MeshBackendKind::Solid,
                ..options
            },
            sizing,
        ),
        MeshBackendKind::StructuredGridTetrahedron => {
            structured_grid::generate_analysis_mesh_with_sizing(geometry, options, sizing)
                .map_err(SolidMeshingError::StructuredGrid)
        }
    }
}

pub fn generate_solid_analysis_mesh(
    geometry: &GeometryAsset,
    options: &VolumeMeshingOptions,
) -> Result<AnalysisMeshArtifact, SolidMeshingError> {
    generate_solid_analysis_mesh_with_sizing(geometry, options, &MeshSizingField::default())
}

pub fn generate_solid_analysis_mesh_with_sizing(
    geometry: &GeometryAsset,
    options: &VolumeMeshingOptions,
    sizing: &MeshSizingField,
) -> Result<AnalysisMeshArtifact, SolidMeshingError> {
    validate_solid_options(options)?;

    let topology = extract_source_topology(geometry).map_err(SolidMeshingError::SourceTopology)?;
    let cad_topology =
        build_cad_topology(geometry, &topology).map_err(SolidMeshingError::CadTopology)?;
    let cad_evaluation = build_cad_evaluation_model(&cad_topology, &topology)
        .map_err(SolidMeshingError::CadEvaluation)?;
    let curve_options = curve_discretization_options(options, geometry);
    let cad_curves = discretize_cad_topology_curves_with_sizing_and_provider(
        &topology,
        &cad_topology,
        curve_options,
        Some(sizing),
        &GeometryCadCurveEvaluatorProvider { geometry },
    )
    .map_err(SolidMeshingError::Curve)?;
    let _curve_mesh_contract = build_curve_mesh_contract(
        "solid_curve_mesh",
        &topology,
        &cad_curves.curves,
        CurveValidationOptions::default(),
    )
    .map_err(SolidMeshingError::CurveValidation)?;
    let surface = discretize_cad_topology_surfaces_with_cad_curves_and_sizing(
        &cad_topology,
        &topology,
        &cad_evaluation,
        &cad_curves,
        surface_discretization_options(),
        Some(sizing),
    )
    .map_err(SolidMeshingError::Surface)?;
    let artifact_sizing =
        sizing_with_application_evidence(sizing, &topology, curve_options, &surface);
    let surface_validation = validate_cad_topology_surface_discretization(
        &cad_topology,
        &topology,
        &surface,
        SurfaceValidationOptions::default(),
    )
    .map_err(SolidMeshingError::SurfaceValidation)?;
    let surface_mesh_contract =
        build_surface_mesh_contract("solid_surface_mesh", &surface, &surface_validation);
    let plc = build_protected_boundary_complex(&surface_mesh_contract)
        .map_err(SolidMeshingError::ProtectedBoundaryComplex)?;
    let tetrahedron_mesh = generate_solid_tetrahedron_mesh(&plc)?;
    let mut recovery = recover_tetrahedron_mesh_from_plc(&plc, tetrahedron_mesh)
        .map_err(SolidMeshingError::TetrahedronRecovery)?;
    let initial_backend_quality =
        backend_quality_evidence_from_tetrahedron_mesh(&recovery.tetrahedron_mesh);
    optimize_recovered_tetrahedron_mesh(
        &mut recovery.tetrahedron_mesh,
        &CadFaceBoundarySmoothingProjector {
            cad_evaluation: &cad_evaluation,
        },
        RecoveredTetrahedronMeshOptimizationOptions::default(),
    );

    Ok(analysis_artifact_from_tetrahedron_mesh(
        geometry,
        &artifact_sizing,
        &surface_mesh_contract,
        &recovery.recovery_queue,
        initial_backend_quality,
        recovery.tetrahedron_mesh,
    ))
}

struct GeometryCadCurveEvaluatorProvider<'a> {
    geometry: &'a GeometryAsset,
}

impl CadCurveEvaluatorProvider for GeometryCadCurveEvaluatorProvider<'_> {
    fn evaluate_curve(
        &self,
        request: &CadCurveEvaluationRequest<'_>,
    ) -> Vec<CadCurveEvaluationSample> {
        self.geometry
            .source_geometry
            .cad_evaluators
            .iter()
            .flat_map(|set| set.curves.iter())
            .filter(|curve| {
                request
                    .imported_curve_id
                    .is_some_and(|curve_id| curve.imported_curve_id == curve_id)
                    && request
                        .evaluator_id
                        .is_none_or(|evaluator_id| curve.evaluator_id == evaluator_id)
            })
            .flat_map(|curve| curve.evaluation_samples.iter())
            .filter(|sample| {
                request
                    .parameters
                    .iter()
                    .any(|parameter| (*parameter - sample.parameter).abs() <= 1.0e-12)
            })
            .cloned()
            .collect()
    }
}

struct CadFaceBoundarySmoothingProjector<'a> {
    cad_evaluation: &'a CadEvaluationModel,
}

impl TetrahedronBoundarySmoothingProjector for CadFaceBoundarySmoothingProjector<'_> {
    fn project_to_source_face(
        &self,
        source_face_id: &TopologyEntityId,
        point_m: Point3,
    ) -> Option<TetrahedronBoundarySmoothingProjection> {
        let source_face_id = source_face_id.id.parse::<u32>().ok()?;
        let frame = self
            .cad_evaluation
            .face_frames
            .iter()
            .find(|frame| frame.source_face_id == source_face_id)?;
        let projection = project_to_face(frame, point_m);
        Some(TetrahedronBoundarySmoothingProjection {
            point_m: projection.point_m,
            distance_m: projection.distance_m,
            in_bounds: projection.uv_in_bounds,
        })
    }
}

#[cfg(test)]
mod tests;