runmat-meshing-surface 0.6.0

Surface meshing stage for RunMat meshing
Documentation
use std::collections::{BTreeMap, BTreeSet};

use runmat_meshing_cad::SourceTopologyFace;
use runmat_meshing_curve::CadCurveEdgeProvenance;

use super::{
    boundary::FaceCurveSegment, SurfaceCadCurveBoundaryEdgeProvenance,
    SurfaceCadCurveBoundaryProvenanceReport, SurfaceDiscretizationError, SurfaceLoopCoverageReport,
};

pub(super) struct SurfaceLoopCoverageAccumulator {
    source_face_count: usize,
    recovered_face_count: usize,
    boundary_loop_count: usize,
    hole_loop_count: usize,
    boundary_segment_count: usize,
    max_loops_per_face: usize,
    boundary_node_ids: BTreeSet<u32>,
    recovered_source_edge_ids: BTreeSet<u32>,
}

impl SurfaceLoopCoverageAccumulator {
    pub(super) fn new(source_face_count: usize) -> Self {
        Self {
            source_face_count,
            recovered_face_count: 0,
            boundary_loop_count: 0,
            hole_loop_count: 0,
            boundary_segment_count: 0,
            max_loops_per_face: 0,
            boundary_node_ids: BTreeSet::new(),
            recovered_source_edge_ids: BTreeSet::new(),
        }
    }

    pub(super) fn record_face(
        &mut self,
        face: &SourceTopologyFace,
        segment_loops: &[Vec<FaceCurveSegment>],
    ) {
        self.record_face_edges(&face.edge_ids, segment_loops);
    }

    pub(super) fn record_face_edges(
        &mut self,
        source_edge_ids: &[u32],
        segment_loops: &[Vec<FaceCurveSegment>],
    ) {
        self.recovered_face_count += 1;
        self.boundary_loop_count += segment_loops.len();
        self.hole_loop_count += segment_loops.len().saturating_sub(1);
        self.max_loops_per_face = self.max_loops_per_face.max(segment_loops.len());
        for segment in segment_loops.iter().flatten() {
            self.boundary_segment_count += 1;
            self.boundary_node_ids.extend(segment.node_ids);
            if source_edge_ids.contains(&segment.source_edge_id) {
                self.recovered_source_edge_ids
                    .insert(segment.source_edge_id);
            }
        }
    }

    pub(super) fn finish(self) -> SurfaceLoopCoverageReport {
        SurfaceLoopCoverageReport {
            source_face_count: self.source_face_count,
            recovered_face_count: self.recovered_face_count,
            boundary_loop_count: self.boundary_loop_count,
            hole_loop_count: self.hole_loop_count,
            boundary_node_count: self.boundary_node_ids.len(),
            recovered_source_edge_count: self.recovered_source_edge_ids.len(),
            boundary_segment_count: self.boundary_segment_count,
            max_loops_per_face: self.max_loops_per_face,
        }
    }
}

pub(super) struct SurfaceCadCurveBoundaryProvenanceAccumulator {
    edges: BTreeMap<u32, SurfaceCadCurveBoundaryEdgeProvenance>,
}

impl SurfaceCadCurveBoundaryProvenanceAccumulator {
    pub(super) fn new() -> Self {
        Self {
            edges: BTreeMap::new(),
        }
    }

    pub(super) fn record_segments(
        &mut self,
        segment_loops: &[Vec<FaceCurveSegment>],
        provenance_by_source_edge: &BTreeMap<u32, &CadCurveEdgeProvenance>,
    ) -> Result<(), SurfaceDiscretizationError> {
        for segment in segment_loops.iter().flatten() {
            let source = provenance_by_source_edge
                .get(&segment.source_edge_id)
                .ok_or(SurfaceDiscretizationError::MissingCadCurveProvenance {
                    source_edge_id: segment.source_edge_id,
                })?;
            self.edges
                .entry(segment.source_edge_id)
                .and_modify(|entry| entry.boundary_segment_count += 1)
                .or_insert_with(|| SurfaceCadCurveBoundaryEdgeProvenance {
                    source_edge_id: source.source_edge_id,
                    cad_edge_id: source.cad_edge_id.clone(),
                    imported_curve_id: source.imported_curve_id,
                    evaluator_id: source.evaluator_id.clone(),
                    evaluator_supports_point_evaluation: source.evaluator_supports_point_evaluation,
                    evaluator_supports_projection: source.evaluator_supports_projection,
                    evaluator_supports_tangent: source.evaluator_supports_tangent,
                    evaluator_supports_curvature: source.evaluator_supports_curvature,
                    evaluator_sample_count: source.evaluator_sample_count,
                    live_query_backed: source.live_query_backed,
                    live_query_sample_count: source.live_query_sample_count,
                    rejected_evaluator_sample_count: source.rejected_evaluator_sample_count,
                    curvature_sample_count: source.curvature_sample_count,
                    curvature_limited_target_size_m: source.curvature_limited_target_size_m,
                    boundary_segment_count: 1,
                });
        }
        Ok(())
    }

    pub(super) fn finish(self) -> SurfaceCadCurveBoundaryProvenanceReport {
        let edges = self.edges.into_values().collect::<Vec<_>>();
        SurfaceCadCurveBoundaryProvenanceReport {
            recovered_source_edge_count: edges.len(),
            boundary_segment_count: edges.iter().map(|edge| edge.boundary_segment_count).sum(),
            imported_curve_edge_count: edges
                .iter()
                .filter(|edge| edge.imported_curve_id.is_some())
                .count(),
            evaluator_curve_edge_count: edges
                .iter()
                .filter(|edge| edge.evaluator_id.is_some())
                .count(),
            evaluator_sample_count: edges.iter().map(|edge| edge.evaluator_sample_count).sum(),
            live_query_edge_count: edges.iter().filter(|edge| edge.live_query_backed).count(),
            live_query_sample_count: edges.iter().map(|edge| edge.live_query_sample_count).sum(),
            rejected_evaluator_sample_count: edges
                .iter()
                .map(|edge| edge.rejected_evaluator_sample_count)
                .sum(),
            curvature_sized_edge_count: edges
                .iter()
                .filter(|edge| edge.curvature_limited_target_size_m.is_some())
                .count(),
            curvature_sample_count: edges.iter().map(|edge| edge.curvature_sample_count).sum(),
            edges,
        }
    }
}