offroad 0.0.1-alpha

2D offsetting for arc polylines.
Documentation
#![allow(dead_code)]

use crate::point::point;
use crate::utils::diff_of_prod;
use crate::{circle::Circle, point::Point};

#[derive(Debug, PartialEq)]
pub enum CircleConfig {
    NoIntersection(),
    NoncocircularOnePoint(Point),
    NoncocircularTwoPoints(Point, Point),
    SameCircles(),
}

pub fn int_circle_circle(circle0: Circle, circle1: Circle) -> CircleConfig {
    const ZERO: f64 = 0f64;
    debug_assert!(circle0.r.is_finite());
    debug_assert!(circle1.r.is_finite());

    let u = circle1.c - circle0.c;
    let usqr_len = u.dot(u);
    let r0 = circle0.r;
    let r1 = circle1.r;
    let r0_m_r1 = r0 - r1;

    if usqr_len == ZERO && r0_m_r1 == ZERO {
        return CircleConfig::SameCircles();
    }

    let r0_m_r1_sqr = r0_m_r1 * r0_m_r1;
    if usqr_len < r0_m_r1_sqr {
        return CircleConfig::NoIntersection();
    }

    let r0_p_r1 = r0 + r1;
    let r0_p_r1_sqr = r0_p_r1 * r0_p_r1;
    if usqr_len > r0_p_r1_sqr {
        return CircleConfig::NoIntersection();
    }

    if usqr_len < r0_p_r1_sqr {
        if r0_m_r1_sqr < usqr_len {
            let s = 0.5 * (diff_of_prod(r0, r0, r1, r1) / usqr_len + 1.0);

            let mut discr = diff_of_prod(r0 / usqr_len, r0, s, s);

            if discr < ZERO {
                discr = ZERO;
            }
            let t = discr.sqrt();
            let v = point(u.y, -u.x);
            let tmp = circle0.c + u * s;
            let p0 = tmp - v * t;
            let p1 = tmp + v * t;
            if t > 0f64 {
                return CircleConfig::NoncocircularTwoPoints(p0, p1);
            } else {
                return CircleConfig::NoncocircularOnePoint(p0);
            }
        } else {
            let p0 = circle0.c + u * (r0 / r0_m_r1);
            return CircleConfig::NoncocircularOnePoint(p0);
        }
    } else {
        let p0 = circle0.c + u * (r0 / r0_p_r1);
        return CircleConfig::NoncocircularOnePoint(p0);
    }
}

#[cfg(test)]
mod tests_circle {
    use super::*;
    use crate::circle::circle;

    fn ff(circle0: Circle, circle1: Circle) -> CircleConfig {
        int_circle_circle(circle0, circle1)
    }

    #[test]
    fn test_same_circles_01() {
        let circle0 = circle(point(100.0, -100.0), 1.0);
        let circle1 = circle(point(100.0, -100.0), 1.0);
        assert_eq!(ff(circle0, circle1), CircleConfig::SameCircles());
    }

    #[test]
    fn test_same_non_intersection_01() {
        let circle0 = circle(point(1000.0, -1000.0), 1.01);
        let circle1 = circle(point(1000.0, -1000.0), 1.0);
        assert_eq!(ff(circle0, circle1), CircleConfig::NoIntersection());
    }

    #[test]
    fn test_same_non_intersection_02() {
        let circle0 = circle(point(1000.0, -1000.0), 1.0);
        let circle1 = circle(point(1002.0, -1002.0), 1.0);
        assert_eq!(ff(circle0, circle1), CircleConfig::NoIntersection());
    }

    #[test]
    fn test_noncircular_two_points() {
        let eps = f64::EPSILON * 10.0;
        let circle0 = circle(point(10.0, -10.0), 1.0);
        let circle1 = circle(point(10.0, -12.0 + eps), 1.0);
        let point0 = point(10.000000042146848, -11.0);
        let point1 = point(9.999999957853152, -11.0);
        let res = ff(circle0, circle1);
        assert_eq!(res, CircleConfig::NoncocircularTwoPoints(point0, point1));
    }

    #[test]
    fn test_noncircular_one_point_01() {
        let eps = f64::EPSILON * 2.0;
        let circle0 = circle(point(10.0, -10.0), 1.0);
        let circle1 = circle(point(10.0, -12.0 + eps), 1.0);
        let point0 = point(10.0, -11.0);
        let res = ff(circle0, circle1);
        assert_eq!(res, CircleConfig::NoncocircularOnePoint(point0));
    }

    #[test]
    fn test_noncircular_one_point_02() {
        let circle0 = circle(point(10.0, -10.0), 1.0);
        let circle1 = circle(point(10.0, -10.5), 0.5);
        let point0 = point(10.0, -11.0);
        let res = ff(circle0, circle1);
        assert_eq!(res, CircleConfig::NoncocircularOnePoint(point0));
    }

    #[test]
    fn test_noncircular_two_points_1() {
        let eps = f64::EPSILON * 5.0;
        let circle0 = circle(point(10.0, -10.0), 1.0);
        let circle1 = circle(point(10.0, -10.5 - eps), 0.5);
        let point0 = point(10.000000059604645, -10.999999999999998);
        let point1 = point(9.999999940395355, -10.999999999999998);
        let res = ff(circle0, circle1);
        assert_eq!(res, CircleConfig::NoncocircularTwoPoints(point0, point1));
    }

    #[test]

    fn test_noncircular_one_point_03() {
        let eps = f64::EPSILON * 2.0;
        let circle0 = circle(point(1000.0, -1000.0), 100.0);
        let circle1 = circle(point(1000.0, -1200.0 + eps), 100.0);
        let point0 = point(1000.0, -1100.0);
        let res = ff(circle0, circle1);
        assert_eq!(res, CircleConfig::NoncocircularOnePoint(point0));
    }
}

#[cfg(test)]
mod tests_circle_old {
    use rand::rand_core::le;

    use super::*;
    use crate::{circle::circle, utils::perturbed_ulps_as_int};

    fn ff(circle0: Circle, circle1: Circle) -> CircleConfig {
        int_circle_circle(circle0, circle1)
    }

    #[test]
    fn test_same_circles01() {
        let circle0 = circle(point(0.0, 0.0), 1.0);
        let circle1 = circle(point(0.0, 0.0), 1.0);
        assert_eq!(ff(circle0, circle1), CircleConfig::SameCircles());
    }
    #[test]
    fn test_same_circles02() {
        let circle0 = circle(point(0.0, 0.0), 0.0);
        let circle1 = circle(point(0.0, 0.0), 0.0);
        assert_eq!(ff(circle0, circle1), CircleConfig::SameCircles());
    }
    #[test]
    fn test_same_circles03() {
        let circle0 = circle(point(0.0, 0.0), f64::MAX);
        let circle1 = circle(point(0.0, 0.0), f64::MAX);
        assert_eq!(ff(circle0, circle1), CircleConfig::SameCircles());
    }
    #[test]
    fn test_same_circles04() {
        let circle0 = circle(point(f64::MAX, f64::MAX), f64::MAX);
        let circle1 = circle(point(f64::MAX, f64::MAX), f64::MAX);
        assert_eq!(ff(circle0, circle1), CircleConfig::SameCircles());
    }

    #[test]
    fn test_donot_intersect01() {
        let r = perturbed_ulps_as_int(1.0, -2);
        let circle0 = circle(point(-1.0, 0.0), r);
        let circle1 = circle(point(1.0, 0.0), 1.0);
        assert_eq!(ff(circle0, circle1), CircleConfig::NoIntersection());
    }
    #[test]
    fn test_donot_intersect02() {
        let x = perturbed_ulps_as_int(1.0, 2);
        let circle0 = circle(point(-1.0, 0.0), 1.0);
        let circle1 = circle(point(x, 0.0), 1.0);
        assert_eq!(ff(circle0, circle1), CircleConfig::NoIntersection());
    }

    #[test]
    fn test_tangent01() {
        let x = perturbed_ulps_as_int(1.0, 1);
        let circle0 = circle(point(-1.0, 0.0), 1.0);
        let circle1 = circle(point(x, 0.0), 1.0);
        assert_eq!(
            ff(circle0, circle1),
            CircleConfig::NoncocircularOnePoint(point(0.0, 0.0))
        );
    }

    #[test]
    fn test_tangent02() {
        let circle0 = circle(point(1.0, 0.0), 1.0);
        let circle1 = circle(point(1.0 + f64::EPSILON, 0.0), 1.0);
        let res = int_circle_circle(circle0, circle1);
        assert_eq!(
            res,
            CircleConfig::NoncocircularTwoPoints(point(1.0, 1.0), point(1.0, -1.0))
        );
    }

    #[test]
    fn test_tangent03() {
        let _0 = perturbed_ulps_as_int(0.0, 1);
        let _1 = perturbed_ulps_as_int(1.0, -1);
        let circle0 = circle(point(0.0, 0.0), 1.0);
        let circle1 = circle(point(_0, 0.0), 1.0);
        let res = ff(circle0, circle1);
        assert_eq!(res, CircleConfig::SameCircles());
    }

    #[test]
    fn test_tangent04() {
        let _1 = perturbed_ulps_as_int(1.0, -1);
        let circle0 = circle(point(0.0, 0.0), 1.0);
        let circle1 = circle(point(0.0, 0.0), _1);
        let res = ff(circle0, circle1);
        assert_eq!(res, CircleConfig::NoIntersection());
    }

    #[test]
    fn test_tangent05() {
        let _1m = perturbed_ulps_as_int(1.0, -1);
        let _1p = perturbed_ulps_as_int(1.0, 1);
        let circle0 = circle(point(1.0, 0.0), 1.0);
        let circle1 = circle(point(_1p, 0.0), _1m);
        let res = ff(circle0, circle1);
        assert_eq!(
            res,
            CircleConfig::NoncocircularTwoPoints(
                point(1.5, 0.8660254037844386),
                point(1.5, -0.8660254037844386)
            )
        );
    }

    #[test]
    fn test_tangent06() {
        let _1m = perturbed_ulps_as_int(1.0, -2);
        let _1p = perturbed_ulps_as_int(1.0, 1);
        let circle0 = circle(point(1.0, 0.0), 1.0);
        let circle1 = circle(point(_1p, 0.0), _1m);
        let res = ff(circle0, circle1);
        assert_eq!(res, CircleConfig::NoncocircularOnePoint(point(2.0, 0.0)));
    }

    #[test]
    fn test_no_intersection2() {
        let c0 = circle(point(0.5, 0.0), 0.5);
        let c1 = circle(point(-1.0, 0.0), 1.0);
        let res = ff(c0, c1);
        assert_eq!(res, CircleConfig::NoncocircularOnePoint(point(0.0, 0.0)));
    }

    use crate::svg::svg;
    #[test]
    fn test_intersection_issue_01() {
        let mut svg = svg(150.0, 200.0);
        let c0 = circle(point(100.0, 130.0), 20.0);
        let c1 = circle(point(75.0, 40.0), 85.0);
        svg.circle(&c0, "red");
        svg.circle(&c1, "blue");
        let p0 = point(113.87064429562277, 115.59148769566033);
        let p1 = point(80.68522962987866, 124.80965843614482);

        svg.circle(&circle(p0, 1.0), "red");
        svg.write();
        let res = ff(c0, c1);
        assert_eq!(res, CircleConfig::NoncocircularTwoPoints(p0, p1));
    }
}