Andre Yonadam
Andre Yonadam

Reputation: 1034

Best method to use for to see if a point is on a line given three pairs of coordinates?

There may have been questions asked about finding if a point is on a line but not in this context and they don't go into answering why various methods have different flaws. What is the best method for accurately finding if a point is on a line given the the coordinate of the point and the coordinates of the line? I've attempted to implement certain methods on my own but they all seem to have their own problems after tests. I'm sure there are other methods out there and I know Java has a method that calculates the distance of a point from a line and returns 0 if its on the line. What method does Java use to find out if a point is on a line?

Method One

The first method compares the distance from point A to point B with the distance from point A to C plus point C to B. The problem with this method is that it isn't precise since it uses Math.sqrt.

public static boolean inLine(double x1, double y1, double x2, double y2, double x3, double y3){
    if((distance(x1, y1, x3, y3) + distance(x2, y2, x3, y3)) == distance(x1, y1, x2, y2)){
        return true;
    }
    return false;
}
public static double distance(double x1, double y1, double x2, double y2){
    double base = x2 - x1;
    double height = y2 - y1;
    double hypotenuse = Math.sqrt((base * base) + (height * height));
    return hypotenuse;
}

Method Two

This is the second method I came up with. It checks if the point is on the line by comparing the the y value of the point to the y value on the line with the same x value as the point. The problem with this method is that it won't work when the line is vertical or horizontal so I implemented some tests to check if its on the line if the line is horizontal or vertical.

public static boolean inLine(double x1, double y1, double x2, double y2, double x3, double y3){

//Check if X and Y Values of the point are within the range of the line
    if(inBetween(x1, x2, x3) & inBetween(y1, y2, y3) ){
        //Check if denominator is going to equal 0 when finding the slope and x of point has the same value
        if(x1 == x2 && x2 == x3){
            if(inBetween(y1, y2, y3)){
                return true;
            }else{
                return false;
            }
        }else if(y1 == y2 && y2 == y3){
            if(inBetween(x1, x2, x3)){
                return true;
            }else{
                return false;
            }
        }else{
            double slope = (y2-y1)/(x2-x1);
            //Check if the y value of the line is equal to the y value of the point
            if(findYIntercept(slope, x1, y1)+slope*x3 == y3){
                return true;
            }
        }

    }else{
        return false;
    }
    return false;
}
public static double findYIntercept(double slope, double x, double y){
    return y-(slope*x);
}
public static boolean inBetween(double a, double b, double c){
    if(a <= c && c <= b){
        return true;
    }
    else if(a >= c && c >= b){
        return true;
    }
    return false;
}

Method Three

This method is similar to the second method but checks if the slopes are identical. It also doesn't work if the line is horizontal or vertical. I also have implemented a situation if the line is horizontal or vertical.

public static boolean inLine(double x1, double y1, double x2, double y2, double x3, double y3){

//Check if X and Y Values of the point are within the range of the line
        if(inBetween(x1, x2, x3) & inBetween(y1, y2, y3) ){
            //Check if denominator is going to equal 0 when finding the slope and x of point has the same value
            if(x1 == x2 && x2 == x3){
                if(inBetween(y1, y2, y3)){
                    return true;
                }else{
                    return false;
                }
            }else if(y1 == y2 && y2 == y3){
                if(inBetween(x1, x2, x3)){
                    return true;
                }else{
                    return false;
                }
            }else{
                double slope1 = (y2-y1)/(x2-x1);
                double slope2 = (y3-y1)/(x3-x1);
                //Check if the y value of the line is equal to the y value of the point
                if(slope1 == slope2){
                    return true;
                }
            }

        }else{
            return false;
        }
        return false;
    }
    public static double findYIntercept(double slope, double x, double y){
        return y-(slope*x);
    }
    public static boolean inBetween(double a, double b, double c){
        if(a <= c && c <= b){
            return true;
        }
        else if(a >= c && c >= b){
            return true;
        }
        return false;
    }

Upvotes: 1

Views: 1872

Answers (3)

Paul Boddington
Paul Boddington

Reputation: 37665

The best method is probably the first. Any method using slopes is problematic because vertical lines have infinite slope.

The problem with your code for method one is not Math.sqrt, it's that floating point calculations are not exact. As a result it is almost always wrong to compare double values a and b using ==. Instead you should use something like Math.abs(a - b) < 0x1p-32 to see if they are close enough.

Because of the limitations of floating point calculations, it is extremely difficult to do this kind of thing properly. java.awt doesn't do it very well. For example, the following program prints 0.5, which is incredibly inaccurate.

double a = 18981256.0;
System.out.println(new Line2D.Double(1.0, 2.0, 2.0, 4.0).ptLineDist(a, 2 * a));

Upvotes: 5

sprinter
sprinter

Reputation: 27996

If this question refers to the java.awt.geom package then the answer is that there is no built in method. There is a Line2D.contains method but that always returns false because it refers to an area containing a point and a line has no area.

Personally I find your third method easiest to use. However you don't need to actually find the slope - you can compare the cross-multiplication to see if they have the same gradient.

That is, dy1 / dx1 = dy2 / dx2 => dy1 * dx2 = dx1 * dy2 (only if dx1 & dx2 != 0)

that takes care of the horizontal case (dy = 0).

Upvotes: 1

laune
laune

Reputation: 31300

I think using vectors is preferable.

class Vector {
    private double x;
    private double y;
    Vector( double x, double y ){
        this.x = x;
        this.y = y;
    }
    Vector( Point p1, Point p2 ){
        this( p1.getX() - p2.getX(), p1.getY() - p2.getY() );
    }
    public double getX(){ return x; }
    public double getY(){ return y; }
    public boolean isColl( Vector v ){
       return y*v.getX() == x*v.getY();
    }
}

class Point extends Vector {
    Point( double x, double y ){
        super( x, y );
    }
}

And here's a test:

public static void main(String[] args) throws Exception {
{
    Point g1 = new Point( 3, 0 );
    Point g2 = new Point( 6, 0 );
    for( Point p: new Point[]{ new Point ( 9, 0 ),
                   new Point ( 0, 9 ),
                   new Point ( 3, 3 ) } ){
    Vector g = new Vector( g1, g2 );
    Vector v1 = new Vector( g1, p );
    Vector v2 = new Vector( g2, p );
    System.out.println( v1.isColl( v2 ) );
    }
}
{
    Point g1 = new Point( 0, 3 );
    Point g2 = new Point( 0, 6 );
    for( Point p: new Point[]{ new Point ( 9, 0 ),
                   new Point ( 0, 9 ),
                   new Point ( 3, 3 ) } ){
    Vector g = new Vector( g1, g2 );
    Vector v1 = new Vector( g1, p );
    Vector v2 = new Vector( g2, p );
    System.out.println( v1.isColl( v2 ) );
    }
}
{
    Point g1 = new Point( 2, 3 );
    Point g2 = new Point( 10, 15 );
    for( Point p: new Point[]{ new Point ( 9, 0 ),
                   new Point ( 0, 9 ),
                   new Point ( 20, 30 ) } ){
    Vector g = new Vector( g1, g2 );
    Vector v1 = new Vector( g1, p );
    Vector v2 = new Vector( g2, p );
    System.out.println( v1.isColl( v2 ) );
    }
}
}

Upvotes: 0

Related Questions