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
use cadmpeg_ir::geometry::pcurve::{
    CirclePcurve, EllipsePcurve, HyperbolaPcurve, ParabolaPcurve, PcurveGeometry, PlacedPcurve,
};
use cadmpeg_ir::math::Point2;
use cadmpeg_ir::transform::{Transform, Transform2};

use crate::options::StepSchema;
use crate::writer::Emitter;

fn operator_attributes<'a>(lines: &'a [String], entity: &str, arity: usize) -> Vec<&'a str> {
    let marker = format!(" = {entity}(");
    let attributes = lines
        .iter()
        .find_map(|line| line.split_once(&marker))
        .and_then(|(_, attributes)| attributes.strip_suffix(");"))
        .expect("transformation operator simple instance")
        .split(',')
        .collect::<Vec<_>>();
    assert_eq!(attributes.len(), arity);
    assert_eq!(&attributes[..3], &["''", "''", "''"]);
    attributes
}

fn referenced_record<'a>(lines: &'a [String], reference: &str) -> &'a str {
    let prefix = format!("{reference} = ");
    lines
        .iter()
        .find_map(|line| line.strip_prefix(&prefix))
        .expect("referenced entity")
}

#[test]
fn three_dimensional_operator_has_all_inherited_attributes_in_schema_order() {
    let transform = Transform::affine([
        [0.0, -2.0, 0.0, 10.0],
        [2.0, 0.0, 0.0, 20.0],
        [0.0, 0.0, -2.0, 30.0],
    ])
    .expect("reflected similarity");
    let mut emitter = Emitter::new();
    super::transformation_operator(&mut emitter, transform);
    let lines = emitter.into_lines().expect("finite operator");
    let attributes = operator_attributes(&lines, "CARTESIAN_TRANSFORMATION_OPERATOR_3D", 8);
    assert_eq!(
        referenced_record(&lines, attributes[3]),
        "DIRECTION('',(0.,1.,0.));"
    );
    assert_eq!(
        referenced_record(&lines, attributes[4]),
        "DIRECTION('',(-1.,0.,0.));"
    );
    assert_eq!(
        referenced_record(&lines, attributes[5]),
        "CARTESIAN_POINT('',(10.,20.,30.));"
    );
    assert_eq!(attributes[6], "2.");
    assert_eq!(
        referenced_record(&lines, attributes[7]),
        "DIRECTION('',(0.,0.,-1.));"
    );
}

#[test]
fn reflected_conic_pcurves_keep_schema_complete_replica_operators() {
    let center = Point2::new(1.0, 2.0);
    let x = Point2::new(1.0, 0.0);
    let y = Point2::new(0.0, 1.0);
    let conics = [
        PcurveGeometry::Circle(CirclePcurve::try_new(center, x, y, 3.0).expect("circle")),
        PcurveGeometry::Ellipse(EllipsePcurve::try_new(center, x, y, 3.0, 2.0).expect("ellipse")),
        PcurveGeometry::Parabola(ParabolaPcurve::try_new(center, x, y, 2.0).expect("parabola")),
        PcurveGeometry::Hyperbola(
            HyperbolaPcurve::try_new(center, x, y, 3.0, 2.0).expect("hyperbola"),
        ),
    ];
    let transform =
        Transform2::affine([[0.0, 2.0, 10.0], [2.0, 0.0, 20.0]]).expect("reflected similarity");
    for schema in StepSchema::ALL {
        for conic in &conics {
            let mut emitter = Emitter::for_schema(schema);
            let reflected = PcurveGeometry::Transformed(
                PlacedPcurve::try_new(Box::new(conic.clone()), transform).expect("placed conic"),
            );
            let replica =
                super::pcurve(&mut emitter, &reflected).expect("writable reflected conic");
            let lines = emitter.into_lines().expect("finite reflected conic");
            let entity = match schema {
                StepSchema::Ap203Edition1 => {
                    assert!(!lines
                        .iter()
                        .any(|line| line.contains("CARTESIAN_TRANSFORMATION_OPERATOR_2D(")));
                    "CARTESIAN_TRANSFORMATION_OPERATOR"
                }
                _ => "CARTESIAN_TRANSFORMATION_OPERATOR_2D",
            };
            let attributes = operator_attributes(&lines, entity, 7);
            assert_eq!(
                referenced_record(&lines, attributes[3]),
                "DIRECTION('',(0.,1.));"
            );
            assert_eq!(
                referenced_record(&lines, attributes[4]),
                "DIRECTION('',(1.,0.));"
            );
            assert_eq!(
                referenced_record(&lines, attributes[5]),
                "CARTESIAN_POINT('',(10.,20.));"
            );
            assert_eq!(attributes[6], "2.");
            assert!(
                referenced_record(&lines, &replica.to_string()).starts_with("CURVE_REPLICA('',")
            );
        }
    }
}