What algorithm can be used to detect gaps between polygons? - algorithm

What algorithms are used to find gaps between adjacent polygons (example pictures show 2 adjacent polygons and a shaded 'gap' between them), and is there a common name for this type of operation? Polygons in my input may have coincident vertices, segments, both, or neither. Polygons are represented as ordered lists of points. Adjacent polygons are defined as having at least one coincident point or segment.
I am developing in Go (and have access to the GEOS library), but any references to algorithm steps or implementations in common languages would be helpful.

This might not be what you were looking for, but something like this might get the job done.
Suppose you can calculate a list of all points of intersection p1, p2, …, pk between the perimeters of the two polygons. Let v1, v2, …, vn be the vertices of the first polygon, and w1, w2, …, wm be the vertices of the second polygon.
First, create two ordered collections c1 and c2, where c1 contains p1, p2, …, pk and v1, v2, …, vn, in order (so that if going around the perimeter of the polygon in the clockwise direction, the vertices appear in the list in the order visited), and c2 contains p1, p2, … pk and w1, v2, …, vm ordered in the same way.
Now, between every two adjacent p(i%k) and p((i+1)%k) there is some overlap or gap. This overlap or gap may be degenerate iff the vertices appearing in c1 and c2 between these two points of intersection are the same; in this case, the polygon traced out has area zero and may be discarded. Otherwise, we must see whether the vertices in c1 and c2 appearing between the points of intersection define a gap or an overlap.
If we have an easy/cheap way of testing whether a point is contained within the original polygon, simply choose a point in the space (e.g., the center of the triangle formed by one of the points of intersection and each of the adjacent points in c1 and c2 is guaranteed to be inside the space) and see whether the point is included in either c1 or c2 (it cannot be in one and not the other; why?). If the point is included, then you have an overlap; otherwise, you have a gap.
Indeed, we do have an easy way. If we are going around the first polygon in clockwise order, then if the point identified above (the middle of the triangle so described) is to the right of the line segment formed by the point of intersection and the vertex in c1 adjacent to it, then it's an overlap; otherwise, it's a gap. Alternatively, you can go clockwise around the points in c2 and use the same rule to tell.
To see whether a point is to the left or right of a vector:
take the vector (e.g., the point of intersection to the adjacent point in either c1 or c2)
take the vector to the candidate point (e.g., the center of the triangle described earlier)
Compute the 3-dimensional cross product
The sign of the z-coordinate of the resulting vector gives the answer.
In this example:
p1, p2, p3 ~ (3.1, 5.5), (3.3, 4), (3.8, 2)
v1, v2, v3, v4, v5 ~ (1, 0), (1, 8), (4, 4.5), (2, 3), (3.8, 2)
w1, w2, w3, w4 ~ (4, 1), (3, 5), (4, 9), (9, 5)
c1 ~ (v1, v2, p1, v3, p2, v4, p3=v5)
c2 ~ (w1, p3, p2, w2, p1, w3, w4)
pairs of points of intersection adjacent in c1:
x1 = (p1, p2), x2 = (p2, p3), x3 = (p3, p1)
pairs of points of intersection adjacent in c2:
y1 = (p3, p2), y2 = (p2, p1), y3 = (p1, p3)
triangle for x1 has vertices (p1, v3, w2), middle is
~ ((3.1+4+3)/3, (5.5+4.5+5)/3) = (3.3, 5)
vector from p1 to v3 ~ (0.9, -1)
vector from p1 to middle of triangle ~ (0.2, -0.5)
cross product of p1-v3 x middle of triangle vector:
+0.9 -1.0 +0.0
+0.2 -0.5 +0.0
i j k
=> -0.45k
this has a negative sign, so this is an overlap
triangle for x2 has vertices (p2, v4, p3=v5), middle is
~ ((3.3+2+3.8)/3, (4+3+2)/3) = (3, 3)
vector from p2 to v4: (-1.3, -1)
vector from p2 to middle of triangle: (-0.3, -1)
cross product of p2-v4 and middle of triangle vector:
-1.3 -1.0 +0.0
-0.3 -1.0 +0.0
i j k
=> 1.3k
this has a positive sign, so this must be a gap

Related

how do I determine a cross shape with given vertices

I am having trouble forming an algorithm to determine if 12 vertices that was inputted by a user in any order, will form a cross shape in a 2D plane.
From the way I looked at it, it can be two rectangle intersecting.
Should I choose to brute force it by comparing the distances,
I will end up having 67 distances from the 12 vertices, which to compare all of them would not be feasible.
Is there any characteristic of a cross or shape that I could use?
What you want is a cross shape define by two intersecting rectilinear rectangles with protrusions greater than zero on all four sides. I believe the following algorithm will fully determine that for you.
Insure that none of the 12 points is identical.
There should only be 4 distinct X values among the 12 points. Put them in ascending order into an array that we’ll call your X-vector.
Do the same with Y values creating a Y-vector with 4 distinct values.
Make a 4x4 array, initializing all cells to zero.
Go through each of the 12 values using their X and Y values along with the X-vector and Y-vector to select a cell in the 4x4 array to increment. Thus if you had a point (12,9) and 12 was at the [0] entry of the X-vector and 9 was in the [2]entry of the Y-vector, you would increment the [0,2] cell of the 4x4 array.
Now your 4x4 Array should look exactly like this:
0, 1, 1, 0
1, 1, 1, 1
1, 1, 1, 1
0, 1, 1, 0
If so, then it is a cross as you have defined it. And if not, or if it fails at any previous step, the it is not a cross.
Something like the following should work:
collect the points in a set or list
iterate the points and find the distinct X and Y coordinates; there should be exactly four different values for X and Y, respectively; if there are more or fewer, it's not a cross-shape
sort the distinct X and Y coordinates and call them x1 through x4 and y1 through y4
check whether the original list of points contains exactly the points (x1, y2), (x1, y3), (x2, y1), (x2, y2), (x2, y3), (x2, y4), (x3, y1), (x3, y2), (x3, y3), (x3, y4), (x4, y2), and (x4, y3), in any order
if there are other properties to be met, e.g. the four arms having same lengths, check those, too, using the identified distinct X and Y values

Relative positions of four points in plane

There are two different relative positions for four points in the plane:
In position 1, the four points can form a convex quadrilateral, (which is their convex hull), and in position 2, they can't (and their convex hull is a triangle). My question is: How can I write an algorithm to find out if the points are in position 1 or 2? (I know the coordinates of all four points).
For any triple of points P, Q and R in the plane, (not collinear), you can determine whether the angle P-Q-R makes a counterclockwise or a clockwise turn by looking at the sign of the quantity:
(P[0] - R[0]) * (Q[1] - R[1]) - (P[1] - R[1]) * (Q[0] - R[0])
where P[0] and P[1] refer to the x- and y-coordinates of P respectively, and similarly for Q and R.
Now call your four points P1, P2, P3 and P4, and compute these signs for each of the four triples (P1, P2, P3), (P1, P2, P4), (P1, P3, P4) and (P2, P3, P4) (be careful here: the order of the points (P, Q, R) in the expression above matters). If all signs are equal, or there are two positive and two negative signs, the convex hull is a quadrilateral. If there are three positive and one negative sign (or the other way around), the convex hull of your four points is a triangle. Or put more simply, if your signs are represented as +1 and -1, multiply the four signs together. If the product is +1, you're in the quadrilateral case; if -1, you're in the triangular case.
The above assumes that no three of the four points are collinear; I leave it to you to enumerate the degenerate cases.
And since this is StackOverflow, here's some code (in Python). First the definition of ccw (making use of a sign helper function).
def sign(x):
""" Return the sign of a finite number x. """
if x > 0:
return 1
elif x < 0:
return -1
else:
return 0
def ccw(P, Q, R):
""" Return 1 if P-Q-R is a counterclockwise turn, -1 for clockwise,
and 0 if the points are collinear (or not all distinct). """
disc = (P[0] - R[0]) * (Q[1] - R[1]) - (P[1] - R[1]) * (Q[0] - R[0])
return sign(disc)
Then the classification of a quadruple of points.
def classify_points(P, Q, R, S):
""" Return 1 if the convex hull of P, Q, R and S is a quadrilateral,
-1 if a triangle, and 0 if any three of P, Q, R and S are
collinear (or if not all points are distinct). """
return ccw(P, Q, R) * ccw(P, Q, S) * ccw(P, R, S) * ccw(Q, R, S)
A simple test: a square should be classified with the result 1.
>>> # Test case 1: quadrilateral convex hull
>>> P = 0, 0
>>> Q = 0, 1
>>> R = 1, 0
>>> S = 1, 1
>>> classify_points(P, Q, R, S)
1
And a triangle with result -1.
>>> # Test case 2: triangle.
>>> P = 0, 0
>>> Q = 0, 3
>>> R = 3, 0
>>> S = 1, 1
>>> classify_points(P, Q, R, S)
-1
And here's a degenerate case (P, Q and S are collinear):
>>> P = 1, 1
>>> Q = 2, 2
>>> R = 5, 7
>>> S = 4, 4
>>> classify_points(P, Q, R, S)
0
Note that if you're using inexact floating-point arithmetic, numerical errors could result in a near-degenerate case being classified as degenerate, or vice versa.
To justify the above: it's easy to check that swapping any two inputs in the ccw definition reverses the sign of the result, and that swapping any two inputs in the classify_points definition leaves the sign of the product unaltered. So we can reorder the points arbitrarily with affecting the classify_points result.
Now suppose that P1, P2, P3 and P4 have a quadrilateral convex hull. Then by the above observation, we can reorder the points to assume that P1, P2, P3 and P4 go around the boundary of that quadrilateral in counterclockwise order. Then each of the ccw expressions is 1, and so the result of classify_points is 1. Similarly, if P1, P2, P3 and P4 have triangular convex hull, we can rearrange so that P1, P2 and P3 go counterclockwise around the triangle boundary and P4 is within the triangle, and in that case the ccw signs are 1, 1, -1 and 1.

Shape algorithm with optimization

I have a shape defined by straight line segments.
I want to simplify the shape to be constructed with straight lines but only with a finite set of slopes.
I want to minimize the amount of segments used and minimize the difference in area from the shape before and after.
I want to minimize these two things simultaneously with a user defined weight emphasizing minimizing one more than another.
minimize { J = w1(number of segments/length) + w2(difference area/length) }
Where w1 and w2 are both weights and length is the length of the new segment. I want an algorithm that does this. Any ideas?
Below I show a few pictures of how I might want it to work. Is there anything out in the literature that might help in writing an algorithm. Thanks!
This seems like a very tough problem! I would approach it by first defining two routines:
diffArea(fig, target) computes the difference area between fig and target
decomp(fig, p1, p2, s1, s2) computes the two figures that can be built by replacing all the segments between p1 and p2 with a pair of segments of shapes s1 and s2. For instance, if there were four segments between points p1 and p2 in fig, then decomp(fig, p1, p2, s1, s2) returns the two figures that are generated by replacing those four segments with appropriately scaled versions of s1 and s2. There's only one way to scale s1 and s2 to fill the space between p1 and p2 (because we're in 2-d space), and the two figures come from either ordering them s1 -> s2 or s2 -> s1.
Given these two routines, I think an iterated local search procedure might work well. You would have the following steps:
Set fig to a large bounding shape around target
For every pair of vertices (p1, p2) in fig (starting with pairs with 1 segment between, then 2 segment between, ...) and for every pair (s1, s2) of shapes:
Compute fig1 and fig2 using decomp(fig, p1, p2, s1, s2)
Let e_fig be the number of edges in fig and e_new by the number of edges in fig1 and fig2
If w1 * e_new + w2 * diffArea(fig1, target) < w1 * e_fig + w2 * diffArea(fig, target), replace fig with fig1
If w1 * e_new + w2 * diffArea(fig2, target) < w1 * e_fig + w2 * diffArea(fig, target), replace fig with fig2
Repeat this procedure until you've tested every pair of vertices and have found no improving replacements. This is obviously not going to give you an optimal solution, but I'd bet it will perform pretty well.
Well, in this case Pareto efficiency seems to be a good weight of a solution.
At first glance it seems that using a discrete optimization would be apropriate.
Choice of a particular algorithm depends on complexity of the figures to be shaped.
For large and complex figures I would suggest using genetic algorithm.

Shift trace of polygon by given vector

I need algorithm to get shift trace of given polygon by given 2d-vector.
Given valid polygon with no holes, but possibly concave.
Operation performs on plane, so the result might be a polygon, possibly with holes.
If it simplifies the task, outer polygon is enough.
It looks simple to describe, but I find it complex to realize, so I look for some ready solutions, preferably in c#.
Suppose you have a polygon P given by the points A1, A2, ... , An.
Now you decide to shift it by X on the x-axis and Y on the y-axis.
You can do this to each point individually to get the ending location of the polygon.
Let's call the shifted polygon Q givng by points B1, B2, ... , Bn.
Then all you need to do is draw the following parallelograms:
(A1 A2 B2 B1), (A2 A3 B3 B2), (A3 A4 B4 B3), ... , (An-1 An Bn Bn-1) , (An A1 B1 Bn)
At this point you will have filled in the shape you want.
Some of the parallelograms will overlap, but that is okay, since you are just filling them all in with the same red color.
By doing it this way, you also get your second example to turn out correctly, (the bottom right corner of the hole in the middle should be diagonal, because of the lip sliding into position).

Tetrahedron orientation for triangle meshes

I have 2 triangles and vertices p0, p1, p2, p3. These two triangle share an edge. From these two triangle I want to make a tetrahedron given by the 4 vertices. The library which I work with requires that "the 4 vertices should be given such that the four vertex triples defining the tetrahedron faces in the drawing appear in counter-clockwise order when being viewed at from the outside" . Assuming one of the two triangles is p0, p1, p2 I calculate the normal as being (p1-p0) (cross) (p2-p0). Can someone please tell me a way to ensure that this condition is met ?
Short answer:
The condition is that p3 must be on the correct side of the plane determined by (p0, p1, p2).
So, after computing the normal for this plane, you need to determine if the vector from (say) p0 to p3 is pointing in the same direction of the normal, or the opposite direction, by taking the dot product dot(normal, p3-p0).
More mathematically speaking:
You need to find the determinant of the 4x4 matrix formed by the homogeneous coordinates of the four points. The sign of the determinant determines if the condition is met; the appropriate sign depends on the exact conventions used, but ideally it should be positive:
require:
0 < det(p0, p1, p2, p3)
== det [ p0.x p0.y p0.z 1 ]
[ p1.x p1.y p1.z 1 ]
[ p2.x p2.y p2.z 1 ]
[ p3.x p3.y p3.z 1 ]
If a particular ordered set of points has a negative determinant, you can fix it by swapping any two of the points (which will negate the determinant):
e.g., swapping p0 and p2:
det(p0, p1, p2, p3) = - det(p2, p1, p0, p3)
^ ^ ^ ^
or, more generally, switching between even and odd permutations of the four vertices.
If the determinant is zero, the four points are co-planar, and cannot be fixed like this.
Finally, the code:
A relatively simple way to compute this determinant with 3-d vector math:
let: v1 = p1 - p0
v2 = p2 - p0
v3 = p3 - p0
norm12 = cross(v1, v2)
-> determinant = dot(norm12, v3)
The final determinant is also known as the "triple product" of v1, v2, and v3.
Note that I have hesitated to try to decode the exact sign convention (i.e., whether you need the determinant to be positive or negative) from your question: the wording and diagram you supply is more than a bit confusing.
Since you have the original library and its documentation, though, you are in the best position to answer this question. As a last resort, you can try the empirical method: try both signs, and pick the one that doesn't blow up...

Resources