Reputation: 123
(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:
Does "inside" Polygon1 include "on an edge of" Polygon1? Clearly it must, otherwise in diagram F (see the image linked below) Polygon2 (red) would have no vertex "inside" Polygon1 (blue) and so fail the above test, when it should pass.
Does an "intersection" of two edges include a point at the end of one of the edges (ie a vertex)? If "yes", then diagrams A and E below have intersections and so fail the test when they should pass. But if "no", then diagrams B, C and D have no intersections and so pass the test when they should fail.
(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
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
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:
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
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:
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