How to determine if two points do not have any obstructions between them

945 Views Asked by At

I am currently trying to put together an algorithm where I can know if there is an obstruction between two defined points in a plane.

Here is an example image. enter image description here

We can see with the image that point 1, 2, 3, & 6 are all accessible from the origin point. Points 4 and 5 are not. You pass through the polygon.

The code I am using is the following. pStartPoint and pEndPoint is the line from the origin to the point in question. The function checks all edges to see if the line passes through the edge.

public double GetSlopeOfLine(Point a, Point b){

    double x =  b.y - a.y;
    double y = b.x - a.x;

    return (x / y);
}

public double GetOffsetOfLine(double x, double y, double slope){
    return (y - (slope * x));
}

public boolean IsPointAccessable(Point pStartPoint, Point pEndPoint){

    //Define the equation of the line for these points. Once we have slope and offset the equation is
    //y = slope * x + offset;

    double slopeOfLine = GetSlopeOfLine(pStartPoint, pEndPoint);
    double offSet = GetOffsetOfLine(pStartPoint.x, pStartPoint.y, slopeOfLine);

    //Collision detection for each side of each obstacle. Once we get the point of collision, does it lie on the 
    //line in between the two points? If so, collision, and I can't reach that point yet. 

    for (Iterator<Obstacles> ObstacleIt = AdjustedObstaclesList.iterator(); ObstacleIt.hasNext();) {

        Obstacles pObstacle = ObstacleIt.next();

        int NumberOfEdges = pObstacle.getPoints().size();

        for(int i=0; i<NumberOfEdges; i++){

            //Get Edge[i];
            int index = i;
            Point pFirstPoint = (Point)pObstacle.getPoints().get(index);
            if(i >= NumberOfEdges - 1)
                index = 0;
            else
                index = i+1;

            Point pNextPoint = (Point)pObstacle.getPoints().get(index);

            double slopeOfEdge = GetSlopeOfLine(pFirstPoint, pNextPoint);
            double offsetEdge = GetOffsetOfLine(pNextPoint.x, pNextPoint.y, slopeOfEdge);

            int x = Math.round((float) ((-offSet + offsetEdge) / (slopeOfLine - slopeOfEdge)));
            int y = Math.round((float) ((slopeOfLine * x) + offSet));

            //If it lies on either point I could be looking at two adjacent points. I can still reach that point. 
            if(x > pStartPoint.x && x < pEndPoint.x && y > pStartPoint.y && y < pEndPoint.y &&
               x > pFirstPoint.x && x < pNextPoint.x && y > pFirstPoint.y && y < pNextPoint.y){

                return false;
            }
        }
    }
    return true;
}

If the line passes through and the point where the lines cross is found between pStartPoint and pEndPoint I am assuming that pEndPoint cannot be reached.

This function is not working and I am wondering if it has something to do with the fact that the origin is not at the bottom left but at the top left and that (width, height) of my window is located in the bottom right. Therefore the coordinate plane is messed up.

My mind must be mush because I cannot think how to adjust for this and if that is truly my mistake as I cannot seem to fix the error. I thought adjusting the slope and offset by multiplying each by -1 might have been the solution but that doesn't seem to work.

Is my solution the right one? Does my code seem correct in checking for an intersect point? Is there a better solution to see if a point is accessible.

There is also going to be the next step after this where once I determine what points are accessible if I am now on one of the points of the polygon. For example, from point 1 what points are accessible without crossing into the polygon?

2

There are 2 best solutions below

9
Alerra On

First, I would like to say that using slopes for this kind of task is do-able, but also difficult due to the fact that they are very volatile in the sense that they can go from negative infinity to infinity with a very small change in the point. Here's a slightly different algorithm, which relies on angles rather than slopes. Another advantage of using this is that the coordinate systems don't really matter here. It goes like this (I reused as much of your existing code as I could):

public boolean IsPointAccessable(Point pStartPoint, Point pEndPoint) {

    //Collision detection for each side of each obstacle. Once we get the point of collision, does it lie on the 
    //line in between the two points? If so, collision, and I can't reach that point yet. 

    for (Iterator<Obstacles> ObstacleIt = AdjustedObstaclesList.iterator(); ObstacleIt.hasNext();) {

        Obstacles pObstacle = ObstacleIt.next();

        int NumberOfEdges = pObstacle.getPoints().size();

        for(int i=0; i<NumberOfEdges; i++){

            //Get Edge[i];
            int index = i;
            Point pFirstPoint = (Point)pObstacle.getPoints().get(index);
            if(i >= NumberOfEdges - 1)
                index = 0;
            else
                index = i+1;

            Point pNextPoint = (Point)pObstacle.getPoints().get(index);

            // Here is where we get a bunch of angles that encode in them important info on 
            // the problem we are trying to solve.
            double angleWithStart = getAngle(pNextPoint, pFirstPoint, pStartPoint);
            double angleWithEnd = getAngle(pNextPoint, pFirstPoint, pEndPoint);
            double angleWithFirst = getAngle(pStartPoint, pEndPoint, pFirstPoint);
            double angleWithNext = getAngle(pStartPoint, pEndPoint, pNextPoint);                

            // We have accumulated all the necessary angles, now we must decide what they mean. 
            // If the 'start' and 'end' angles are different signs, then the first and next points
            // between them. However, for a point to be inaccessible, it also must be the case that
            // the 'first' and 'next' angles are opposite sides, as then the start and end points
            // Are between them so a blocking occurs. We check for that here using a creative approach

            // This is a creative way of checking if two numbers are different signs.
            if (angleWithStart * angleWithEnd <= 0 && angleWithFirst * angleWithNext <= 0) {
                return false;
            }

        }
    }
    return true;
}

Now, all that is left to do is find a method that calculates the signed angle formed by three points. A quick google search yielded this method (from this SO question):

private double getAngle(Point previous, Point center, Point next) { 
   return Math.toDegrees(Math.atan2(center.x - next.x, center.y - next.y)-
                    Math.atan2(previous.x- center.x,previous.y- center.y));
}

Now, this method should work in theory (I am testing to be sure and will edit my answer if I find any issues with signs of angles or something like that). I hope you get the idea and that my comments explain the code well enough, but please leave a comment/question if you want me to elaborate further. If you don't understand the algorithm itself, I recommend getting a piece of paper out and following the algorithm to see what exactly is going on. Hope this helps!

EDIT: To hopefully aid in better understanding the solution using angles, I drew a picture with the four base cases of how the start, end, first, and next could be oriented, and have attached it to this question. Sorry for the sloppiness, I drew it rather quickly, but this should in theory make the idea clearer.

picture

0
Mike 'Pomax' Kamermans On

If you have a low segment count (for instance, your example only shows 12 segments for three shapes, two shapes of which we know we can ignore (because of bounding box checks), then I would recommend simply performing line/line intersection checking.

Point s = your selected point;
ArrayList<Point> points = polygon.getPoints();
ArrayList<Edge> edges = polygon.getEdges();
for(Point p: points) {
  Line l = new Line(s, p);
  for(Edge e: edges) {
    Point i = e.intersects(l);
    if (i != null) {
      System.out.println("collision", i.toString());
    }
  }
}

With an intersects method that is pretty straight forward:

Point intersects(Line l) {
  // boring variable aliassing:
  double x1 = this.p1.x,
         y1 = this.p1.y,
         x2 = this.p2.x,
         y2 = this.p2.y,
         x3 = l.p1.x,
         y2 = l.p1.y,
         x3 = l.p2.x,
         y2 = l.p2.y,
  // actual line intersection algebra:
         nx = (x1 * y2 - y1 * x2) * (x3 - x4) -
              (x1 - x2) * (x3 * y4 - y3 * x4),
         ny = (x1 * y2 - y1 * x2) * (y3 - y4) -
              (y1 - y2) * (x3 * y4 - y3 * x4),
          d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
  if (d == 0) return null;
  return new Point(nx/d, ny/d);
}