I need help coming up with an algorithm to determine if the user clicks on any point of a line. A line is given by two points.

So you have x1, y1 positions of the first point, and x2,y2 positions of the second point.

I need to determine in the mousePressed event, if the x and y positions of the mouse (evt.getX() and evt.getY()) fall on a point on the line between the two original points.

Any help on this would be greatly appreciated. Thanks

Dani AI

Generated

A reliable, simple approach is to test the mouse point against the line segment by finding the nearest point on the segment and comparing squared distances to a pixel tolerance. That avoids expensive square roots and handles angled, horizontal, and vertical segments uniformly. was right to flag axis-aligned cases, and implemented a perpendicular-distance + bounding-box clamp; the method below is an alternative that is robust for short/degenerate segments and cheap to compute.

public static boolean isPointNearSegment(int x1,int y1,int x2,int y2,int px,int py,int tol) {
    double dx = x2 - x1;
    double dy = y2 - y1;
    double len2 = dx*dx + dy*dy;
    double t = 0.0;
    if (len2 > 1e-12) {
        t = ((px - x1)*dx + (py - y1)*dy) / len2;   // projection factor
        if (t < 0) t = 0;
        else if (t > 1) t = 1;
    }
    double projx = x1 + t*dx;
    double projy = y1 + t*dy;
    double dist2 = (px - projx)*(px - projx) + (py - projy)*(py - projy);
    return dist2 <= (double)tol * tol;
}

Practical notes: pick tol in screen pixels (increase if the line is drawn thick or the map is zoomed). Use an early quick-reject by checking the mouse against the segment bbox expanded by tol to skip most segments. For many segments, add a spatial index (grid or quadtree) so hit-testing scales. Treat degenerate segments (length ~ 0) by comparing to the endpoint. Prefer double arithmetic to avoid integer overflow on large coordinates.

For convenience, Java already provides a tested implementation: Line2D.ptSegDist(...) (see the JDK docs), which returns the perpendicular distance to a segment and can be compared to your tolerance if you prefer a built-in method: Line2D (java.awt.geom) - ptSegDist.

Recommended Answers

All 5 Replies

In the case that line is horizontal x1 == x2, if line is vertical y1 ==y2.
1) So what you do collect pointer position(a,b)
2) Find out if your line is horizontal or vertical
A) If horizontal a == x1 && ( b >= y1 && b =< y2)
B) if vertical (a >= x1 && a =< x2) && b == y1
That is just one of many approches, however if line is under certain angle this will not work and you will need serious arithmetic to work it out

yes, the line will not always (in fact, usually not) be exactly horizontal or vertical.

The program i am working on is similar to mapquest. I have a Map Editor which loads an image of a (somewhat small area) map. then i create points for locations. Then i connect the points to create a path. I have all of this working, and all the locations are stored in a Vector and all the paths are stored in another Vector. I need to have the option to 1) delete a path, and 2) choose whether it is directed (one-way) or not. In order to do both of these, I need to be able to click on any point of the line to select it. Quite complicated

I figured this out. and yes, it involves comparing a lot of numbers and taking square roots, etc. it works perfectly though.

Good job, would you mind to share with us your solution?

Yep- here it is.

The
((MapEdge)edge.elementAt(index)).get------
lines are just getting the X and Y coordinates of the two endpoints of the lines

public boolean pathSelected(int index, int x, int y)
    {
        int _x0 = ((MapEdge)edge.elementAt(index)).getFrom().x;
        int _y0 = ((MapEdge)edge.elementAt(index)).getFrom().y;
        int _x1 = ((MapEdge)edge.elementAt(index)).getTo().x;
        int _y1 = ((MapEdge)edge.elementAt(index)).getTo().y;
        int _dx = ((MapEdge)edge.elementAt(index)).getTo().x
            - ((MapEdge)edge.elementAt(index)).getFrom().x;
        int _dy = ((MapEdge)edge.elementAt(index)).getTo().y
            - ((MapEdge)edge.elementAt(index)).getFrom().y;
        double dx0 = _dx;
        double dy0 = _dy;
        double dx = x - _x0;
        double dy = y - _y0;
        double d = dy0 * dx - dx0 * dy;
        double r = Math.sqrt(dx0*dx0 + dy0*dy0);
        if(Math.abs(r) < 0.1)
        {
            r = 1;
        }
        d = Math.abs(d/r);
        if(d < 3 &&
           x >= getMinX(_x0, _dx) && x < getMaxX(_x0, _dx) &&
           y >= getMinY(_y0, _dy) && y < getMaxY(_y0, _dy))
        {
            return true;
        }
        else
        {
            return false;
        }
    }
 
public int getMinX(int _x0, int _dx)
        {
            int x1 = _x0 + _dx;
            if(_x0 < x1)
                return _x0;
            else
                return x1;
        }
        public int getMaxX(int _x0, int _dx)
        {
            int x1 = _x0 + _dx;
            if(_x0 > x1)
                return _x0;
            else
                return x1;
        }
        public int getMinY(int _y0, int _dy)
        {
            int y1 = _y0 + _dy;
            if(_y0 < y1)
                return _y0;
            else
                return y1;
        }
        public int getMaxY(int _y0, int _dy)
        {
            int y1 = _y0 + _dy;
            if(_y0 > y1)
                return _y0;
            else
                return y1;
        }
commented: Thank you for sharing the knowledge you learned +6
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.