Путь Безье, посмотрите, пересекает ли он

У меня есть код, который позволяет пользователю рисовать фигуру, для этого я использую UIBezierPath. Но мне нужно посмотреть, пересекает ли фигура саму себя, например, так: http://upload.wikimedia.org/wikipedia/commons/0/0f/Complex_polygon.svg Тогда это недопустимая форма. Как я могу найти это?

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

Я думаю, что проблема где-то в этом методе.

-(BOOL)pathIntersects:(double *)x:(double *)y {
int count = pathPoints.count;
CGPoint p1, p2, p3, p4;
for (int a=0; a<count; a++) {

    //Line 1
    if (a+1<count) {
        p1 = [[pathPoints objectAtIndex:a] CGPointValue];
        p2 = [[pathPoints objectAtIndex:a+1] CGPointValue];
    }else{
        return NO;
    }

    for (int b=0; b<count; b++) {

        //Line 2
        if (b+1<count) {
            p3 = [[pathPoints objectAtIndex:b] CGPointValue];
            p4 = [[pathPoints objectAtIndex:b+1] CGPointValue];
        }else{
            return NO;
        }


        if (!CGPointEqualToPoint(p1, p3) && !CGPointEqualToPoint(p2, p3) && !CGPointEqualToPoint(p4, p1) && !CGPointEqualToPoint(p4, p2)
            && !CGPointEqualToPoint(p1, p2) && !CGPointEqualToPoint(p3, p4)) {

            if (LineIntersect(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, x, y)) { 
                return YES;
            }

        }

    }
}
return NO;
}

Это код, который я нашел, чтобы увидеть, пересекаются ли две строки. Он на C, но я должен работать.

int LineIntersect(
              double x1, double y1,
              double x2, double y2,
              double x3, double y3,
              double x4, double y4,
              double *x, double *y)
{
double mua,mub;
double denom,numera,numerb;

denom  = (y4-y3) * (x2-x1) - (x4-x3) * (y2-y1);
numera = (x4-x3) * (y1-y3) - (y4-y3) * (x1-x3);
numerb = (x2-x1) * (y1-y3) - (y2-y1) * (x1-x3);

/* Are the line coincident? */
if (ABS(numera) < 0.00001 && ABS(numerb) < 0.00001 && ABS(denom) < 0.00001) {
    *x = (x1 + x2) / 2;
    *y = (y1 + y2) / 2;
    return(TRUE);
}

/* Are the line parallel */
if (ABS(denom) < 0.00001) {
    *x = 0;
    *y = 0;
    return(FALSE);
}

/* Is the intersection along the the segments */
mua = numera / denom;
mub = numerb / denom;
if (mua < 0 || mua > 1 || mub < 0 || mub > 1) {
    *x = 0;
    *y = 0;
    return(FALSE);
}
*x = x1 + mua * (x2 - x1);
*y = y1 + mua * (y2 - y1);
return(TRUE);
}

person emillime    schedule 15.11.2012    source источник
comment
Вы говорите о многоугольнике, как в примере, или о кривых Безье?   -  person SJuan76    schedule 15.11.2012
comment
Я говорю о полигонах. Я позволяю пользователю рисовать путь, а затем закрываю путь, чтобы он стал многоугольником. Но если путь пересекает сам себя, то форма недействительна и должна быть удалена.   -  person emillime    schedule 15.11.2012


Ответы (1)


Это зависит от того, насколько сложным может быть многоугольник, нарисованный пользователем, и от количества точек на пути. В идеале должна быть точка для всех вершин фигуры и ничего более. Получите CGPath из UIBezierPath и используйте GCPathApply для передачи элементов функции, которая добавляет каждую точку в массив. Обход массива с двумя циклами for, один из которых вложен в другой, который проверяет каждый отрезок строки на соответствие каждому отрезку после него, используя стандартную проверку пересечения строк. Как только пересечение будет найдено, вырваться из петли. Или, если бы это был удобный метод, вернуть BOOL. Это самый простой способ.

РЕДАКТИРОВАТЬ: Вот пример функции пересечения линии, которая возвращает BOOL, сообщающий вам, пересекаются ли два сегмента. Передайте две точки, которые создают первый сегмент, а затем две точки, которые составляют второй сегмент. Он был поспешно изменен из фрагмента исходного кода, который я быстро нашел в Интернете, но он работает.

CGPoint lineSegmentsIntersect(CGPoint L1P1, CGPoint L1P2, CGPoint L2P1, CGPoint L2P2) 
{ 
    float x1 = L1P1.x, x2 = L1P2.x, x3 = L2P1.x, x4 = L2P2.x;
    float y1 = L1P1.y, y2 = L1P2.y, y3 = L2P1.y, y4 = L2P2.y;

    float bx = x2 - x1; 
    float by = y2 - y1; 
    float dx = x4 - x3; 
    float dy = y4 - y3;

    float b_dot_d_perp = bx * dy - by * dx;

    if(b_dot_d_perp == 0) {
        return NO;
    }

    float cx = x3 - x1;
    float cy = y3 - y1;
    float t = (cx * dy - cy * dx) / b_dot_d_perp;

    if(t < 0 || t > 1) {
        return NO;
    }

    float u = (cx * by - cy * bx) / b_dot_d_perp;

    if(u < 0 || u > 1) { 
        return NO;
    }

    return YES;
}

Вы можете использовать это так.

if (lineSegmentsIntersect(lineOnePointOne,lineOnePointTwo,lineTwoPointOne,lineTwoPointTwo)){
     //segments intersect
} else {
     //segments did not intersect
}

Вам решать создать двойной цикл для проверки правильных сегментов друг против друга.

person Metabble    schedule 15.11.2012
comment
Хорошо спасибо. Я попробую это, я учил это делать, но я хотел подождать, если у кого-то есть более простое решение. - person emillime; 15.11.2012
comment
Кстати, мне не нужно использовать путь Безье. Было бы проще, если бы я использовал что-то еще, например CGPath. Или я могу использовать что-то другое? - person emillime; 15.11.2012
comment
Я сохранил свой путь как свойство CGMutablePathRef. Я не использую UIBezierPath, если мне не нужны кривые и прочее. - person Metabble; 16.11.2012
comment
Привет, у меня еще не было времени проверить это. Я попробую сегодня, но что вы имеете в виду под стандартным тестом на пересечение линий? Как я могу это сделать, если у меня есть 4 точки (2 строки)? Спасибо за вашу помощь. - person emillime; 19.11.2012
comment
@fuskaren Извините, что так долго не мог ответить вам. Я обновил свой пост функцией, которую вы можете использовать. Просто передайте ему четыре точки, и он вернет BOOL, сообщающий вам, пересекаются ли линии. - person Metabble; 22.11.2012
comment
Спасибо за помощь. Код пересечения линий теперь работает. Но я не могу заставить его работать с моим путем. Это должно быть помечено как неправильное, но это не так. piclair.com/data/g0nwi.jpg Возможно, сегменты линий слишком малы, они не считаются пересекающимися? - person emillime; 22.11.2012
comment
Нвм, нашел проблему. В моем коде просто удалите оператор else, в котором я создаю строку 2, точки p3 и p4. Спасибо за помощь! - person emillime; 22.11.2012
comment
Кстати, код очень медленный, потому что иногда нужно проверить много строк. Сначала у меня это было в методе перемещения касаний, но это отставание, поэтому теперь я тестирую его только тогда, когда пользователь заканчивает касание. У вас нет решения, которое не нужно перебирать весь путь? - person emillime; 22.11.2012
comment
@fuskaren Не повторяйте каждый раз весь путь; просто проверьте, что изменилось. В touchesMoved каждый раз, когда пользователь перемещает палец, я добавляю точку. Затем я беру отрезок, образованный этой новой и последней точкой, и проверяю его на соответствие старым отрезкам. Таким образом, каждый раз, когда пользователь перемещает палец, я проверяю новый добавленный сегмент на все старые. Если бы вы прошли весь путь, потребовалось бы около 5000 проверок каждый раз, когда они двигали пальцем, если бы длина строки составляла 100 сегментов; только выполнение нового сегмента против старого уменьшит это до 99 проверок, когда их палец двигается. - person Metabble; 23.11.2012
comment
Спасибо, я сделал это и обнаружил, что задержка была вызвана наличием NSLog в цикле. Когда я удалил его, все работает гладко. - person emillime; 23.11.2012
comment
@fuskaren Отлично. Рад помочь. - person Metabble; 23.11.2012