cadmpeg-ir 0.6.5

CAD data model, validation, diffing, and codec API for cadmpeg.
// SPDX-License-Identifier: Apache-2.0
#![allow(clippy::unwrap_used)]

use super::{directed_subd_sum, unit_cube};
use crate::document::CadIr;
use crate::validate::validate_neutral;

#[test]
fn directed_subd_sum_fixture_round_trips_and_validates() {
    let ir = directed_subd_sum().unwrap();
    let report =
        crate::validate::validate_neutral(&ir, Vec::new()).expect("validate the neutral model");
    assert!(report.is_ok(), "{:?}", report.findings);
    let json = ir.to_canonical_json().expect("serialize fixture");
    assert_eq!(CadIr::from_json(&json).expect("parse fixture"), ir);
}

#[test]
fn directed_subd_sum_fixture_carries_the_literal_axes_and_frame() {
    use crate::geometry::analytic::{LineCurve, PlaneSurface};
    use crate::geometry::{
        CurveGeometry, SolvedCurveGeometry, SolvedSurfaceGeometry, SurfaceGeometry,
    };
    use crate::ids::{CurveId, SurfaceId};
    use crate::math::{Point3, Vector3};

    let ir = directed_subd_sum().unwrap();
    let origin = Point3::new(0.0, 0.0, 0.0);
    for (key, direction) in [
        (crate::identity_key!("u"), Vector3::new(1.0, 0.0, 0.0)),
        (crate::identity_key!("v"), Vector3::new(0.0, 1.0, 0.0)),
    ] {
        let id = v2_id!(CurveId, "curve", key);
        let curve = ir.model.curves.iter().find(|curve| curve.id == id).unwrap();
        let CurveGeometry::Solved(SolvedCurveGeometry::Line(line)) = &curve.geometry else {
            panic!("the directed SubD example curve is a line");
        };
        assert_eq!(
            serde_json::to_string(line).unwrap(),
            serde_json::to_string(&LineCurve::try_new(origin, direction).unwrap()).unwrap()
        );
    }
    let id = v2_id!(SurfaceId, "surface", crate::identity_key!("sum-cache"));
    let surface = ir
        .model
        .surfaces
        .iter()
        .find(|surface| surface.id == id)
        .unwrap();
    let SurfaceGeometry::Procedural {
        cache: Some(SolvedSurfaceGeometry::Plane(plane)),
        ..
    } = &surface.geometry
    else {
        panic!("the directed SubD example cache is a plane");
    };
    assert_eq!(
        serde_json::to_string(plane).unwrap(),
        serde_json::to_string(
            &PlaneSurface::try_new(
                origin,
                Vector3::new(0.0, 0.0, 1.0),
                Vector3::new(1.0, 0.0, 0.0)
            )
            .unwrap()
        )
        .unwrap()
    );
}

#[cfg(feature = "schema")]
#[test]
fn directed_subd_sum_fixture_matches_schema_shape() {
    let schema = serde_json::to_value(crate::cadir_json_schema()).expect("serialize schema");
    let schema_text = schema.to_string();
    assert!(schema_text.contains("procedural_surfaces"));
    assert!(schema_text.contains("sharpness"));
    assert!(schema_text.contains("\"sum\""));
}

#[test]
fn unit_cube_has_expected_census() {
    let ir = unit_cube().expect("valid unit cube fixture");
    assert_eq!(ir.model.bodies.len(), 1);
    assert_eq!(ir.model.regions.len(), 1);
    assert_eq!(ir.model.shells.len(), 1);
    assert_eq!(ir.model.faces.len(), 6);
    assert_eq!(ir.model.loops.len(), 6);
    assert_eq!(ir.model.coedges.len(), 24);
    assert_eq!(ir.model.edges.len(), 12);
    assert_eq!(ir.model.vertices.len(), 8);
    assert_eq!(ir.model.points.len(), 8);
    assert_eq!(ir.model.surfaces.len(), 6);
    assert_eq!(ir.model.curves.len(), 12);
}

#[test]
fn unit_cube_validates_clean() {
    let ir = unit_cube().expect("valid unit cube fixture");
    let report = validate_neutral(&ir, Vec::new()).expect("validate the neutral model");
    assert!(
        report.is_ok(),
        "cube should have no error findings, got: {:?}",
        report.findings
    );
    assert_eq!(report.error_count(), 0);
    assert_eq!(report.warning_count(), 0);
    assert_eq!(report.entity_counts.get("coedges"), Some(&24));
}

#[test]
fn every_cube_edge_has_two_opposite_sense_coedges() {
    let ir = unit_cube().expect("valid unit cube fixture");
    for edge in &ir.model.edges {
        let coedges: Vec<_> = ir
            .model
            .coedges
            .iter()
            .filter(|c| c.edge == edge.id)
            .collect();
        assert_eq!(coedges.len(), 2, "edge {} should have 2 coedges", edge.id);
        assert_ne!(
            coedges[0].sense, coedges[1].sense,
            "edge {} coedges should have opposite sense",
            edge.id
        );
        // Partners point at each other.
        assert_eq!(coedges[0].radial_next, coedges[1].id);
        assert_eq!(coedges[1].radial_next, coedges[0].id);
    }
}