conspire 0.7.2

The Rust interface to conspire.
Documentation
use super::super::test::{box_surface, dual, hexahedron, sphere};
use super::super::{Class, Sign, Vertex};
use crate::{
    geometry::mesh::Connectivity,
    math::{CrossProduct, Tensor},
};
use std::collections::HashSet;

#[test]
fn tables_single_hexahedron() {
    let tessellation = sphere(3);
    let mesh = hexahedron([0.9, -0.1, -0.1], [1.1, 0.1, 0.1]);
    let classes = tessellation.classify(&mesh);
    let tables = tessellation
        .tables(&mesh, &classes, &HashSet::new())
        .unwrap();
    assert_eq!(tables.signs().len(), 8);
    assert_eq!(
        tables
            .signs()
            .values()
            .filter(|&&sign| sign == Sign::Inside)
            .count(),
        4
    );
    assert_eq!(tables.crossings().len(), 4);
    tables.crossings().values().for_each(|points| {
        assert_eq!(points.len(), 1);
        points
            .iter()
            .for_each(|point| assert!((point.norm() - 1.0).abs() < 0.01))
    });
    assert_eq!(tables.segments().len(), 4);
    tables
        .segments()
        .values()
        .for_each(|segments| assert_eq!(segments.len(), 1))
}

#[test]
fn tables_generic_matches_hex_tables() {
    let tessellation = sphere(3);
    let mesh = hexahedron([0.9, -0.1, -0.1], [1.1, 0.1, 0.1]);
    let classes = tessellation.classify(&mesh);
    let tables = tessellation
        .tables(&mesh, &classes, &HashSet::new())
        .unwrap();
    let generic = tessellation
        .tables_generic(&mesh, &classes, &HashSet::new())
        .unwrap();
    assert_eq!(generic.signs(), tables.signs());
    assert_eq!(generic.crossings(), tables.crossings());
    assert_eq!(generic.faces().len(), tables.faces().len());
    tables.faces().iter().for_each(|(key, corners)| {
        assert_eq!(generic.faces()[&key.to_vec()], corners.to_vec());
    });
    assert_eq!(generic.segments().len(), tables.segments().len());
    tables.segments().iter().for_each(|(key, pairs)| {
        assert_eq!(&generic.segments()[&key.to_vec()], pairs);
    });
}

#[test]
fn tables_sphere_dual() {
    let tessellation = sphere(3);
    let mesh = dual(&tessellation, 8.0);
    let classes = tessellation.classify(&mesh);
    let tables = tessellation
        .tables(&mesh, &classes, &HashSet::new())
        .unwrap();
    assert!(!tables.crossings().is_empty());
    tables.crossings().values().flatten().for_each(|point| {
        let norm = point.norm();
        assert!((0.985..=1.0 + 1e-9).contains(&norm), "{norm}")
    });
    let coordinates = mesh.coordinates();
    tables.signs().iter().for_each(|(&node, &sign)| {
        let norm = coordinates[node].norm();
        if (norm - 1.0).abs() > 0.02 {
            assert_eq!(
                sign,
                if norm < 1.0 {
                    Sign::Inside
                } else {
                    Sign::Outside
                }
            )
        }
    });
    tables.segments().values().flatten().for_each(|segment| {
        segment.iter().for_each(|vertex| match vertex {
            Vertex::Node(_) => (),
            Vertex::Crossing(edge, ordinal) => {
                assert!(tables.crossings()[edge].len() > *ordinal)
            }
        })
    })
}

#[test]
fn tables_double_crossing_edge() {
    let plate = box_surface([-2.0, -2.0, -0.05], [2.0, 2.0, 0.05]);
    let mesh = hexahedron([-1.0, -1.0, -1.0], [1.0, 1.0, 1.0]);
    let classes = plate.classify(&mesh);
    assert_eq!(classes, vec![Class::Cut]);
    let tables = plate.tables(&mesh, &classes, &HashSet::new()).unwrap();
    assert_eq!(
        tables
            .signs()
            .values()
            .filter(|&&sign| sign == Sign::Outside)
            .count(),
        8
    );
    [[0, 4], [1, 5], [2, 6], [3, 7]]
        .into_iter()
        .for_each(|[bottom, top]| {
            let points = &tables.crossings()[&[bottom, top]];
            assert_eq!(points.len(), 2);
            assert!(points[0][2] < points[1][2], "{points:?}");
            points
                .iter()
                .for_each(|point| assert!((point[2].abs() - 0.05).abs() < 1e-9, "{point:?}"));
        });
    let result = plate.assemble(&mesh, &classes, &tables).unwrap();
    assert_eq!(result.number_of_element_blocks(), 1);
    match &result.connectivities()[0] {
        Connectivity::Hexahedral(hexes) => {
            assert_eq!(hexes.iter().count(), 1);
            let element: Vec<usize> = hexes.iter().flatten().copied().collect();
            let coordinates = result.coordinates();
            coordinates
                .iter()
                .for_each(|point| assert!((point[2].abs() - 0.05).abs() < 1e-9, "{point:?}"));
            let volume = &(&coordinates[element[1]] - &coordinates[element[0]])
                .cross(&(&coordinates[element[3]] - &coordinates[element[0]]))
                * &(&coordinates[element[4]] - &coordinates[element[0]]);
            assert!((volume - 0.4).abs() < 1e-9, "{volume}")
        }
        _ => panic!(),
    }
}