5

I'm unable to come up with an algorithm to detect weakly simple polygons (i.e. polygons where the sides are allowed to touch but not cross over). Currently I am just checking for intersections between every side - here is the function I call for all non-adjacent sides. This way, only simple polygons are allowed (no touching at all). Polygons are vectors of points.

bool linesIntersect(const point &a1, const point &a2, const point &b1, const point &b2) {
    // Solve intersection of parametric lines using inverse matrix
    // The equation of the parametric lines are line1 = (a2 - a1)*s + a1
    // and line2 = (b2 - b1)*t + b1, where a1, a2, etc. are vectors.
    // Two equations can be produced from the two components, and these
    // this system of two equations can be solved for s and t
    // If the parameters s and t are between 0 and 1, it means that the lines intersect
    float x21 = a2.x - a1.x, x43 = b2.x - b1.x, x31 = b1.x - a1.x,
        y21 = a2.y - a1.y, y43 = b2.y - b1.y, y31 = b1.y - a1.y;
    float determinant = x43*y21 - x21*y43;
    if(determinant == 0.) return false;

    float s = float(x43*y31 - x31*y43)/determinant;
    float t = float(x21*y31 - x31*y21)/determinant; 

    if(s <= 1. && s >= 0. && t <= 1. && t >= 0.) return true; // lines intersect
    return false;
}

Using s < 1. && s > 0. && t < 1. && t > 0. does not work because it accepts some complex polygons as simple.

The first figure in this question shows a couple of examples. Below is a typical weakly simple polygon that the program would be dealing with.

A weakly simple polygon

I would prefer pseudocode since the math jargon in the aforementioned question (1) scares me and I don't think I have the knowledge to implement any complex algorithm. I am using Boost.Polygon for something else if there's something in there, but I didn't find anything.

EDIT:

Here is how I use the function. checkPts is a vector<point> with an assumed side from the last point to the first.

// Check for self-intersecting polygons
for(int i = 0; i < checkPts.size() - 2; ++i) {
    for(int j = i + 2; j < checkPts.size() - 2; ++j) {
        if(linesIntersect(checkPts[i], checkPts[i+1], checkPts[j], checkPts[j+1])) error("self-intersecting polygon");
    }
}
Community
  • 1
  • 1
user21760
  • 93
  • 5
  • 1
    Here's a neat calculation you can do to check whether sides intersect: http://stackoverflow.com/a/7069702/3516541. – Ben Jun 17 '14 at 19:43
  • I can't be sure if that's what essentially what you're doing. Your notation is a bit confusing at a cursory glance. – Ben Jun 17 '14 at 19:48
  • Your question boils down to comparing floating-point numbers for equality. Are you sure that what you're trying to do is actually worth doing? – Beta Jun 17 '14 at 19:55
  • OK I'll edit it, but I'm basically finding the intersection of two parametric lines. s and t are the parameters (i.e. line = direction_vector*s + position_vector), and they need to be between 0 and 1 for the lines to be intersecting. – user21760 Jun 17 '14 at 19:56
  • And I think determining whether two line segments intersect is the easy part. The tricky part is finding whether the whole polygon is a weakly simple polygon. I use the function for every non-adjacent side presently, but that doesn't do what I want - it determines if there is no touching in the polygon at all, except at vertices of coarse. – user21760 Jun 17 '14 at 19:58
  • simply checking for every pair to be non-intersecting should do it. Can you post THAT part? – Ben Jun 17 '14 at 20:14
  • @Ben I'm sorry, but I think that that answer only determines if the polygon is convex. I need to determine if a polygon is weakly simple or not. – user21760 Jun 17 '14 at 20:18
  • 1
    @Ben If you consider touching line segments to be acceptable (i.e. a vertex on top of a side, or two coincident sides are considered *non*-intersecting), then certain complex polygons are found incorrectly to be weakly simple. See the first figure in this question: http://cstheory.stackexchange.com/questions/11512/detecting-two-kinds-of-almost-simple-polygons. On the other hand, if line segments cannot touch at all, then the weakly simple polygons I am looking for are not accepted. In weakly simple polygons, sides are allowed to touch but not cross. – user21760 Jun 17 '14 at 20:35
  • I think we have to adjust our meaning of "intersect". If a point is ON a line, but does not cross over, does it intersect? If one segment is on top of another, are they intersecting? – Ben Jun 17 '14 at 20:37
  • @user21760, I have taken a look at the link. It would seem we must test for nonintersecting PATHS, not just segments. It's very interesting, I'll have to think on it. – Ben Jun 17 '14 at 20:46

1 Answers1

1

I am not sure I get it, because you seem to already have a solution. Just call lineIntersects on every pair of non-adjacent edges.

If two edges have no common points, then lineIntersects returns false, which is expected.

If two edges cross each other, lineIntersects returns true, and thus, you know that the polygon is not weakly simple.

If two edges touch, like in the picture, then the determinant that you compute in linesIntersects is 0 (i.e : the lines are parallel). lineIntersects returns false. Which is what you want (you allow edges to touch)

Of course, there is always the tricky part where operations on float leads to rounding errors, but for me, the math in your function is correct. (and should definitely work on the example in the picture)

Edit : A more "mathematical" approach. Two edges either have 0 points in common, 1 point in common, or an infinite number of points in common (they "touch"). Being weakly simple just means that for any two pair of edges, you are not allowed to have the "1 point in common" case. Thus, you need a function that finds out when you have exactly 1 point in common. My claim is that lineIntersects already does it

Edit 2 : I forgot to explain that having 1 point in common is not always a problem. But only if the common point between the two edges is at an endpoint of one of the two edges. Then we should "allow" it (return false). But this does not change my answer because in lineIntersects, we compute s < 1. && s > 0. && t < 1. && t > 0. and not s <= 1. && s >= 0. && t <= 1. && t >= 0.. So we already "allow" this case.

B. Decoster
  • 7,723
  • 1
  • 32
  • 52
  • 1
    One point in common is not a problem, if that is an endpoint of one of the segments. The polygon in the example has such a case, where a 'T' is formed. – Ben Voigt Jul 07 '14 at 20:01
  • You are right, I skipped a detail. As OP explained, he does `s < 1. && s > 0. && t < 1. && t > 0.`. This makes it such as 1 point in common is not a problem if it's an endpoint. So his function is still perfect for the job. – B. Decoster Jul 07 '14 at 20:03
  • 1
    @Fezvez [Here](http://imgur.com/wrGyHE6) is a case where `linesIntersect` would return false for every pair of non-intersecting edges (with `s < 1. && s > 0. && t < 1. && t > 0.`) even though the polygon is complex. This would be a "2 points in common" case. – user21760 Jul 14 '14 at 14:36
  • @user21760 : that is one fine counter example you gave me. It made me think that at the fundamental level, I don't know how to characterize what is a weakly simple polygon. In your example, the lines "cross" each other, but the definition of crossing is... not well defined for me. That's super interesting, I must look into it – B. Decoster Jul 14 '14 at 21:56
  • I don't know what I was thinking yesterday with 2 points in common since they are coincident. Anyhow, it makes the problem much more difficult, I think. – user21760 Jul 16 '14 at 12:41