dimforge / ncollide

2 and 3-dimensional collision detection library in Rust.
https://ncollide.org
Apache License 2.0
921 stars 105 forks source link

Inconsistent behavior of AABB::clip_line method on edge case. #284

Closed ivanceras closed 5 years ago

ivanceras commented 5 years ago

I'm trying to get intersection between a line segment and an AABB box, this works for most use-case, but one edge case where the line-segment is collinear with the side of the box, the behavior seems to be inconsistent. The bottom and left side result is OK, while the top and right seems to be wrong.

    #[test]
    fn test_horizontal_clip_line_top() {
        let aabb = AABB::new(Point2::new(0.0, 0.0), Point2::new(1.0, 2.0));

        let start: Point2<f32> = Point2::new(0.0, 2.0);
        let end = Vector2::new(1.0, 2.0);

        let clipped = aabb.clip_line(&start, &end).expect("There should be 1");
        println!("clipped: {:#?}", clipped);
        assert_eq!(*clipped.a(), Point2::new(0.0, 2.0)); //start OK
        assert_eq!(*clipped.b(), Point2::new(1.0, 2.0)); //end ERR, clipped.b() = (0.0, 2.0)
    }

    #[test]
    fn test_horizontal_clip_bottom() {
        let aabb = AABB::new(Point2::new(0.0, 0.0), Point2::new(1.0, 2.0));

        let start: Point2<f32> = Point2::new(0.0, 0.0);
        let end = Vector2::new(1.0, 0.0);

        let clipped = aabb.clip_line(&start, &end).expect("There should be 1");

        println!("clipped: {:#?}", clipped);
        assert_eq!(*clipped.a(), Point2::new(0.0, 0.0));//start OK
        assert_eq!(*clipped.b(), Point2::new(1.0, 0.0));//end  OK
    }

    #[test]
    fn test_vertical_clip_line_left() {
        let aabb = AABB::new(Point2::new(0.0, 0.0), Point2::new(1.0, 2.0));

        let start: Point2<f32> = Point2::new(0.0, 0.0);
        let end = Vector2::new(0.0, 2.0);

        let clipped = aabb.clip_line(&start, &end).expect("There should be 1");
        println!("clipped: {:#?}", clipped);
        assert_eq!(*clipped.a(), Point2::new(0.0, 0.0)); // start OK
        assert_eq!(*clipped.b(), Point2::new(0.0, 2.0)); // end OK
    }

    #[test]
    fn test_vertical_clip_line_right() {
        let aabb = AABB::new(Point2::new(0.0, 0.0), Point2::new(1.0, 2.0));

        let start: Point2<f32> = Point2::new(1.0, 0.0);
        let end = Vector2::new(1.0, 2.0);

        let clipped = aabb.clip_line(&start, &end).expect("There should be 1");
        println!("clipped: {:#?}", clipped);
        assert_eq!(*clipped.a(), Point2::new(1.0, 0.0)); //start OK
        assert_eq!(*clipped.b(), Point2::new(1.0, 2.0)); // end ERR, clipped.b = (1.0, 0.0)
    }
ivanceras commented 5 years ago

Turns out I didn't convert the end point to be a vector. Making the 2nd argument in clip_line function to be a vector from start point to end point will return the correct clip_line instead of just passing the end_point with half ass vector conversion.