hypercurve 0.3.0

Hyperreal-backed planar curves, contours, and regions for CAD topology
Documentation
use hypercurve::{
    BulgeVertex2, Classification, Contour2, CurvePolicy, CurveString2, LineSeg2, Point2, Real,
    Segment2,
};

fn s(value: i32) -> Real {
    value.into()
}

fn p(x: i32, y: i32) -> Point2 {
    Point2::new(s(x), s(y))
}

fn vertex(x: i32, y: i32, bulge: i32) -> BulgeVertex2 {
    BulgeVertex2::new(p(x, y), s(bulge))
}

fn contour(vertices: &[BulgeVertex2]) -> Contour2 {
    Contour2::from_bulge_vertices(vertices).unwrap()
}

fn line_segment(start_x: i32, start_y: i32, end_x: i32, end_y: i32) -> Segment2 {
    Segment2::Line(LineSeg2::try_new(p(start_x, start_y), p(end_x, end_y)).unwrap())
}

fn policy() -> CurvePolicy {
    CurvePolicy::certified()
}

#[test]
fn curve_string_self_contact_detector_ignores_adjacent_corner() {
    let curve =
        CurveString2::try_new(vec![line_segment(0, 0, 2, 0), line_segment(2, 0, 2, 2)]).unwrap();

    assert_eq!(
        curve.has_self_contacts(&policy()).unwrap(),
        Classification::Decided(false)
    );
}

#[test]
fn curve_string_self_contact_detector_finds_nonadjacent_crossing() {
    let curve = CurveString2::try_new(vec![
        line_segment(0, 0, 4, 4),
        line_segment(4, 4, 0, 4),
        line_segment(0, 4, 4, 0),
    ])
    .unwrap();

    assert_eq!(
        curve.has_self_contacts(&policy()).unwrap(),
        Classification::Decided(true)
    );
}

#[test]
fn curve_string_self_contact_detector_does_not_ignore_closing_endpoint() {
    let curve = CurveString2::try_new(vec![
        line_segment(0, 0, 2, 0),
        line_segment(2, 0, 2, 2),
        line_segment(2, 2, 0, 0),
    ])
    .unwrap();

    assert_eq!(
        curve.has_self_contacts(&policy()).unwrap(),
        Classification::Decided(true)
    );
}

#[test]
fn prepared_curve_string_self_contact_detector_matches_plain_detector() {
    let curve = CurveString2::try_new(vec![
        line_segment(0, 0, 2, 0),
        line_segment(2, 0, 2, 2),
        line_segment(2, 2, 0, 0),
    ])
    .unwrap();
    let policy = policy();
    let prepared = curve.prepare_topology_queries(&policy);

    assert_eq!(prepared.curve_string(), &curve);
    assert!(prepared.curve_box().is_some());
    assert_eq!(prepared.segment_boxes().len(), curve.segments().len());
    assert_eq!(
        prepared.has_self_contacts(&policy).unwrap(),
        curve.has_self_contacts(&policy).unwrap()
    );
    assert_eq!(
        prepared.has_self_contacts(&policy).unwrap(),
        Classification::Decided(true)
    );
}

#[test]
fn self_contact_detector_ignores_adjacent_rectangle_vertices() {
    let rectangle = contour(&[
        vertex(0, 0, 0),
        vertex(4, 0, 0),
        vertex(4, 4, 0),
        vertex(0, 4, 0),
    ]);

    assert_eq!(
        rectangle.has_self_contacts(&policy()).unwrap(),
        Classification::Decided(false)
    );
}

#[test]
fn self_contact_detector_finds_nonadjacent_crossing() {
    let bowtie = contour(&[
        vertex(0, 0, 0),
        vertex(4, 4, 0),
        vertex(0, 4, 0),
        vertex(4, 0, 0),
    ]);

    assert_eq!(
        bowtie.has_self_contacts(&policy()).unwrap(),
        Classification::Decided(true)
    );
}

#[test]
fn self_contact_detector_finds_nonadjacent_line_arc_crossing() {
    let contour = contour(&[
        vertex(0, 0, 1),
        vertex(2, 0, 0),
        vertex(3, 2, 0),
        vertex(1, 2, 0),
        vertex(1, -2, 0),
        vertex(3, -3, 0),
        vertex(-1, -3, 0),
    ]);

    assert_eq!(
        contour.has_self_contacts(&policy()).unwrap(),
        Classification::Decided(true)
    );
}

#[test]
fn self_contact_detector_finds_adjacent_line_arc_crossing_beyond_shared_endpoint() {
    let contour = contour(&[
        vertex(0, 0, 1),
        vertex(2, 0, 0),
        vertex(0, -2, 0),
        vertex(-1, 0, 0),
    ]);

    assert_eq!(
        contour.has_self_contacts(&policy()).unwrap(),
        Classification::Decided(true)
    );
}

#[test]
fn prepared_contour_self_contact_detector_matches_plain_detector() {
    let pinched = contour(&[
        vertex(0, 0, 0),
        vertex(2, 0, 0),
        vertex(1, 1, 0),
        vertex(2, 2, 0),
        vertex(0, 2, 0),
        vertex(1, 1, 0),
    ]);
    let policy = policy();
    let prepared = pinched.prepare_topology_queries(&policy);

    assert_eq!(prepared.contour(), &pinched);
    assert!(prepared.contour_box().is_some());
    assert_eq!(prepared.segment_boxes().len(), pinched.segments().len());
    assert_eq!(
        prepared.has_self_contacts(&policy).unwrap(),
        pinched.has_self_contacts(&policy).unwrap()
    );
    assert_eq!(
        prepared.has_self_contacts(&policy).unwrap(),
        Classification::Decided(true)
    );
}

#[test]
fn self_contact_detector_finds_repeated_nonadjacent_endpoint() {
    let pinched = contour(&[
        vertex(0, 0, 0),
        vertex(2, 0, 0),
        vertex(1, 1, 0),
        vertex(2, 2, 0),
        vertex(0, 2, 0),
        vertex(1, 1, 0),
    ]);

    assert_eq!(
        pinched.has_self_contacts(&policy()).unwrap(),
        Classification::Decided(true)
    );
}