Bezier path see if it crosses

北战南征 提交于 2019-12-08 03:08:16

问题


I have a code that lets the user draw a shape, I'm using UIBezierPath for this. But I need to see if the shape crosses itself, for example like this: http://upload.wikimedia.org/wikipedia/commons/0/0f/Complex_polygon.svg Then it's not a a valid shape. How can I find this?

Edit: I still haven't solved this. I save all the points between the lines in the path in a array. And then I loop through the array and try to find if any lines intersects. But it does not work, sometimes it says that there is an intersection when it isn't.

I think that the problem is somewhere in this method.

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

This is the code I found to see if two lines intersects, It's in C but I should work.

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

回答1:


It depends on how complex the polygon drawn by the user can be and the number of points in the path. Ideally, there would be a point for all the vertices in the shape and nothing more. Get a CGPath from the UIBezierPath and use GCPathApply to hand the elements to a function, which adds each point to an array. Traverse the array with two for loops, one nested in the other, which checks each line segment against every line segment after it using a standard line-line intersection test. As soon as an intersection has been found, break from the loop. Or, if this were a convenience method, return a BOOL. That's the simplest way.

EDIT: Here's an example of a line-line intersection function which returns a BOOL telling you whether or not two segments cross. Pass in the two points that create the first segment followed by the two points that make the second segment. It was hastily modified from a piece of source code I found online quickly, but it works.

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

You can use it like this.

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

It's up to you to create the double loop to check the correct segments against one another.



来源:https://stackoverflow.com/questions/13394422/bezier-path-see-if-it-crosses

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!