runmat-meshing-surface 0.6.0

Surface meshing stage for RunMat meshing
Documentation
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct SurfaceValidationOptions {
    pub max_projection_error_m: f64,
    pub min_orientation_alignment: f64,
    pub require_source_edge_conformity: bool,
}

impl Default for SurfaceValidationOptions {
    fn default() -> Self {
        Self {
            max_projection_error_m: 1.0e-8,
            min_orientation_alignment: 1.0 - 1.0e-8,
            require_source_edge_conformity: true,
        }
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SurfaceValidationReport {
    pub source_face_count: usize,
    pub surface_element_count: usize,
    pub source_edge_loop_count: usize,
    pub closed_source_edge_loop_count: usize,
    pub conforming_source_edge_count: usize,
    pub missing_source_edge_count: usize,
    pub max_projection_error_m: f64,
    pub min_orientation_alignment: f64,
    pub face_coverage_ratio: f64,
}

#[derive(Debug, Clone, PartialEq)]
pub enum SurfaceValidationError {
    InvalidOptions,
    EmptySurface,
    MissingSurfaceNode {
        element_id: u32,
        node_id: u32,
    },
    MissingSourceFace {
        source_face_id: u32,
    },
    MissingSourceEdge {
        source_edge_id: u32,
    },
    MissingCadFaceId {
        element_id: u32,
    },
    MissingCadFace {
        cad_face_id: String,
    },
    InvalidCadLoopEdgeId {
        cad_face_id: String,
        loop_edge_id: String,
    },
    SourceEdgeOutsideCadFace {
        cad_face_id: String,
        source_edge_id: u32,
    },
    EdgeConformityFailed {
        source_edge_id: u32,
        source_edge_node_ids: [u32; 2],
        recovered_segment_count: usize,
    },
    OpenSourceLoop {
        source_edge_id: u32,
        endpoint_id: u32,
        incidence_count: usize,
    },
    DegenerateElement {
        element_id: u32,
    },
    InvalidElementNormal {
        element_id: u32,
    },
    InvalidElementArea {
        element_id: u32,
    },
    InvalidParametricEvidence {
        element_id: u32,
    },
    InvalidProjectionEvidence {
        element_id: u32,
    },
    ProjectionError {
        element_id: u32,
        error_m: f64,
        max_error_m: f64,
    },
    OrientationMismatch {
        element_id: u32,
        source_face_id: u32,
        alignment: f64,
        min_alignment: f64,
    },
    UncoveredSourceFace {
        source_face_id: u32,
    },
    UncoveredCadFace {
        cad_face_id: String,
    },
}

impl std::fmt::Display for SurfaceValidationError {
    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::InvalidOptions => write!(
                formatter,
                "surface validation options must use finite projection and orientation thresholds"
            ),
            Self::EmptySurface => write!(formatter, "surface validation input has no elements"),
            Self::MissingSurfaceNode {
                element_id,
                node_id,
            } => write!(
                formatter,
                "surface element {element_id} references missing node {node_id}"
            ),
            Self::MissingSourceFace { source_face_id } => {
                write!(formatter, "source face {source_face_id} is missing")
            }
            Self::MissingSourceEdge { source_edge_id } => {
                write!(formatter, "source edge {source_edge_id} is missing")
            }
            Self::MissingCadFaceId { element_id } => write!(
                formatter,
                "surface element {element_id} does not carry CAD face provenance"
            ),
            Self::MissingCadFace { cad_face_id } => {
                write!(formatter, "CAD face {cad_face_id} is missing")
            }
            Self::InvalidCadLoopEdgeId {
                cad_face_id,
                loop_edge_id,
            } => write!(
                formatter,
                "CAD face {cad_face_id} has invalid loop edge id {loop_edge_id}"
            ),
            Self::SourceEdgeOutsideCadFace {
                cad_face_id,
                source_edge_id,
            } => write!(
                formatter,
                "source edge {source_edge_id} is not a loop edge for CAD face {cad_face_id}"
            ),
            Self::EdgeConformityFailed {
                source_edge_id,
                source_edge_node_ids,
                recovered_segment_count,
            } => write!(
                formatter,
                "source edge {source_edge_id} with endpoints {:?} is not represented by a matching surface element edge; recovered surface segment count is {recovered_segment_count}",
                source_edge_node_ids
            ),
            Self::OpenSourceLoop {
                source_edge_id,
                endpoint_id,
                incidence_count,
            } => write!(
                formatter,
                "source edge {source_edge_id} endpoint {endpoint_id} has loop incidence {incidence_count}, expected 2"
            ),
            Self::DegenerateElement { element_id } => {
                write!(formatter, "surface element {element_id} is degenerate")
            }
            Self::InvalidElementNormal { element_id } => write!(
                formatter,
                "surface element {element_id} has invalid unit-normal evidence"
            ),
            Self::InvalidElementArea { element_id } => write!(
                formatter,
                "surface element {element_id} has invalid area evidence"
            ),
            Self::InvalidParametricEvidence { element_id } => write!(
                formatter,
                "surface element {element_id} has invalid parametric coordinate evidence"
            ),
            Self::InvalidProjectionEvidence { element_id } => write!(
                formatter,
                "surface element {element_id} has invalid projection evidence"
            ),
            Self::ProjectionError {
                element_id,
                error_m,
                max_error_m,
            } => write!(
                formatter,
                "surface element {element_id} projection error {error_m:.6e} m exceeds {max_error_m:.6e} m"
            ),
            Self::OrientationMismatch {
                element_id,
                source_face_id,
                alignment,
                min_alignment,
            } => write!(
                formatter,
                "surface element {element_id} on source face {source_face_id} orientation alignment {alignment:.6e} is below {min_alignment:.6e}"
            ),
            Self::UncoveredSourceFace { source_face_id } => {
                write!(formatter, "source face {source_face_id} is not covered")
            }
            Self::UncoveredCadFace { cad_face_id } => {
                write!(formatter, "CAD face {cad_face_id} is not covered")
            }
        }
    }
}

impl std::error::Error for SurfaceValidationError {}