Andy31
Andy31

Reputation: 123

Determine whether one polygon contains another

(For my purposes "polygons" do not included self-intersecting polygons or polygons with holes - just simple (concave or convex) polygons.)

I have found various suggestions for this problem, which are mainly based around the following:

If there are no intersections between the edges of Polygon1 and the edges of Polygon2, and at least one vertex of Polygon2 is "inside" Polygon1, then Polygon1 contains Polygon2.

(eg see the accepted answer here)

However, the devil is in the detail:

Selection of diagrams to illustrate the above. (NB diagrams A, B and C have vertices of Polygon2 on edges of Polygon1, diagrams D and E vice versa.)

I can't work out a condition to test which distinguishes correctly between these various cases. I'd be grateful for any pointers?

Upvotes: 8

Views: 2675

Answers (4)

Andy31
Andy31

Reputation: 123

Many thanks for the replies, especially to @Dukeling and @n.m.

I have implemented (in Python) the sweepline solution proposed by @n.m. and am posting it here in case anybody else finds it useful. (I found that one easier to code than Dukeling's solution.) In my use case, I know which will be the containing polygon (if there is one), so I don't need to test both ways round.

I have tested it with over twenty test cases, including all those in the diagram above, and their reflections in y=x. However, if anybody spots any edge case where the implementation doesn't work, or any improvements to the efficiency of the code, comments would be welcome.

Edit:
I have removed the code, as I found a number of cases for which it did not work. In the end I went for a more comprehensive solution which, given two polygons A and B determines whether A contains B, A is inside B, A and B overlap, or A and B are disjoint.

To speed things up, it starts by looking at bounding boxes, which eliminates some possibilities, then if the bounding boxes are equal it looks at areas, and only then goes on the the sweepline algorithm.

The code is quite long, so I am not including it here, but if anyone is interested, you can see it as the positionRelativeTo method of PolygonObject at https://github.com/andy31lewis/brySVG. This has been tested with a couple of hundred test cases and seems pretty reliable.

Upvotes: 0

n. m. could be an AI
n. m. could be an AI

Reputation: 120079

The sweepline algorithm (as nearly always) gives us the most robust and efficient solution.

In its simplest form, sweepline finds all line segment intersections. It is trivial to extend it to check polygon containment. You just keep track which line or point belong to each polygon. At any step of the algorithm, the intersection of the sweeping line and the interior of each polygon is a finite set of vertical segments. You have these cases:

  1. If there is a proper (i.e. not at an endpoint) edge intersection at any step, game over.
  2. Otherwise if at every step red and blue segments are disjoint, then the polygons are completely outside each other.
  3. Otherwise if at every step red segments are identical to blue segments (i.e. the red set and the blue set are the same), then the two polygons are the same.
  4. Otherwise if at every step each red segment is completely inside some blue segment, then the red polygon is inside the blue one.
  5. Otherwise if at every step each blue segment is completely inside some red segment, then the blue polygon is inside the red one.
  6. Otherwise polygon boundaries intersect.

This takes care of all edge cases. If you want to classify your cases A, E and F as "inside", test only intersection of segment interiors (i.e. segments (0,1) and (1,2) are not intersecting, and (0,1) is inside of (0,2)). Otherwise, just treat the above cases as proper intersections.

If at some step there are two edges that are collinear and parallel to the sweep line and they intersect, it could be a bit hard to decide. However all edge cases can be resolved by calculating sweepline-polygon intersection not at vertices (as is normal to the sweepline algorithm) but between vertices (say at a midpoint between the current vertex and the next). This way only polygon interiors (not boundaries) are ever considered.

In effect the algorithm breaks up our polygons into a bunch of little trapezoids, sandwiched between parallel (say vertical) lines drawn through each vertex. It's very easy to check whether these trapezoids are intersecting, or disjoint, or completely contain one another. An illustration can be found here.

Edit: clarified some wording. Edit 2: found an edge case ;)

Upvotes: 3

גלעד ברקן
גלעד ברקן

Reputation: 23945

We can traverse the edges in O(|EA| + |EB|) and play "catch:" while the current edge of one polygon is extending beyond the edge of another in at least one dimension, move along the next edge/s of the other polygon, then switch again. Assert containment, which we can tell by monitoring intersections and which side of the edge is inside its polygon.

Upvotes: 0

Bernhard Barker
Bernhard Barker

Reputation: 55639

If we're trying to check whether polygon B is inside polygon A:

Like mentioned in the answer you linked to, start off doing a line intersection test for each pair of edges, one from each polygon. If any edges intersect (excluding vertices that lie on edges and common vertices), B is not inside A.

If a vertex V of one polygon lies on an edge of the other, treat that edge instead as 2 edges, with the vertex V as a new vertex for that polygon.

Now we only have to think about common vertices.

For each common vertex V:

  • We can say V has edges EA1 and EA2 (from A) and EB1 and EB2 (from B).
  • Get the gradients of all 4 the edges.
  • Use this to determine which edges are adjacent.

  • If EB1 and EB2 are not adjacent, B is not within A.

  • If EB2 lies on A (that is, EB2 lies on EA2, i.e. they have an equal gradient), we don't yet know whether B is in A.

    In this case, we'll need to keep track of on which side EB1 was and move on to the adjacent vertex VB (the other vertex of EB2), and check whether EB3, the edge after EB2, is on the same side as EB1. If they're on the different sides, B is not inside A.

    If EB3 is also on A, we need to check EB4, and so on, until we find one that isn't.

    If both EB1 is on EA1 and EB2 is on EA2, we need to move in both directions to determine on which side we need to be. If all edges of B lie on A, A is equal to B.

    (Note: if, for example, EB2 lies on EA1 instead of EA2, you can simply rename them to fulfill the above condition)

All of this is assuming we don't allow polygons that intersect with themselves - allowing that will probably break this.

There may be some non-trivial details here, but this should be a decent starting point.

Upvotes: 3

Related Questions