rcgal 0.2.2

Rust Computational Geometry Algorithms Library.
Documentation
use crate::{
    algorithm::{
        location::{
            location_enum::Point2Circle2Location,
            point_2_circle_segment_2::locate_point_2_circle_segment_2,
        },
        projection::point_2_line_segment_2::point_2_project_line_segment_2,
    },
    kernel::{number_type::NumberType, point_2::Point2, segment_2::Segment2},
};

pub fn is_line_segment_2_circle_segment_2_intersected<T: NumberType>(
    line_segment: &impl Segment2<T>,
    circle_segment: &impl Segment2<T>,
) -> bool {
    let source = line_segment.source();
    let target = line_segment.target();
    let center = circle_segment.center();
    let source_relation = locate_point_2_circle_segment_2(&source, circle_segment);
    let target_relation = locate_point_2_circle_segment_2(&target, circle_segment);
    if source_relation == Point2Circle2Location::Inside
        && target_relation == Point2Circle2Location::Inside
    {
        return false;
    } else if source_relation == Point2Circle2Location::On
        || target_relation == Point2Circle2Location::On
    {
        return true;
    } else {
        let projection_point = point_2_project_line_segment_2(&center, line_segment);
        match projection_point {
            Some(projection_point) => {
                let projection_relation =
                    locate_point_2_circle_segment_2(&projection_point, circle_segment);
                if projection_relation == Point2Circle2Location::On
                    || projection_relation == Point2Circle2Location::Inside
                {
                    return true;
                } else {
                    return false;
                }
            }
            None => {
                return false;
            }
        }
    }
}

pub fn line_segment_2_circle_segment_2_intersection<T: NumberType>(
    line_segment: &impl Segment2<T>,
    circle_segment: &impl Segment2<T>,
) -> Vec<Point2<T>> {
    let mut result = Vec::new();
    let source = line_segment.source();
    let target = line_segment.target();
    let center = circle_segment.center();
    let source_relation = locate_point_2_circle_segment_2(&source, circle_segment);
    let target_relation = locate_point_2_circle_segment_2(&target, circle_segment);
    if source_relation == Point2Circle2Location::Inside
        && target_relation == Point2Circle2Location::Inside
    {
        return result;
    } else if source_relation == Point2Circle2Location::On
        && target_relation == Point2Circle2Location::On
    {
        result.push(source);
        result.push(target);
        return result;
    } else {
        if source_relation == Point2Circle2Location::Inside
            || target_relation == Point2Circle2Location::Inside
        {
            let mut inside_point = source;
            let mut outside_point = target;
            if target_relation == Point2Circle2Location::Inside {
                inside_point = target;
                outside_point = source;
            }
            let v = outside_point - inside_point;
            let w = circle_segment.center() - inside_point;
            let b = w.dot(&v) / v.dot(&v);
            let t = v * b;
            let projection_point = Point2::new(t.x(), t.y());
            let projection_point = inside_point + projection_point;
            let projection_point = Point2::new(projection_point.x(), projection_point.y());
            let distance: T = circle_segment.center().distance(&projection_point);
            let offset =
                (circle_segment.radius() * circle_segment.radius() - distance * distance).sqrt();
            let projection_point = projection_point.get_vector() + v.normalize() * offset;
            result.push(Point2::new(projection_point.x(), projection_point.y()));
            return result;
        } else {
            let projection_point = point_2_project_line_segment_2(&center, line_segment);
            match projection_point {
                Some(projection_point) => {
                    let projection_relation =
                        locate_point_2_circle_segment_2(&projection_point, circle_segment);
                    if projection_relation == Point2Circle2Location::On {
                        result.push(projection_point);
                        return result;
                    } else if projection_relation == Point2Circle2Location::Inside {
                        let distance = circle_segment.center().distance(&projection_point);
                        let radius = circle_segment.radius();
                        let offset = radius * radius - distance * distance;
                        let offset = offset.sqrt();
                        let vector = (target - source).normalize();
                        let point_a = projection_point.get_vector() + vector * offset;
                        let point_b = projection_point.get_vector() - vector * offset;
                        result.push(Point2::new(point_a.x(), point_a.y()));
                        result.push(Point2::new(point_b.x(), point_b.y()));
                        return result;
                    } else {
                        return result;
                    }
                }
                None => {
                    if source_relation == Point2Circle2Location::On {
                        result.push(source);
                    } else if target_relation == Point2Circle2Location::On {
                        result.push(target);
                    }
                    return result;
                }
            }
        }
    }
}

#[cfg(test)]
mod tests {

    use crate::kernel::{
        circle_segment_2::CircleSegment2, line_segment_2::LineSegment2, point_2::Point2,
    };

    use super::*;

    #[test]
    fn is_intersected() {
        let segment_2 = LineSegment2::new(Point2::new(0.0, 0.0), Point2::new(1.0, 1.0));
        let circle_2 = CircleSegment2::new(Point2::new(0.0, 0.0), 1.0);
        assert_eq!(
            is_line_segment_2_circle_segment_2_intersected(&segment_2, &circle_2),
            true
        );

        let segment_2 = LineSegment2::new(Point2::new(0.0, 0.0), Point2::new(1.0, 1.0));
        let circle_2 = CircleSegment2::new(Point2::new(0.0, 0.0), 2.0);
        assert_eq!(
            is_line_segment_2_circle_segment_2_intersected(&segment_2, &circle_2),
            false
        );

        let segment_2 = LineSegment2::new(
            Point2::new(0.0, 0.0),
            Point2::new(f64::sqrt(2.0) / 2.0, f64::sqrt(2.0) / 2.0),
        );
        let circle_2 = CircleSegment2::new(Point2::new(0.0, 0.0), 1.0);
        assert_eq!(
            is_line_segment_2_circle_segment_2_intersected(&segment_2, &circle_2),
            true
        );

        let segment_2 = LineSegment2::new(
            Point2::new(f64::sqrt(2.0) / 2.0 + 0.1, f64::sqrt(2.0) / 2.0),
            Point2::new(f64::sqrt(2.0) / 2.0, f64::sqrt(2.0) / 2.0),
        );
        let circle_2 = CircleSegment2::new(Point2::new(0.0, 0.0), 1.0);
        assert_eq!(
            is_line_segment_2_circle_segment_2_intersected(&segment_2, &circle_2),
            true
        );

        let segment_2 = LineSegment2::new(
            Point2::new(f64::sqrt(2.0) / 2.0 + 0.1, f64::sqrt(2.0) / 2.0),
            Point2::new(3.0, 3.0),
        );
        let circle_2 = CircleSegment2::new(Point2::new(0.0, 0.0), 1.0);
        assert_eq!(
            is_line_segment_2_circle_segment_2_intersected(&segment_2, &circle_2),
            false
        );

        let segment_2 = LineSegment2::new(Point2::new(-2.0, 0.0), Point2::new(2.0, 0.0));
        let circle_2 = CircleSegment2::new(Point2::new(0.0, 0.0), 1.0);
        assert_eq!(
            is_line_segment_2_circle_segment_2_intersected(&segment_2, &circle_2),
            true
        );
    }

    #[test]
    fn intersection() {
        let segment_2 = LineSegment2::new(Point2::new(0.0, 0.0), Point2::new(1.0, 1.0));
        let circle_2 = CircleSegment2::new(Point2::new(0.0, 0.0), 1.0);
        assert_eq!(
            line_segment_2_circle_segment_2_intersection(&segment_2, &circle_2),
            vec!(Point2::new(f64::sqrt(2.0) / 2.0, f64::sqrt(2.0) / 2.0))
        );

        let segment_2 = LineSegment2::new(
            Point2::new(f64::sqrt(2.0) / 2.0, f64::sqrt(2.0) / 2.0),
            Point2::new(f64::sqrt(2.0) / -2.0, f64::sqrt(2.0) / 2.0),
        );
        let circle_2 = CircleSegment2::new(Point2::new(0.0, 0.0), 1.0);
        assert_eq!(
            line_segment_2_circle_segment_2_intersection(&segment_2, &circle_2),
            vec!(
                Point2::new(f64::sqrt(2.0) / 2.0, f64::sqrt(2.0) / 2.0),
                Point2::new(f64::sqrt(2.0) / -2.0, f64::sqrt(2.0) / 2.0)
            )
        );

        let segment_2 = LineSegment2::new(
            Point2::new(10.0, f64::sqrt(2.0) / 2.0),
            Point2::new(-10.0, f64::sqrt(2.0) / 2.0),
        );
        let circle_2 = CircleSegment2::new(Point2::new(0.0, 0.0), 1.0);
        assert_eq!(
            line_segment_2_circle_segment_2_intersection(&segment_2, &circle_2),
            vec!(
                Point2::new(f64::sqrt(2.0) / -2.0, f64::sqrt(2.0) / 2.0),
                Point2::new(f64::sqrt(2.0) / 2.0, f64::sqrt(2.0) / 2.0)
            )
        );

        let segment_2 = LineSegment2::new(Point2::new(10.0, 10.0), Point2::new(-10.0, -10.0));
        let circle_2 = CircleSegment2::new(Point2::new(0.0, 0.0), 1.0);
        assert_eq!(
            line_segment_2_circle_segment_2_intersection(&segment_2, &circle_2),
            vec!(
                Point2::new(f64::sqrt(2.0) / -2.0, f64::sqrt(2.0) / -2.0),
                Point2::new(f64::sqrt(2.0) / 2.0, f64::sqrt(2.0) / 2.0)
            )
        );

        let segment_2 = LineSegment2::new(Point2::new(4.0, 3.0), Point2::new(10.0, 3.0));
        let circle_2 = CircleSegment2::new(Point2::new(0.0, 0.0), 5.0);
        assert_eq!(
            line_segment_2_circle_segment_2_intersection(&segment_2, &circle_2),
            vec!(Point2::new(4.0, 3.0))
        );

        let segment_2 = LineSegment2::new(Point2::new(4.0, 3.0), Point2::new(-10.0, 3.0));
        let circle_2 = CircleSegment2::new(Point2::new(0.0, 0.0), 5.0);
        assert_eq!(
            line_segment_2_circle_segment_2_intersection(&segment_2, &circle_2),
            vec!(Point2::new(-4.0, 3.0), Point2::new(4.0, 3.0),)
        );
    }
}