Sam Burns
Sam Burns

Reputation: 103

Checking collisions with a line and a rectangle

Is it possible to check a collision with a line using an Area object?

Currently the way I am doing it is not working:

This returns false when you run it. But the line is very clearing touching the rectangle, in fact its completely inside it.

import java.awt.Rectangle;
import java.awt.geom.Area;
import java.awt.geom.Line2D;

public class collision {

public static void main(String[] args) {

    Area area1 = new Area(new Rectangle(0, 0, 100, 100));
    Area area2 = new Area(new Line2D.Double(0, 0, 100, 100));

    System.out.println(isColliding(area1, area2));
}

public static boolean isColliding(Area area1, Area area2) {
    if (area2 != null) {
        Area collide1 = new Area(area1);
        collide1.subtract(area2);
        if (!collide1.equals(area1)) {
            return true;
        }
    }

    return false;

}

}

Upvotes: 2

Views: 1704

Answers (2)

Sam Burns
Sam Burns

Reputation: 103

I was able to solve this problem by making a recursive function that checks points along the thing. The higher you set the depth the more accurately it will check, but will take longer to complete. I have been using 10 as my depth (which I believe checks 2047 points along the line) and I encountered no performance loss. Unless your Area object contains really thin parts I don't believe you will need more than this.

Someone feel free to comment and revise my method if you believe you can improve it in any way :)

Thanks to ajb for his suggestion of using a PathIterator which gave me the idea to check points along the line.

public static boolean findPoints(Area area1, Line2D line1, int depth) {

    Point p1 = new Point((int) (line1.getX2() + line1.getX1()) / 2,
            (int) (line1.getY2() + line1.getY1()) / 2);

    if (depth == 0) {
        return false;
    }

    pointMiddle = new Point(p1);
    if (area1.contains(p1)) {
        return true;

    } else {
        return findPoints(area1, new Line2D.Double(p1, line1.getP2()),
                depth - 1)
                || findPoints(area1, new Line2D.Double(line1.getP1(), p1),
                        depth - 1);
    }

}

Upvotes: 1

ajb
ajb

Reputation: 31689

If, as you say in the comments, you know that you will always be checking a Line2D and a Rectangle for collision, you can use the intersects method of Line2D (see javadoc):

public static void main(String[] args) {

    Rectangle rect1 = new Rectangle(0, 0, 100, 100);
    Line2D line2 = new Line2D.Double(0, 0, 100, 100);

    System.out.println(isColliding(rect1, line2));
}

public static boolean isColliding(Rectangle2D rect1, Line2D line2) {
    if (line2 != null) {
        return line2.intersects(rect1);
    }   
    return false;    
}

To test if a line collides with a more general Area is more difficult; I don't think there's a method in the library for this. If the Area is built up of rectangles, you could try saving an array (or List) of the Rectangle objects, and testing whether the line intersects with any of them. Another approach: You can try using the contains method of Area, which tests whether an area contains a point. If either endpoint of the Line2D is contained by the Area, then the line and the area collide. If both endpoints are outside the area, but the area consists of straight lines (area.isPolygonal()), you could retrieve each line segment from the polygon and test whether the line intersects each line segment. You can do this with area.getPathIterator(null), and then using something like this on the resulting path iterator:

double[] coords = new double[6];
double moveX, moveY, prevX, prevY, newX, newY;
while (!pathIterator.isDone()) {
    switch (pathIterator.currentSegment(coords)) {
        case PathIterator.SEG_MOVETO:
             moveX = coords[0];  moveY = coords[1];
             prevX = moveX;  prevY = moveY;
             break;
        case PathIterator.SEG_LINETO:
             newX = coords[0];  newY = coords[1];
             if ([line2 intersects the line from (prevX,prevY) to (newX,newY)]) {
                 return true;
             }
             prevX = newX; prevY = newY;
             break;
        case PathIterator.SEG_QUADTO:
        case PathIterator.SEG_CUBICTO:
             throw new RuntimeException("What is a curve doing in my rectangle?");
        case PathIterator.SEG_CLOSE:
             // go back to the last SEG_MOVETO point, usually the first point
             if ([line2 intersects the line from (prevX,prevY) to (moveX,moveY)]) {
                 return true;
             }
             prevX = newX; prevY = newY;
             break;
    }
}

There are methods in Line2D to test whether a line segment intersects another.

Note: I have not tested this at all. I hope it works.

Upvotes: 0

Related Questions