Given a polygon that may be concave and may have holes, how can I get the largest simple convex polygon composed of a subset of its vertices?
ie, given the simple concave polygon:
p = Polygon([(30, 2.01), (31.91, 0.62), (31.18, -1.63), (28.82, -1.63), (28.09, 0.62), (30, -0.001970703138552843)])
I want the largest simple convex polygon (perhaps the same but without the leftmost point (28.09, 0.62) and replace (28.82, -1.63) with (30, -1.63)). Like this:
This is just an unmeasured example. It may be that in fact both (28.09, 0.62) and (30, 2.01) must be removed, if this produces a larger area, such as might result from the cut indicated by the green line here:
But, assuming the first cut was correct, If we added a hole to the "other side":
p = Polygon([(30, 2.01), (31.91, 0.62), (31.18, -1.63), (28.82, -1.63), (28.09, 0.62), (30, -0.001970703138552843)],
[[(30.1,0.62), (30.1,1.25), (31, 1.25), (31,0.62)]])
the largest simple convex polygon might in such cases rotate to the other half of the polygon, so instead of dropping the previous point, it would drop (30, 2.01) and replace (31.91, 0.62) with a point between that and (31, -1.63). Obviously, in this case it would throw out all of the vertices of the hole.
commentary
Any hole that would be left intact inside the polygon would introduce a concave angle to the polygon by definition. in the case that there is a hole in the input polygon, at most one edge from it can remain in the output polygon (and, by definition of "simple polygon", that edge would be a member of the exterior coordinates).
There's a little bit of sloppiness in this definition so I should try to be more clear. All interior and exterior vertices are members of the set of possible points in the output simple polygon. So are all points that intersect the interior and exterior bounds (so the line segments between them). The selection of points should result in a simple, convex polygon that is inscribed in the source polygon. In the case that the source polygon is a simple, convex polygon, it should return the same polygon as output. It is quite possible to have whole families of candidate solutions with equal area. If they are maximal, any one of them will do.
Sketch approach: if you throw out cuts like in the sample with the green line, then all that remains are removal of points with projections from segments. So you could count all interior and exterior points as a set, and exclude subsets of 0 or more of them, then find the largest convex polygon. So, either just exclude the point or when excluding a point produces a new concave angle, project from the line segment on that side of the angle to the line segment on the other side of the polygon (this is the approach used to produce the first sample solution image). Revisiting the green line cuts, these are lines that bisect the polygon and tangent the center point of the concave angle. If this bisection must run perpendicular to the line from the center point to the centroid of the remaining polygon, then this is not much more complex. But I'm not sure that that is true. And in any case, that is a lot of polygons to consider.
note: at first I marked a duplicate, thinking this is essentially a more complicated version of another question (Finding largest subset of points forming a convex polygon but with holes). However, this approach does not allow for addition of new vertices in the solution. For example, Delaunay triangulation of the first shape in this article produces no new points:
[ 'POLYGON ((28.09 0.62, 28.82 -1.63, 30 -0.001970703138552843, 28.09 0.62))',
'POLYGON ((28.09 0.62, 30 -0.001970703138552843, 30 2.01, 28.09 0.62))',
'POLYGON ((30 2.01, 30 -0.001970703138552843, 31.91 0.62, 30 2.01))',
'POLYGON ((31.91 0.62, 30 -0.001970703138552843, 31.18 -1.63, 31.91 0.62))',
'POLYGON ((28.82 -1.63, 31.18 -1.63, 30 -0.001970703138552843, 28.82 -1.63))']
The article provided as possible duplicate only counts subsets of the points to find the maximal convex hull -- ie, it does not introduce points on the line.
I am not really sure your problem is well specified (or rather, they way you describe it it is reduced to a well-known, simpler problem).
First, let me introduce you the idea of the Convex Hull:
Given a list of points, the convex hull is the smallest convex (simple) polygon that contains all points.
The shape of the CH is essentially what you would get if you were to "place a rubber band" around the points so that it touches the outer ones.
Now, there is a straightforward property of the CH:
Given a set of points, the are of their CH is larger (or equal) than the area of any other (simple) polygon those may form.
This is true because
i) If they form a convex polygon, then they form the CH by definition.
ii) otherwise, they form some non convex shape. Visually, you can get from the CH to that non convex shape by "removing triangles" comprised of 2 points on the CH and one inner point. So you are removing area, so the CH has the largest area.
So, the largest convex polygon comprise of all the vertices is the CH.
Now, about selecting a subset of the original vertices: This will obviously give you a smaller (or equal) -sized shape. So there is no point in selecting any subset, really.
Also, holes don't really impact this argument. Keeping the whole is obviously to your benefit, since you can add the area around the hole.
So, the final answer (unless I missed something), is that all you need is the Convex Hull.
Fortunately, there are some good python libraries for computing, plotting and messing around with convex hulls.
Related
the Wikipedia article about Delaunay Triangulations in d dimensions states as a prerequisite for uniqueness of a triangulation:
It is known that there exists a unique Delaunay triangulation for P if P is a set of points in general position; that is, the affine hull of P is d-dimensional and no set of d + 2 points in P lie on the boundary of a ball whose interior does not intersect P.
Now that I've written my own Delaunay library, I want to be able to verify the uniqueness of the triangulation given its points. Checking the dimensionality of the affine hull can be done easily by calculating the rank of the set. The second part however is way more difficult.
How can I check if d+2 points lie on the boundary of a ball not intersecting with the set and without some gigantic loop over each point? Or is there maybe an alternative way of checking the uniqueness?
I'm using Python with Numpy, however this is more of an theoretical question, thus the language doesn't matter.
Thanks!
For each a simplex of maximal dimension of your triangulation, with d+1 points, you have d+1 neighbor simplices. Two neighbor simplices share d points (forming a simplex themselves). As I do not know if you are familiar with that working in dimension d, let's have a look at dimensions 2 and 3...
In dimension 2, a simplex of maximal dimension is a triangle (simplex of dimension 2), and two neighbor triangles share an edge (simplex of dimension 1).
In dimension 3, a simplex of maximal dimension is a tetrahedron (simplex of dimension 3), and two neighbor tetrahedra share an a face that is a triangle (simplex of dimension 2).
Anyway, given a Delaunay triangulation in dimension d, if you want to check if you have a unique triangulation, iterate on simplices of dimension d-1 (triangles in 3D, or edges in 2D), and take two incident simplices (two neighbor tetrahedra in 3D, or neighbor triangles in 2D). That gives you d+2 points. As the triangulation is Delaunay, you know that the in_sphere predicate for those d+2 points has a result positive or null. If, during the iteration on simplices of dimension d, the result of all the in_sphere predicate for the d+2 points is strictly positive, then your triangulation is unique, otherwise it is degenerated and non-unique.
Have a look at the paper Efficient Exact Geometric Predicates for Delaunay Triangulations by Olivier Devillers and Sylvain Pion: the introduction gives the basics of the theory about Delaunay triangulations, and the in_sphere predicate. It also gives leads to implement the orientation and in_sphere predicates in an exact but efficient way. That might help you in your implementation of the Delaunay triangulations.
You have already a Delaunay triangulation i.e., there are no points inside a circumsphere. Now, e.g., for the 2D case, simply check if you can flip any edge such that it is still a Delaunay triangulation. If not, you have a unique solution. Be aware of the rounding errors of double precision arithmetic.
Sorry if the title doesn't make it clear.
Here is the more detailed situation.
Given n dots and n rectangles.
Rectangles can overlap.
Dots are represented as (x,y)
Rectangles are represented as (x,y,w,h)
x,y refer to location in x and y axes, respectively
w,h refer to width and height, respectively
How do i check if the following two conditions are met simultaneously:
each dot falls in a certain rectangle (doesn't matter which)
AND
each rectangle contains at least one dot.
Is there a better way instead of iterating through each dot and each rectangle?
It would be best if you can show me how to do this in python.
Thanks!
I think you can use what is called oriented surfaces created by the mathematician Gauss i believe. this allows you to calculate any polygon area. Using the point to test as a fifth point and one other rectangle point as sixth point (duplicate) you can calculate a new area for this new six-side polygon. You will obtain the same area or a bigger area depending on the point position compared to the rectangle.
Addendum
The oriented surfaces allows you to calculate the area of any polygon when knowing their coordinates. The polygon must be defined as a set of points P(Xp,Yp) in the specific order describing the contour. Two consecutive points will be connected by a line.
In the picture below the polygon can be defined as the set [A,B,C,D], but also as [C,D,A,B] or [B,A,D,C].
It cannot be defined as [A,C,B,D] since this would define a polygon shaped like a butterfly wings as shown below.
Oriented Surfaces
For each couple of ordered successive point - meaning [A,B], [B,C], [C,D], [D,A] for the defined set [A,B,C,D] for example - the formula allows us to calculate the area of the triangle formed by a couple and the axis origin. This surface is oriented - meaning it has a positive or a negative value - according to the rotation (clockwise or counter-clockwise). In the figure below the triangles (OAB) and (OBC) and (ODA) will have a negative area, while the triangle (OCD) will have a positive area. By adding all those area, one can notice that the result will be the area of polygon (A,B,C,D), which is negative because it is drawn clockwise.
Calculations
You can find a clear example of the calculations and try a few things here: https://www.mathopenref.com/coordpolygonarea.html. To complete my example I have drawn a polygon similar (but not identical) to the ones above on this website and the result is as follow: -22
Adding a point
When you add a point, which is the point you want to test, you will obtain a 5-point polygon. The first thing you have to do is to place it in the correct order so that you don't have segments crossing. To do that you can create loop where the new point P is placed successively at the different positions in the set - meaning (PABCD), then (APBCD), etc until (ABCDP)- and calculate for each the area. The set giving you the maximum area in absolute value is the one you keep.
Here is an example from the website https://rechneronline.de/pi/simple-polygon.php. The first polygon is the initial, the second is badly defined and the last one is correctly defined.
One can see that that if the added point is outside the original polygon then the area is increased. At the opposite, if the added point is inside the original polygon, the area is decreased:
Note
If the original point set is not ordered correctly, you will have to reorder it as described just above
In Python you will have to use ordered object such as a list
To check that each rectangle has at least a point inside, you will have to check each point against all rectangles and maintain a dictionary describing which point is inside which rectangle
Adding: I also realized that since a rectangle is convex it is possible to know whether a point P is inside by just checking the four oriented triangle area in order namely (ABP) (BCP) (CDP) and (DAP). If those four area have the same sign then P is inside the rectangle (ABCD), otherwise it is outside.
I need to join two convex, non-intersecting polygons into one joined covex polygon in way of minimisation of resulting area, like in picture below: I'm seeking for an alhorithm doing this. I also would be appreciate if someone provide me with corresponding python implementation.
If there are two non-intersecting polygons having say, m and n vertices respectively, then your problem can be thought of in this way:
Finding the convex polygon of the least area containing all of the m+n points. Having said this, check out the QuickHull Algorithm here: http://www.geeksforgeeks.org/quickhull-algorithm-convex-hull/
Additionally you can also check out these algorithms.
Jarvis's Algorithm: http://www.geeksforgeeks.org/convex-hull-set-1-jarviss-algorithm-or-wrapping/
And, Graham's Scan: http://www.geeksforgeeks.org/convex-hull-set-2-graham-scan/
Hope this helps.
P.S. I think you can find the python implementations of these algorithms anywhere on the internet. :)
For an efficient solution, you can adapt the Monotone Chain method (https://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain) as follows:
for both polygons, find the leftmost and rightmost sites (in case of ties, use the highest/lowest respectively);
these sites split the polygons in two chains, that are ordered on X;
merge the two upper and two lower chains with comparisons on X (this is a pass of mergesort);
reject the reflex sites from the upper and lower chains, using the same procedure as in the monotone chain method (a variant of Graham's walk).
The total running time will be governed by
n + m comparisons to find the extreme sites;
n + m comparisons for the merge;
n + m + 2 h LeftOf tests (signed area; h is the number vertices of the result).
Thus the complexity is O(n + m), which is not optimal but quite probably good enough for your purposes (a more sophisticated O(Log(n + m) solution is possible when the polygons do not overlap, but not worth the fuss for small polygon sizes).
In the example, the result of the merges are just the concatenation of the chains, but more complex cases can arise.
Final remark: if you keep all polygons as the concatenation of two monotone chains, you can spare the first step of the above procedure.
Finding the convex hull of both sets would work but the following approach is probably faster as it only needs to visit the polygons vertices in order:
Given polygons P and Q, pick from every one a vertex p1 and q1.
Search in Q the vertex q2 contiguous to q1 such that the rotation from p1-q1 to p1-q2 is clockwise (this can be checked easyly using vector cross product).
Repeat until you reach a point qk whose two contiguous vertices in Q generate and anticlockwise rotation.
Now, invert the process traveling from p1 across contigous vertices in P such that the rotation is anticlockwise until an extreme pl is found again.
Repeat from 2 until no more advance is possible. You have now two points pm and pn which are two the vertices where one side of the red area meets the black polygons in your drawing above.
Now repeat the algorithm again but changing the directions, from clockwise to anti-clockwise and vice-versa in order to find the vertices for the other side of the red area.
The only remaining work is generating the final polygon from the two red area sides already found and the segments from the polygons.
I have plotted n random points (the black points) and used delaunay triangulation, now I want to interpolate m random evaluation points (the red points) so I need to calculate which triangle the evaluation point is inside.
What is the approach for calculating the vertices of the triangle for each point?
For a given triangle, ABC, a point is inside the triangle if it is on the same side of line AB as point C is, on the same side of line BC as point A is, and on the same side of line AC as point B is. You can pre-optimize this check for each triangle and check them all until you find the triangle(s) it is in. See this page for more details.
To save computation, you can compute the minimum and maximum X and Y coordinates of the points for each triangle. If the X and Y coordinates of a point are not within the minimum and maximum values, you can immediately skip checking that triangle. The point cannot be inside it if it isn't inside the rectangle that bounds the triangle.
I'll assume that triangles do not intersect except of common edges.
You don't want to check every triangle (or subset of them) independently. The main reason is computation errors - due to them you may get answer "inside" for more than one triangle (or zero) which may break logic of your program.
More robust way is:
Find closest edge to the point
Select one of triangles touching this edge
Make one check for that triangle (the point lies on the same side as the third triangle vertex)
If "inside" - return this triangle
If "outside" - return another triangle on this edge (or "nothing" if there is no other triangle)
Even if you will return wrong triangle because of computation error, there still be exactly one triangle and point will lie close enough to it to accept such mistakes.
For #1 you can use something like quad-tree as Michael Wild suggests.
This simple example triangulates 10 random points, a further 3 random points are generated and if they fall inside a triangle the vertices are given:
import numpy as np
from pyhull.delaunay import DelaunayTri
def sign(a,b,c):
return (a[0]-c[0])*(b[1]-c[1])-(b[0]-c[0])*(a[1]-c[1])
def findfacet(p,simplice):
c,b,a = simplice.coords
b1 = sign(p,a,b) < 0.0
b2 = sign(p,b,c) < 0.0
b3 = sign(p,c,a) < 0.0
return b1 == b2 == b3
data = np.random.randn(10, 2)
dtri = DelaunayTri(data)
interpolate = np.random.randn(3, 2)
for point in interpolate:
for triangle in dtri.simplices:
if findfacet(point,triangle):
print "Point",point,"inside",triangle.coords
break
Using matplotlib to visualize (code omitted):
The dotted cyan lines now connect the points to interpolate with the vertices of triangle it lays within. The black lines are the convex hull, and the solid cyan lines are the delaunay triangulation.
A Delaunay triangulation is in itself a search data structure. Your Delaunay triangulation implementation probably has location functions. How have you computed the Delaunay triangulation of your points?
CGAL has an implementation of 2D and 3D triangulations. The resulting data structure is able to localize any point using a walk from a given point. See for example that chapter of the manual. CGAL is a C++ library, but it has python bindings.
In Python, how would one find all integer points common to two circles?
For example, imagine a Venn diagram-like intersection of two (equally sized) circles, with center-points (x1,y1) and (x2,y2) and radii r1=r2. Additionally, we already know the two points of intersection of the circles are (xi1,yi1) and (xi2,yi2).
How would one generate a list of all points (x,y) contained in both circles in an efficient manner? That is, it would be simple to draw a box containing the intersections and iterate through it, checking if a given point is within both circles, but is there a better way?
Keep in mind that there are four cases here.
Neither circle intersects, meaning the "common area" is empty.
One circle resides entirely within the other, meaning the "common area" is the smaller/interior circle. Also note that a degenerate case of this is if they are the same concentric circle, which would have to be the case given the criteria that they are equal-diameter circles that you specified.
The two circles touch at one intersection point.
The "general" case where there are going to be two intersection points. From there, you have two arcs that define the enclosed area. In that case, the box-drawing method could work for now, I'm not sure there's a more efficient method for determining what is contained by the intersection. Do note, however, if you're just interested in the area, there is a formula for that.
You may also want to look into the various clipping algorithms used in graphics development. I have used clipping algorithms to solve alot of problems similar to what you are asking here.
If the locations and radii of your circles can vary with a granularity less than your grid, then you'll be checking a bunch of points anyway.
You can minimize the number of points you check by defining the search area appropriately. It has a width equal to the distance between the points of intersection, and a height equal to
r1 + r2 - D
with D being the separation of the two centers. Note that this rectangle in general is not aligned with the X and Y axes. (This also gives you a test as to whether the two circles intersect!)
Actually, you'd only need to check half of these points. If the radii are the same, you'd only need to check a quarter of them. The symmetry of the problem helps you there.
You're almost there.
Iterating over the points in the box should be fairly good, but you can do better if for the second coordinate you iterate directly between the limits.
Say you iterate along the x axis first, then for the y axis, instead of iterating between bounding box coords figure out where each circle intersects the x line, more specifically you are interested in the y coordinate of the intersection points, and iterate between those (pay attention to rounding)
When you do this, because you already know you are inside the circles you can skip the checks entirely.
If you have a lot of points then you skip a lot of checks and you might get some performance improvements.
As an additional improvement you can pick the x axis or the y axis to minimize the number of times you need to compute intersection points.
So you want to find the lattice points that are inside both circles?
The method you suggested of drawing a box and iterating through all the points in the box seems the simplest to me. It will probably be efficient, as long as the number of points in the box is comparable to the number of points in the intersection.
And even if it isn't as efficient as possible, you shouldn't try to optimize it until you have a good reason to believe it's a real bottleneck.
I assume by "all points" you mean "all pixels". Suppose your display is NX by NY pixels. Have two arrays
int x0[NY], x1[NY]; initially full of -1.
The intersection is lozenge-shaped, between two curves.
Iterate x,y values along each curve. At each y value (that is, where the curve crosses y + 0.5), store the x value in the array. If x0[y] is -1, store it in x0, else store it in x1.
Also keep track of the lowest and highest values of y.
When you are done, just iterate over the y values, and at each y, iterate over the x values between x0 and x1, that is, for (ix = x0[iy]; ix < x1[iy]; ix++) (or the reverse).
It's important to understand that pixels are not the points where x and y are integers. Rather pixels are the little squares between the grid lines. This will prevent you from having edge-case problems.