Как определить, что между двумя точками нет препятствий - PullRequest
0 голосов
/ 25 августа 2018

В настоящее время я пытаюсь составить алгоритм, в котором я могу узнать, есть ли препятствие между двумя определенными точками на плоскости.

Вот пример изображения. enter image description here

Из изображения видно, что точки 1, 2, 3 и 6 доступны из исходной точки. Пункты 4 и 5 нет. Вы проходите через многоугольник.

Я использую следующий код. pStartPoint и pEndPoint - это линия от начала координат до рассматриваемой точки. Функция проверяет все ребра, чтобы увидеть, проходит ли линия через ребро.

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;
}

Если линия проходит и найдена точка пересечения линий между pStartPoint и pEndPoint, я предполагаю, что pEndPoint не может быть достигнут.

Эта функция не работает, и мне интересно, имеет ли она какое-то отношение к тому факту, что источник находится не внизу слева, а вверху слева и что (ширина, высота) моего окна находится внизу право. Поэтому координатная плоскость испорчена.

Мой разум, должно быть, месиво, потому что я не могу думать, как приспособиться к этому, и если это действительно моя ошибка, так как я не могу исправить ошибку. Я думал, что регулировка наклона и смещения путем умножения каждого на -1 могла бы быть решением, но, похоже, это не сработало.

Правильно ли мое решение? Мой код кажется правильным при проверке точки пересечения? Есть ли лучшее решение, чтобы увидеть, если точка доступна.

Будет также следующий шаг после этого, когда я определю, какие точки доступны, если я сейчас нахожусь на одной из точек многоугольника. Например, из пункта 1 какие точки доступны без пересечения в многоугольник?

Ответы [ 2 ]

0 голосов
/ 26 августа 2018

Если у вас низкое количество сегментов (например, в вашем примере показаны только 12 сегментов для трех фигур, две из которых, как мы знаем, мы можем игнорировать (из-за проверок ограничивающего прямоугольника), то я бы порекомендовал просто выполнить строку / линию проверка пересечения.

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());
    }
  }
}

С intersects методом, который довольно прост:

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);
}
0 голосов
/ 26 августа 2018

Во-первых, я хотел бы сказать, что использование уклонов для такого рода задач выполнимо, но также сложно из-за того, что они очень изменчивы в том смысле, что они могут переходить из отрицательной бесконечности в бесконечность с оченьнебольшое изменение в точке.Вот немного другой алгоритм, который основывается на углах , а не на склонах.Еще одним преимуществом использования этого является то, что системы координат здесь не имеют большого значения.Это выглядит так (я повторно использовал столько существующего кода, сколько смог):

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;
}

Теперь все, что осталось сделать, - это найти метод, который вычисляет угол со знаком, образованный тремя точками.Быстрый поиск в Google дал этот метод (из этого ТАКого вопроса):

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));
}

Теперь этот метод должен работать в теории (я проверяю, чтобы убедиться, и отредактирую свой ответ, еслиЯ нахожу какие-либо проблемы с признаками углов или что-то подобное).Я надеюсь, что вы поняли идею, и что мои комментарии достаточно хорошо объясняют код, но, пожалуйста, оставьте комментарий / вопрос, если вы хотите, чтобы я уточнил подробнее.Если вы не понимаете сам алгоритм, я рекомендую достать лист бумаги и следовать алгоритму, чтобы увидеть, что именно происходит.Надеюсь, это поможет!

РЕДАКТИРОВАТЬ: Надеюсь, чтобы помочь лучше понять решение с помощью углов, я нарисовал картину с четырьмя базовыми случаями того, как start, end, first и next могли бы ориентироваться и приложить его к этому вопросу.Извините за неряшливость, я нарисовал ее довольно быстро, но теоретически это должно прояснить идею.

picture

...