cadmpeg-codec-step 0.6.8

Read and write ISO 10303-21 STEP Part 21 files for AP203, AP214, and AP242.
Documentation
// SPDX-License-Identifier: Apache-2.0
//! Numeric indexes for STEP geometry carriers.

use std::collections::HashMap;

use super::step_instance_id;
use cadmpeg_ir::document::CadIr;
use cadmpeg_ir::math::Point3;

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(super) struct CurveIndex(pub(super) usize);

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(super) struct SurfaceIndex(pub(super) usize);

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(super) struct PointIndex(pub(super) usize);

pub(super) struct PointCarrier {
    pub(super) index: PointIndex,
    position: Point3,
}

/// Decode-local lookup tables for STEP carrier instance ids.
pub(super) struct CarrierIndex {
    pub(super) curves: HashMap<u64, CurveIndex>,
    pub(super) points: HashMap<u64, PointCarrier>,
    pub(super) surfaces: HashMap<u64, SurfaceIndex>,
}

impl CarrierIndex {
    /// Index newly admitted surfaces without copying existing carrier maps.
    pub(super) fn refresh_surfaces(&mut self, ir: &CadIr) {
        for (index, surface) in ir.model.surfaces.iter().enumerate() {
            if let Some(id) = step_instance_id(surface.id.as_str()) {
                self.surfaces.insert(id, SurfaceIndex(index));
            }
        }
    }

    pub(super) fn from_ir(ir: &CadIr) -> CarrierIndex {
        let mut curves = HashMap::new();
        for (index, curve) in ir.model.curves.iter().enumerate() {
            if let Some(id) = step_instance_id(curve.id.as_str()) {
                curves.insert(id, CurveIndex(index));
            }
        }
        let mut points = HashMap::new();
        for (index, point) in ir.model.points.iter().enumerate() {
            if let Some(id) = step_instance_id(point.id.as_str()) {
                points.insert(
                    id,
                    PointCarrier {
                        index: PointIndex(index),
                        position: point.position().get(),
                    },
                );
            }
        }
        let mut surfaces = HashMap::new();
        for (index, surface) in ir.model.surfaces.iter().enumerate() {
            if let Some(id) = step_instance_id(surface.id.as_str()) {
                surfaces.insert(id, SurfaceIndex(index));
            }
        }
        Self {
            curves,
            points,
            surfaces,
        }
    }

    pub(super) fn get(&self, id: u64) -> Option<&Point3> {
        self.points.get(&id).map(|point| &point.position)
    }

    pub(super) fn contains_key(&self, id: u64) -> bool {
        self.points.contains_key(&id)
    }
}

#[cfg(test)]
mod tests {
    use cadmpeg_ir::geometry::{
        analytic::{LineCurve, PlaneSurface},
        Curve, CurveGeometry, SolvedCurveGeometry, SolvedSurfaceGeometry, Surface, SurfaceGeometry,
    };
    use cadmpeg_ir::ids::{CurveId, PointId, SurfaceId};
    use cadmpeg_ir::math::{Point3, Vector3};
    use cadmpeg_ir::topology::Point;
    use cadmpeg_ir::CadIr;

    use super::CarrierIndex;

    fn carriers() -> CadIr {
        let mut ir = CadIr::empty();
        ir.model.curves.push(Curve {
            parameter_range: None,
            id: CurveId::from(crate::ids::data(crate::ids::kind!("curve"), 1)),
            geometry: CurveGeometry::Solved(SolvedCurveGeometry::Line(
                LineCurve::try_new(Point3::new(0.0, 0.0, 0.0), Vector3::new(1.0, 0.0, 0.0))
                    .expect("finite line"),
            )),
            source_object: None,
        });
        ir.model.points.push(Point::new(
            PointId::from(crate::ids::data(crate::ids::kind!("point"), 2)),
            cadmpeg_ir::features::FinitePoint3::ZERO,
            None,
        ));
        ir.model.surfaces.push(Surface {
            id: SurfaceId::from(crate::ids::data(crate::ids::kind!("surface"), 3)),
            geometry: SurfaceGeometry::Solved(SolvedSurfaceGeometry::Plane(
                PlaneSurface::try_new(
                    Point3::new(0.0, 0.0, 0.0),
                    Vector3::new(0.0, 0.0, 1.0),
                    Vector3::new(1.0, 0.0, 0.0),
                )
                .expect("finite plane"),
            )),
            source_object: None,
        });
        ir
    }

    #[test]
    fn surface_extension_reuses_existing_carrier_entries() {
        let mut ir = carriers();
        let mut index = CarrierIndex::from_ir(&ir);
        let mut surface = ir.model.surfaces[0].clone();
        surface.id = SurfaceId::from(crate::ids::data(crate::ids::kind!("surface"), 4));
        ir.model.surfaces.push(surface);
        index.refresh_surfaces(&ir);
        index.refresh_surfaces(&ir);
        assert_eq!(index.curves[&1].0, 0);
        assert_eq!(index.points[&2].index.0, 0);
        assert_eq!(index.surfaces[&3].0, 0);
        assert_eq!(index.surfaces[&4].0, 1);
        assert_eq!(index.get(2), Some(&Point3::new(0.0, 0.0, 0.0)));
    }
}