Example figure
Point A: { x: xA; y: yA; neighbors: { B, J } }
Point B: { x: xB; y: yB; neighbors: { A, C } }
Point C: { x: xC; y: yC; neighbors: { B, D, G, H } }
etc.
Input
Set of verticies (points, cartesian coordinate system).
Some verticies are connected to others.
There is no edge's intersection on the input.
Question
How to find the closest bounding border for the given point (for example one of the green points 1, 2, 3)? I can use only connected verticies.
Solution for point 1 is { A, B, C, D, E, F, G, I, J }.
(Not { A, B, D } – there is no edge between { B and D } and { D and A }).
Solution for point 2 is { C, D, E, F, G }.
Solution for point 3 is { C, G, H }.
My idea
Find intersection of the vertical line (this line goes through question point) and the edge (between 2 verticies). I know 2 verticies now. How continue??
Can I use any algorithm from graph theory for this situation?
First, there are three corner cases in your idea, that must be dealt with:
the vertical line intersects above and below, you must choose one
the vertical line could intersect with a vertex not an edge
the vertical line could intersect with an edge that is also vertical
(so that there is an infinity of common points)
This said,
Say you find an edge below the point. So you found 1 edge and 2 vertices. You can select the left vertex, compute the angle of the found edge relative to each segment originating from this vertex, and select the segment with the lowest angle. Then you follow this new edge to find a new vertex, and iterate.
Related
I have a list of vertices that define a polygon, a list of perimeter edges that connect those vertices and define the outline of the polygon, and a list of inner edges connecting vertices, effectively splitting the polygon. None of the edges intersect each other (they only meet at the start and end points).
I want to partition the bigger polygon into its smaller components by splitting it at the inner edges. Basically I need to know which sets of vertices are part of a polygon that has no intersecting edges.
Basically, this is the information I have:
The vertices [0, 1, 3, 5, 7, 9, 11, 13, 15] define the outside perimeter of my polygon and the blue edge (5, 13) is an inner edge splitting that perimeter into two polygons. (Disregard the horizontal purple lines, they are the result of the trapezoidalization of the polygon to find the edge (5, 13). They have no further meaning)
This is what I want to get to:
One polygon is defined by the vertices [0, 1, 3, 5, 13, 15] and the other is defined by [5, 7, 9, 11, 13].
I need a solution that works for any number of splitting inner edges.
In the end, I would like to be able to partition a polygon like the following:
The sub-polygons are not necessarily convex. That might be of importance for any algorithm depending on it. The red sub-polygon has a concave vertex (13) for example.
My idea initially was to traverse the inside of each sub-polygon in a clockwise or counter-clockwise direction, keeping track of the vertices I encounter and their order. However I am having trouble finding a starting edge/vertex and guaranteeing that the next cw or ccw point is in fact on the inside of that sub-polygon I want to extract.
I tried to google for solutions but this is a field of mathematics I am too unfamiliar with to know what to search for. An idea/algorithm of how to tackle this problem would be much appreciated!
(I don't need any actual code, just an explanation of how to do this or pseudocode would totally suffice)
Now, unfortunately I don't have some code to show as I need a concept to try out first. I don't know how to tackle this problem and thus can't code anything that could accomplish what I need it to do.
EDIT:
This is just one step in what I am trying to do ultimately, which is polygon triangulation. I have read numerous solutions to that problem and wanted to implement it via trapezoidalization to get monotone polygons and ultimately triangulate those. This is basically the last step of (or I guess the next step after) the trapezoidalization, which is never explained in any resources on the topic that i could find.
The end result of the trapezoidalization are the inner edges which define the split into (in my case vertically monotone) polygons. I just need to split the polygons along those edges "datastructurally" so I can work on the subpolygons individually. I hope that helps to clarify things.
The key of the algorithm that you need, is to know how edges can be ordered:
Finding out which is the next edge in (counter)clockwise order
You can calculate the absolute angle, of an edge from node i to node j, with the following formula:
atan2(jy-iy, jx-ix)
See atan2
The relative angle between an edge (i, j) and (j, k) is then given by:
atan2(ky-jy, kx-jx) - atan2(jy-iy, jx-ix)
This expression may yield angles outside of the [-𝜋, 𝜋] range, so you should map the result back into that range by adding or subtracting 2𝜋.
So when you have traversed an edge (i, j) and need to choose the next edge (j, k), you can then pick the edge that has the smallest relative angle.
The Algorithm
The partitioning algorithm does not really need to know upfront which edges are internal edges, so I'll assume you just have an edge list. The algorithm could look like this:
Create an adjacency list, so you have a list of neighboring vertices for every vertex.
Add every edge to this adjacency list in both directions, so actually adding two directed edges for each original edge
Pick a directed edge (i, j) from the adjacency list, and remove it from there.
Define a new polygon and add vertex i as its first vertex.
Repeat until you get back to the first vertex in the polygon that's being constructed:
add vertex j to the polygon
find vertex k among the neighbors of vertex j that is not vertex i, and which minimizes the relative angle with the above given formula.
add this angle to a sum
Delete this directed edge from the neighbors of vertex j, so it will never be visited again
let i = j, j = k
If the sum of angles is positive (it will be 2𝜋) then add the polygon to the list of "positive" polygons, otherwise (it will be -2𝜋) add it to an alternative list.
Keep repeating from step 2 until there are no more directed edges in the adjacency list.
Finally you'll have two lists of polygons. One list will only have one polygon. This will be the original, outer polygon, and can be ignored. The other list will have the partitioning.
As a demo, here is some code in a runnable JavaScript snippet. It uses one of the examples you pictured in your question (but will consecutive vertex numbering), finds the partitioning according to this algorithm, and shows the result by coloring the polygons that were identified:
function partition(nodes, edges) {
// Create an adjacency list
let adj = [];
for (let i = 0; i < nodes.length; i++) {
adj[i] = []; // initialise the list for each node as an empty one
}
for (let i = 0; i < edges.length; i++) {
let a = edges[i][0]; // Get the two nodes (a, b) that this edge connects
let b = edges[i][1];
adj[a].push(b); // Add as directed edge in both directions
adj[b].push(a);
}
// Traverse the graph to identify polygons, until none are to be found
let polygons = [[], []]; // two lists of polygons, one per "winding" (clockwise or ccw)
let more = true;
while (more) {
more = false;
for (let i = 0; i < nodes.length; i++) {
if (adj[i].length) { // we have unvisited directed edge(s) here
let start = i;
let polygon = [i]; // collect the vertices on a new polygon
let sumAngle = 0;
// Take one neighbor out of this node's neighbor list and follow a path
for (let j = adj[i].pop(), next; j !== start; i = j, j = next) {
polygon.push(j);
// Get coordinates of the current edge's end-points
let ix = nodes[i][0];
let iy = nodes[i][1];
let jx = nodes[j][0];
let jy = nodes[j][1];
let startAngle = Math.atan2(jy-iy, jx-ix);
// In the adjacency list of node j, find the next neighboring vertex in counterclockwise order
// relative to node i where we came from.
let minAngle = 10; // Larger than any normalised angle
for (let neighborIndex = 0; neighborIndex < adj[j].length; neighborIndex++) {
let k = adj[j][neighborIndex];
if (k === i) continue; // ignore the reverse of the edge we came from
let kx = nodes[k][0];
let ky = nodes[k][1];
let relAngle = Math.atan2(ky-jy, kx-jx) - startAngle; // The "magic"
// Normalise the relative angle to the range [-PI, +PI)
if (relAngle < -Math.PI) relAngle += 2*Math.PI;
if (relAngle >= Math.PI) relAngle -= 2*Math.PI;
if (relAngle < minAngle) { // this one comes earlier in counterclockwise order
minAngle = relAngle;
nextNeighborIndex = neighborIndex;
}
}
sumAngle += minAngle; // track the sum of all the angles in the polygon
next = adj[j][nextNeighborIndex];
// delete the chosen directed edge (so it cannot be visited again)
adj[j].splice(nextNeighborIndex, 1);
}
let winding = sumAngle > 0 ? 1 : 0; // sumAngle will be 2*PI or -2*PI. Clockwise or ccw.
polygons[winding].push(polygon);
more = true;
}
}
}
// return the largest list of polygons, so to exclude the whole polygon,
// which will be the only one with a winding that's different from all the others.
return polygons[0].length > polygons[1].length ? polygons[0] : polygons[1];
}
// Sample input:
let nodes = [[59,25],[26,27],[9,59],[3,99],[30,114],[77,116],[89,102],[102,136],[105,154],[146,157],[181,151],[201,125],[194,83],[155,72],[174,47],[182,24],[153,6],[117,2],[89,9],[97,45]];
let internalEdges = [[6, 13], [13, 19], [19, 6]];
// Join outer edges with inner edges to an overall list of edges:
let edges = nodes.map((a, i) => [i, (i+1)%nodes.length]).concat(internalEdges);
// Apply algorithm
let polygons = partition(nodes, edges);
// Report on results
document.querySelector("div").innerHTML =
"input polygon has these points, numbered 0..n<br>" +
JSON.stringify(nodes) + "<br>" +
"resulting polygons, by vertex numbers<br>" +
JSON.stringify(polygons)
// Graphics handling
let io = {
ctx: document.querySelector("canvas").getContext("2d"),
drawEdges(edges) {
for (let [a, b] of edges) {
this.ctx.moveTo(...a);
this.ctx.lineTo(...b);
this.ctx.stroke();
}
},
colorPolygon(polygon, color) {
this.ctx.beginPath();
this.ctx.moveTo(...polygon[0]);
for (let p of polygon.slice(1)) {
this.ctx.lineTo(...p);
}
this.ctx.closePath();
this.ctx.fillStyle = color;
this.ctx.fill();
}
};
// Display original graph
io.drawEdges(edges.map(([a,b]) => [nodes[a], nodes[b]]));
// Color the polygons that the algorithm identified
let colors = ["red", "blue", "silver", "purple", "green", "brown", "orange", "cyan"];
for (let polygon of polygons) {
io.colorPolygon(polygon.map(i => nodes[i]), colors.pop());
}
<canvas width="400" height="180"></canvas>
<div></div>
I am implementing simple deformation in WebGL by moving vertices up or down, but stumbled upon a problem with the way the triangles are laid out.
I am using the PlaneGeometry in three.js and it uses the following layout for the triangles:
indices.push( a, b, d )
indices.push( b, c, d )
The layout used by three.js is on the left. Mine alternates between both.
Moving vertices up or down results in the image on the left, where, after moving the vertex, the deformation looks off. The blue dot represents the center vertex.
I decided to alternate the triangles using the following code:
if ( ( ix + iy ) % 2 === 0 ) {
// Even segment
indices.push( a, b, d )
indices.push( b, c, d )
} else {
// Odd segment
indices.push( a, b, c )
indices.push( a, c, d )
}
As you can see on the right, it gives me a much better result.
The two questions I have are:
Is the layout I used valid and what is its name?
Is there a better better way to solve this problem?
In 2D plane there are four points: O, A, B, P.
O, A, B define an "angle", i.e. two rays, both originating at O, and one passing though A, while the other goes through B. How to tell on which "side" of the angle the point P lies, i.e. whether it is inside the space marked by the two rays?
Note that the points are placed arbitrarily, i.e. the angle might have larger size than π.
This is similar problem to determining which side of line a point lies, as discussed e.g. in comp.graphics.algorithms FAQ (Subject 1.02: How do I find the distance from a point to a line?), but here it is about two rays, instead of one line.
Edit: Apologies for not stating it more explicitly: the angle is oriented, i.e. given P, it might lay on the right of O, A, B, but then it is on the left of O, B, A. Let's say the triangle O, A, B has clock-wise orientation. Again, it is similar to the "which side of line" problem: there it also matters whether the line passes though A, B, or B, A.
An example:
\ \
A B
\ right \ left
left \ right \
O------B---- O------A----
When looking from O, we can split the plane into two regions or "sides".
A region that sweeps ray OA towards ray OB while rotating counterclockwise.
And the rest, i.e. ray OB sweeps counterclockwise to OA.
To check if the point is in which region, you can use
// if isInRegion(O, A, B) is true, P is in the first region.
// otherwise, isInRegion(O, B, A) will be true.
bool isInRegion(O, A, B, P) {
return isCCW(O, A, P) && !isCCW(O, B, P)
}
// ref: http://www.cs.cmu.edu/%7Equake/robust.html
// For more robust methods, see the link.
bool isCCW(a, b, c) {
return ((a.x - c.x)*(b.y - c.y) - (a.y - c.y)*(b.x - c.x)) > 0;
}
I tried it here.
The discussion that follows is meant to detect points inside the hatched area.
There are two cases to be considered:
the angle AOB is smaller than a flat, then the point P must be to the right of AO and to the right of OB (intersection of the two half planes),
the angle AOB is larger than a flat, then the point P must be to the right of AO or to the right of OB (union of the two half planes).
The complete boolean expression is
AO|B . (AO|P . OB|P) + ¬ AO|B . (AO|P + OB|P),
where XY|Z expresses that Z lies on the right of XY, which is equivalent to "XYZ is clockwise" and is determined by the sign of the triangle area.
I don't think it is possible to make the expression simpler, unless you have several P for the same AOB.
I am looking for an algorithm that, given two rectangles that overlap partially or totally, finds the ordered list of vertexes that defines the polygon representing the sum of the two rectangles.
To be more specific:
I have as input two ordered list of points, representing the two rectangles
I know how to find the vertexes of the resulting polygon, which is formed by the vertexes of each rectangle which are outside the other rectangle, plus the intersection points between each edge of one rectangle with each edge of the other
I don't currently know how to order into an array the points, obtained as explained above, so that the element j and j+1 of the array represents the two vertexes of the same edge (this is what I mean by ordered list of vertexes).
Thanks in advance for any help
UPDATE :
I found a way to sort vertexes to obtain a polygon, as follows:
compute the vertexes centroid ( coords average )
sort the vertexes by the angle formed between the segment from the centroid and the vertex and any reference line passing by the centroid (e.g. the X axis ).
However, although I consistently obtain a polygon enclosing the two rectangles, without holes or intersecting edges, it is not always the polygon I want ( sometimes it includes extra area not belonging to one of the input rectangles ).
So I'm going back to the solution pointed in one of the comments, which is also described here:
polygon union without holes
Once you have the 4 vertices, you merely find the farther point using the distance formula (since it seems we can't make the assumption of a collinear or unrotated beginning rects)
So if you have points a = (xA, yA), b, c, d and you know these 4 points make a rectangle
float dist(Point a, Point b){
float dx = a.x - b.x;
float dy = a.y - b.y;
return Math.sqrt(dx * dx + dy * dy);
}
//somewhere else, where u need it
//put point A into index 0
Point curFarthest = b;
float distance = dist(a, b);
if (dist(a, c) > distance){
curFarther = c;
distance = dist(a, c);
} else if (dist(a, d) > distance){
curFarther = d;
curFarthest = dist(a, d);
}
//store curFarthest into index 2
// store the rest (exculding points a and curFarthest)
// into index 1 and 3 in no particular order
I am working on the same problem but I use a different approach(work still in progress).
Find the intersection points.
Distance of each point(vertices) with its neighboring connected points.
Using Dinics Algorithm find the Maxmimum flow.
Note: there will be a few special cases. But then again my problems revolves around polygons having 1 common point(vertice).
We have a ray that starts at point A(X, Y) and goes on forever through given point B(X, Y) != A. We have a rectangle defined by points K,L,M,N each with its (X, Y).
I wonder how to detect if our ray intersects with any point of our rectangle (get a bool, not precice coordinates)? What is algorithm for calculating such value?
Let me get this straight. You have a vector v headed off in direction (b_x - a_x, b_y - a_y) and starting at (a_x, a_y).
Consider the vector w = (b_y - a_y, a_x - b_x). It is at right angles to the first. (Verify with a dot product.) Therefore for any point (p_x, p_y) you can easily tell which side of the vector it is on by taking a dot product of (p_x - a_x, p_y - a_y) with w and looking at the sign.
So take that dot product with all 4 corners of your rectangle. If any give a 0 dot product, they are on the vector, if the signs change there is an intersection, if the sign is always the same there is no intersection.
You can use the sweep line algorithm to do so.
http://en.wikipedia.org/wiki/Sweep_line_algorithm
A less clever, but conceptually simpler approach: the ray intersects the rectangle if and only if it intersects at least one of the sides. So for each side of the rectangle, find the intersection (if any) of the line passing through the endpoints with the ray AB; then it's simply a range check to determine if that intersection lies is part of the line segment on the boundary of the rectangle, or if it is outside.
You probably want to compute the segment (if any) of the ray AB that intersects the rectangle. If your rectangle is axis-aligned, this will be easier to compute in a numerical sense, but the logic should be similar.
You can represent a directed line L as [a, b, c] such that, if point P is (X, Y):
let L(P) = a*X + b*Y + c
then, if L(P) == 0, point P is on L
if L(P) > 0, point P is to the left of L
if L(P) < 0, point P is to the right of L
Note that this is redundant in the sense that, given any k > 0, [k*a, k*b, k*c] represents the same line (this property makes it a homogeneous coordinate system). We can also represent points with homogeneous coordinates by augmenting them with a third coordinate:
2D point P = (X, Y)
-> homogeneous coordinates [x, y, w] for P are [X, Y, 1]
L(P) = L.a*P.x + L.b*P.y + L.c*P.w == a*X + b*Y + c*1
In any case, given two corners of your rectangle (say, P and Q), you can compute the homogeneous coordinates of the line through P and Q using a 3-D cross-product of their homogeneous coordinates:
homogeneous coordinates for line PQ are: [P.X, P.Y, 1] cross [Q.X, Q.Y, 1]
-> PQ.a = P.Y - Q.Y
PQ.b = Q.X - P.X
PQ.c = P.X*Q.Y - Q.X*P.Y
You can verify mathematically that points P and Q are both on the above-described line PQ.
To represent the segment of line AB that intersects the rectangle, first compute vector V = B - A, as in #btilly's answer. For homogeneous coordinates, this works as follows:
A = [A.X, A.Y, 1]
B = [B.X, B.Y, 1]
-> V = B - A = [B.X-A.X, B.Y-A.Y, 0]
for any point C on AB: homogeneous coordinates for C = u*A + v*V
(where u and v are not both zero)
Point C will be on the ray part of the line only if u and v are both non-negative. (This representation may seem obscure, compared to the usual formulation of C = A + lambda * V, but doing it this way avoids unnecessary divide-by-zero cases...)
Now, we can compute the ray intersection: we represent a segment of the line AB by the parametric [u,v] coordinates of each endpoint: { start = [start.u, start.v]; end = [end.u, end.v] }.
We compute the edges of the rectangle in the counterclockwise direction, so that points inside the rectangle are on the left/positive side (L(P)>0) of every edge.
Starting segment is entire ray:
start.u = 1; start.v = 0
end.u = 0; end.v = 1
for each counterclockwise-directed edge L of the rectangle:
compute:
L(A) = L.a*A.X + L.b*A.Y + L.c
L(V) = L.a*V.X + L.b*V.Y
L(start) = start.u * L(A) + start.v * L(V)
L(end) = end.u * L(A) + end.v * L(V)
if L(start) and L(end) are both less than zero:
exit early: return "no intersection found"
if L(start) and L(end) are both greater or equal to zero:
do not update the segment; continue with the next line
else, if L(start) < 0:
update start coordinates:
start.u := L(V)
start.v := -L(A)
else, if L(end) < 0:
update end coordinates:
end.u := -L(V)
end.v := L(A)
on normal loop exit, the ray does intersect the rectangle;
the part of the ray inside the rectangle is the segment between points:
homog_start = start.u * A + start.v * V
homog_end = end.u * A + end.v * V
return "intersection found":
intersection_start.X = homog_start.x/homog_start.w
intersection_start.Y = homog_start.y/homog_start.w
intersection_end.X = homog_end.x/homog_end.w
intersection_end.Y = homog_end.y/homog_end.w
Note that this will work for arbitrary convex polygons, not just rectangles; the above is actually a general ray/convex polygon intersection algorithm. For a rectangle, you can unroll the for-loop; and, if the rectangle is axis-aligned, you can drastically simplify the arithmetic. However, the 4-case decision in the inner loop should remain the same for each edge.