Ошибка, Реализация алгоритма числа обмоток, (OpenGL, C ++) - PullRequest
0 голосов
/ 20 марта 2009

Я пытаюсь реализовать алгоритм числа обмоток, чтобы проверить, находится ли точка внутри другого многоугольника. Хотя результаты моего алгоритма неверны и не соответствуют. Я работал над этим целую вечность, и это стало чем-то вроде боли!

Я в основном конвертировал псевдокод из заметок и веб-сайтов, например, softsurfer.com

Я успешно обнаружил, перекрываются ли границы моего игрока и строительного объекта. Я возвращаю результат в структуру (BoxResult), которая сообщает мне, было ли столкновение, и возвращает окно, с которым он столкнулся (ниже)

struct BoxResult{
bool collide;
Building returned;
};

void buildingCollision(){
int wn = 0;                         //winding number count
BoxResult detect = boxDetection();  //detect if any bounding boxes intersect
    if(detect.collide){ //If a bounding box has collided, excute Winding Number Algorithm.
        for(int i = 0; i < player.getXSize(); i++){
            Point p;
            p.x = player.getXi(i);
            p.y = player.getYi(i);
            wn = windingNum(detect.returned,p);
            cout << wn << endl;
            //Continue code to figure out rebound reaction
        }
    }
}

Затем я проверяю столкновение между зданием и игроком (ниже). Я попробовал 5 различных попыток и часов отладки, чтобы понять, где происходит ошибка, однако я реализую самый неэффективный метод, который просто использует математику (ниже).

      int windingNum(Building & b, Point & p){
int result = 0;             //Winding number is one, if point is in poly
float total;            //Counts the total angle between different vertexs
double wn;

    for(int i = 0; i <= b.getXSize()-1;i++){
    float acs, nom, modPV, modPV1, denom, angle;
        if(i ==  3){
                //Create the different points PVi . PVi+1
                Point PV, PV1;
                PV.x = (b.getXi(i) + wx) * p.x; 
                PV.y = (b.getYi(i) + wy) * p.y;
                PV1.x = (b.getXi(0) + wx) * p.x; 
                PV1.y = (b.getYi(0) + wy) * p.y;

                modPV = sqrt( (PV.x * PV.x) + (PV.y * PV.y));       //Get the modulus of PV
                modPV1 = sqrt( (PV1.x * PV1.x) + (PV1.y * PV1.y));  //Get modulus of PV1

                nom = (PV1.x * PV.x) + (PV1.y * PV.y);              //Dot product of PV and PV1
                denom = modPV * modPV1;     //denomintor of winding number equation
                angle = nom / denom;
                acs = acos(angle) * 180/PI;     //find the angle between the different points
                total = total + acs;        //add this angle, to the total angle count
            }
            if(i < 3){
                //Create the different points PVi . PVi+1
                Point PV, PV1;
                PV.x = (b.getXi(i) + wx) * p.x; 
                PV.y = (b.getYi(i) + wy) * p.y;
                PV1.x = (b.getXi(i+1) +wx) * p.x; 
                PV1.y = (b.getYi(i+1) +wy) * p.y;

                modPV = sqrt((PV.x * PV.x) + (PV.y * PV.y));        //Get the modulus of PV
                modPV1 = sqrt((PV1.x * PV1.x) + (PV1.y * PV1.y));   //Get modulus of PV1

                nom = (PV1.x * PV.x) + (PV1.y * PV.y);              //Dot product of PV and PV1
                denom = modPV * modPV1;     //denomintor of winding number equation
                angle = nom / denom;
                acs = acos(angle) * 180/PI;  //find the angle between the different points
                total = total + acs;        //add this angle, to the total angle count
                }
    }

    wn = total;
    if(wn < 360){
        result = 0;}
    if(wn == 360){
        result = 1;}

    return result;
}

По причинам, которые я не понимаю, acs = acos (angle) всегда возвращает 1. # IND000. Кстати, вы знаете, я просто тестирую алгоритм на другом квадрате, отсюда два оператора if, если i == 3 и if i <3. </p>

Также, если вам нужно знать это, wy и wx - это мировые координаты, которые переводятся. Таким образом перемещая игрока по всему миру, например чтобы переместить игрока вперед все переводится на минус число для wy.

Кроме того, объект Building будет выглядеть примерно так, как показано ниже:

struct Building {
vector<float> x;   //vector storing x co-ords
vector<float> y;   //vector storing y co-ords
float ymax, ymin, xmax, xmin //values for bounding box
vector<int> polygons; //stores the number points per polygon (not relevant to the problem)
}

Если кто-нибудь может помочь, я был бы удивительно благодарен! Я просто хотел бы видеть, где все идет не так! (Что-то, я уверен, что все программисты сказали там время lol) Спасибо за чтения ...

Ответы [ 7 ]

1 голос
/ 21 марта 2009

Что касается вашей реализации алгоритма числа пересечений, первая очевидная ошибка заключается в том, что вы не перебираете все стороны. Вы один короткий. Вы должны выполнить цикл до i

int ip1 = ( i + 1 )  % n;

Это относится и к коду в исходном вопросе, разумеется, чтобы вам не пришлось иметь две копии кода.

Вторым является то, что

rem = cn % 1;

не имеет никакого эффекта. Код на софтсёрфере хорош, т.е.

rem = (cn&1);

Он пытается определить, является ли cn нечетным или четным, проверяя, установлен ли последний бит. Если вы хотите провести тот же тест с использованием оператора по модулю%, вы должны записать его как

rem = cn % 2;

, так как это присваивает остаток от деления на два из cn для rem.

Я не смотрел дальше, чтобы увидеть, есть ли еще проблемы.

1 голос
/ 21 марта 2009

Я, вероятно, не понимаю вашу проблему / вопрос, но здесь есть простой и надежный пункт в тесте полигонов: PNPOLY .

1 голос
/ 21 марта 2009

Две строки, вычисляющие модуль PV и PV1, неверны. Они должны быть

modPV  = sqrt(PV.x  * PV.x  + PV.y  * PV.y );
modPV1 = sqrt(PV1.x * PV1.x + PV1.y * PV1.y);

Устраняет ли это проблему?

0 голосов
/ 21 марта 2009

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

int cn_PnPoly( Point P, Building & b, int n )
{
    int    cn = 0;    // the crossing number counter
    int     rem = 0;
    vector<float>x;
    vector<float>y;
    x.swap(b.getX());
    y.swap(b.getY());

        // loop through all edges of the polygon
    for (int i=0; i<n; i++) {    // edge from V[i] to V[i+1]
    int ip1 = (i +1) %n;
        if (((  (y.at(i)+wy) <= P.y) && ( (y.at(ip1)+wy) > P.y))    // an upward crossing
            || ((  (y.at(i)+wy) > P.y) && ( (y.at(ip1)+wy) <= P.y))) { // a downward crossing
            // compute the actual edge-ray intersect x-coordinate
                float vt = (float)(P.y - (y.at(i)+wy) ) / ( (y.at(ip1)+wy) - (y.at(i)+wy) );
                if (P.x < (x.at(i)+wx) + vt * ( (x.at(ip1)+wx) - (x.at(i)+wx) )) // P.x < intersect
                ++cn;   // a valid crossing of y=P.y right of P.x
        }
    }
    rem =  (cn&1);
    return (rem);    // 0 if even (out), and 1 if odd (in)
}

Хотя это работает, чтобы определить, когда точка находится в многоугольнике, она не учитывает текущий курс игрока.

Если это не имеет смысла, в 2D-игре я перемещаю карту мира вокруг игрока, переводя все многоугольники с помощью мировых координат. Это wx и wy в игре. Также я вращаю игрока около заголовка переменной.

Они определены в функции рисования, однако функция обнаружения столкновений не учитывает направление. Для этого нужно ли умножить координаты x и y, заданные объектом Building, на заголовок? К сожалению, я не очень хорош в геометрии.

0 голосов
/ 21 марта 2009

Привет, сделал то, что Трубадур предложил относительно алгоритма пересечения чисел, и сделал несколько изменений, однако оператор if по какой-то причине никогда не возвращает true. Я публикую новый код ниже. Кстати, еще раз спасибо за все ответы: -)

int cn_PnPoly( Point P, Building & b, int n )
{
    int    cn = 0;    // the crossing number counter
    int     rem = 0;
    vector<float>x;
    vector<float>y;
    x.swap(b.getX());
    y.swap(b.getY());
    //// loop through all edges of the polygon
    //for (int i=0; i<n; i++) {    // edge from V[i] to V[i+1]
    //   if (((V[i].y <= P.y) && (V[i+1].y > P.y))    // an upward crossing
    //    || ((V[i].y > P.y) && (V[i+1].y <= P.y))) { // a downward crossing
    //        // compute the actual edge-ray intersect x-coordinate
    //        float vt = (float)(P.y - V[i].y) / (V[i+1].y - V[i].y);
    //        if (P.x < V[i].x + vt * (V[i+1].x - V[i].x)) // P.x < intersect
    //            ++cn;   // a valid crossing of y=P.y right of P.x
    //    }
    //}
    //return (cn&1);    // 0 if even (out), and 1 if odd (in)


        // loop through all edges of the polygon
    for (int i=0; i<n; i++) {    // edge from V[i] to V[i+1]
    int ip1 = (i +1) %n;
        if (((y.at(i) <= P.y) && (y.at(ip1) > P.y))    // an upward crossing
            || ((y.at(i) > P.y) && (y.at(ip1) <= P.y))) { // a downward crossing
            // compute the actual edge-ray intersect x-coordinate
                float vt = (float)(P.y - y.at(i)) / (y.at(ip1) - y.at(i));
                if (P.x < x.at(i) + vt * (x.at(ip1) - x.at(i))) // P.x < intersect
                ++cn;   // a valid crossing of y=P.y right of P.x
        }
    }
    rem =  (cn&1);
    return (rem);    // 0 if even (out), and 1 if odd (in)
}
0 голосов
/ 21 марта 2009

Я пытался реализовать PNPOLY, как предполагает Одрис. Однако это дает некоторые забавные результаты. Ниже приведен оригинальный код C, а ниже - мое преобразование этого кода для моего приложения ...

Оригинальный код C ...

int pnpoly(int nvert, float *vertx, float *verty, float testx, float testy)
{
  int i, j, c = 0;
  for (i = 0, j = nvert-1; i < nvert; j = i++) {
    if ( ((verty[i]>testy) != (verty[j]>testy)) &&
     (testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )
       c = !c;
  }
  return c;
}

Мой код ....

Где wx и wy - глобальные координаты.

int pnpoly(int nvert, vector<float> vertx, vector<float> verty, float testx, float testy)
{
  int i, j, c = 0;
  for (i = 0, j = nvert-1; i < nvert; j = i++) {
      if ( (( (verty.at(i)+wy) > testy) != ( (verty.at(j)+wy) >testy)) &&
        (testx < ((vertx.at(j)+wx) - (vertx.at(i)+wx) ) * (testy- (verty.at(i)+wy) ) / ( (verty.at(j)+wy) - (verty.at(i)+wy)) + (vertx.at(i)+wx)) )
       c++;
  }
  return c;
}

Я тестирую объект игрока против 2D квадратного здания. Это также возвращает странные результаты, когда я нажимаю на нижнюю строку (xmin, ymin до xmax, ymin), все работает нормально. Если я ударю по этике сторон (xmin, ymin до xmin, ymax или xmax, ymin до xmax, ymax), он возвращает 1, только если игрок находится далеко от точки оргина. Также на стороне (xmin, ymin до xmin, ymax), где игрок входит в ограничивающий прямоугольник, алгоритм возвращает 2, несмотря на попадание в многоугольник. На верхней стороне (xmin, ymax к xmax, ymax) возвращается 1, только если игрок полностью находится в многоугольнике.

Также я передаю два вектора x и y из объекта Building и размер вектора как int nvert. Может ли это быть связано с заголовком объекта игрока? Как учитывается алгоритм?

0 голосов
/ 21 марта 2009

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

int cn_PnPoly( Point P, Building & b, int n )
{
    int    cn = 0;    // the crossing number counter
    int     rem = 0;
    vector<float>x;
    vector<float>y;
    x.swap(b.getX());
    y.swap(b.getY());
    //// loop through all edges of the polygon
    //for (int i=0; i<n; i++) {    // edge from V[i] to V[i+1]
    //   if (((V[i].y <= P.y) && (V[i+1].y > P.y))    // an upward crossing
    //    || ((V[i].y > P.y) && (V[i+1].y <= P.y))) { // a downward crossing
    //        // compute the actual edge-ray intersect x-coordinate
    //        float vt = (float)(P.y - V[i].y) / (V[i+1].y - V[i].y);
    //        if (P.x < V[i].x + vt * (V[i+1].x - V[i].x)) // P.x < intersect
    //            ++cn;   // a valid crossing of y=P.y right of P.x
    //    }
    //}
    //return (cn&1);    // 0 if even (out), and 1 if odd (in)


        // loop through all edges of the polygon
    for (int i=0; i<n-1; i++) {    // edge from V[i] to V[i+1]
        if (((y.at(i) <= P.y) && (y.at(i+1) > P.y))    // an upward crossing
            || ((y.at(i) > P.y) && (y.at(i+1) <= P.y))) { // a downward crossing
            // compute the actual edge-ray intersect x-coordinate
                float vt = (float)(P.y - y.at(i)) / (y.at(i+1) - y.at(i));
                if (P.x < x.at(i) + vt * (x.at(i+1) - x.at(i))) // P.x < intersect
                ++cn;   // a valid crossing of y=P.y right of P.x
        }
    }
    rem = cn % 1;
    return (rem);    // 0 if even (out), and 1 if odd (in)
}

Опять же, это всегда возвращает ноль, я не уверен, почему!?! Я неправильно преобразовал алгоритм? Имеет ли значение, в каком направлении проверяются точки (т.е. по часовой стрелке, против часовой стрелки)?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...