Самопересечение полилинии

Я пишу код, чтобы определить, является ли полилиния самопересекающейся или нет.
Если хотя бы два звена полилинии пересекаются (в своих внутренних точках), она называется самопересекающейся.
Для начала я пишу функцию для определения пересечения двух пересекающихся отрезков. Там я использую школьную формулу прямой y = kx + b.

А затем функция f, где я проверяю каждые 2 точки 2 отрезков на пересечение. В принципе все работает, но код ломается, когда какая-то часть полилинии не пересекается точно, а просто "задевает" какой-то другой отрезок этой полилинии. Например, как в тесте:

Тест:

      4   
0 0   
2 2   
2 1   
1 1  

Код:

      #include <iostream>
#include <fstream>
using namespace std;

ifstream fin("input.txt");
ofstream fout("output.txt");

class peresec{
    public:
        double x,y;
};

int intersect(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
{
    double k1, k2, b1, b2, x, y, tmp;

    if(x1>=x2) {tmp=x1; x1=x2; x2=tmp; tmp=y1; y1=y2; y2=tmp;}
    if(x3>=x4) {tmp=x3; x3=x4; x4=tmp; tmp=y3; y3=y4; y4=tmp;}

    if(y1==y2) k1=0; else k1 =  ( y2 - y1 ) / ( x2 - x1 );
    if(y3==y4) k2=0; else k2 =  ( y4 - y3 ) / ( x4 - x3 );
    if(k1 == k2) return 0;
   
    b1=y1-k1*x1;
    b2=y3-k2*x3;

    x = (b2-b1)/(k1-k2);
    y = k1*x + b1;

    if(x1<=x && x3<=x && x2>=x && x4>=x && !((x==x1 && y==y1) || (x==x2 && y==y2) || (x==x3 && y==y3) || (x==x4 && y==y4)))
        {return 1;}
    else
        return 0;
}

void f(peresec *a, int n)
{
    int flag;

    for (int i=0; i<n; i++)
        for (int j=0; j<n; j++)
        {
            flag=intersect(a[i].x, a[i].y, a[(i + 1) % n].x, a[(i + 1) % n].y, a[j].x, a[j].y, a[(j + 1) % n].x, a[(j + 1) % n].y);
            if(flag==1) {fout << 1; return;}
        }
    if(flag == 0){fout << 0; return;}
}

int main()
{
    long long count;
    peresec *a;
     if( !(fin >> count)){fout<<0; return 0;}
     fin.seekg(0);
    
    
     fin >> count;
     if(count == 0) {fout<<0; return 0;}
    
     a = new peresec[count];
    for(int  i = 0; i < count; i++){ fin >> a[i].x; fin >> a[i].y;}

    f(a,count);

    return 0;
}

Затем, испытав сбой на этом коде, я решил изменить логику функции пересечения и сделал что-то вроде:

      bool intersect(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
    {
        double v1, v2, v3, v4;
        v1=(x4-x3)*(y1-y3)-(y4-y3)*(x1-x3);
        v2=(x4-x3)*(y2-y3)-(y4-y3)*(x2-x3);
        v3=(x2-x1)*(y3-y1)-(y2-y1)*(x3-x1);
        v4=(x2-x1)*(y4-y1)-(y2-y1)*(x4-x1);
        if((v1*v2<0) && (v3*v4<0)) return true;
        else return false;
    }

Но даже здесь код ломается в этом тесте. Должен выводить 1, если есть самопересечение, иначе 0.

Скорее всего проблема в цикле for в функцииf. Я уже все перепробовал. Я также пробовал это:

      for (int i = 0; i < n - 1; i ++)
        for (int j = i + 2; i < n; j ++)

К сожалению, это не помогло.

Можете ли вы объяснить, почему код ломается???

1 ответ

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

Еще один неприятный случай — когда конечная точка попадает «на» ссылку, и из-за числовых неточностей может быть обнаружено ноль или два пересечения, или, что еще хуже, только одно. И добавление допусков не помогает!


Чтобы решить эти сложные случаи, вы должны сначала выяснить точную причину, по которой вы хотите обнаружить эти самопересечения. Потому что правильные решения зависят от приложения.

Другие вопросы по тегам