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
//! Connected shell component admission.

use std::collections::BTreeMap;

use cadmpeg_core::decode::{DecodeArena, DecodeContext, DecodePolicy};
use cadmpeg_ir::ids::{FaceId, LoopId, VertexId};
use cadmpeg_ir::topology::{Loop, LoopBoundary};

fn faces() -> [FaceId; 2] {
    [
        FaceId::try_from("step:data:face#1").expect("test face id"),
        FaceId::try_from("step:data:face#2").expect("test face id"),
    ]
}

#[test]
fn unconnected_faces_form_separate_components() {
    let faces = faces();
    let arena = DecodeArena::new();
    let (service_ctx, _) = DecodeContext::from_root_bytes(&[], &arena, &DecodePolicy::service())
        .expect("build the decode context");
    let components =
        super::super::connected_face_components(&faces, &[], &[], &BTreeMap::new(), &service_ctx)
            .expect("compute the connected face components");
    assert_eq!(components, vec![vec![0], vec![1]]);
}

#[test]
fn shared_vertex_joins_faces_into_one_component() {
    let faces = faces();
    let vertex = VertexId::try_from("step:data:vertex#1").expect("test vertex id");
    let loops = [
        Loop {
            id: LoopId::try_from("step:data:loop#1").expect("test loop id"),
            face: faces[0].clone(),
            boundary: LoopBoundary::Vertex {
                vertex: vertex.clone(),
                pcurves: Vec::new(),
            },
        },
        Loop {
            id: LoopId::try_from("step:data:loop#2").expect("test loop id"),
            face: faces[1].clone(),
            boundary: LoopBoundary::Vertex {
                vertex,
                pcurves: Vec::new(),
            },
        },
    ];
    let arena = DecodeArena::new();
    let (ctx, _) = DecodeContext::from_root_bytes(&[], &arena, &DecodePolicy::service())
        .expect("build the decode context");
    assert_eq!(
        super::super::connected_face_components(&faces, &loops, &[], &BTreeMap::new(), &ctx)
            .expect("shared vertex joins faces"),
        vec![vec![0, 1]]
    );
}

#[test]
fn shared_vertex_components_use_spanning_links_and_preserve_partitions() {
    let count = 100;
    let faces = (0..count)
        .map(|id| FaceId::try_from(format!("step:data:face#{id}")).expect("face id"))
        .collect::<Vec<_>>();
    for groups in [1, 10, 100] {
        let loops = faces
            .iter()
            .enumerate()
            .map(|(index, face)| Loop {
                id: LoopId::try_from(format!("step:data:loop#{index}")).expect("loop id"),
                face: face.clone(),
                boundary: LoopBoundary::Vertex {
                    vertex: VertexId::try_from(format!("step:data:vertex#{}", index % groups))
                        .expect("vertex id"),
                    pcurves: Vec::new(),
                },
            })
            .collect::<Vec<_>>();
        let arena = DecodeArena::new();
        let policy = DecodePolicy::service();
        // Face scratch, indexes, group memberships, spanning links, and DFS
        // outputs total eight entries per face for these disjoint groups.
        let (ctx, _) = DecodeContext::from_root_bytes(b"", &arena, &policy).expect("context");
        let actual =
            super::super::connected_face_components(&faces, &loops, &[], &BTreeMap::new(), &ctx)
                .expect("component storage grows with membership, not group pairs");
        let expected = (0..groups)
            .map(|group| (group..count).step_by(groups).collect::<Vec<_>>())
            .collect::<Vec<_>>();
        assert_eq!(actual, expected);
    }
}