Nearest neighbor using polar coordinates - sorting

Is there any method to order a set of (x,y) points with respect the origin depending on both distance and angle?
In my problem I have a set of points in the plane, all spread around the center (0,0). These represent some data feature, and nearest points represent similar data.
Then I need to order them and so I itarate until all points are considered, taking the NN with respect to the origin (0,0) considering just the distance between points.
These points are very closer, and although similar data are closer one each other, some different data are represented far from each other but in the same circumference around the origin (this means that they have the same distance with respect the center), thus often I take points at almost the same distance (the same radius) but that lie in the opposite sides of the (imaginary) circle around the center.
I want to use the polar coordiantes to order these points. So I want to consider first the radial distance, and then the angle. I can compute these tho measures for all the points, but I don't know how to obtain an unique index to order them.
This is the code I use: in avPositions I have a set of pre-computed (x,y) positions of a grid, for each available position I have to select the nearest point in DATA.
for(var i=0;i<avPositions.length; i++)
{
res = posed.indexOf(false);
if(res<0) break;
x = avPositions[i][0];
y = avPositions[i][1];
dist = null;
dist = DATA.map(function(obj) { x1 = obj[0];
y1 = obj[1];
S = (x-x1)*(x-x1) + (y-y1)*(y-y1);
return Math.sqrt(S);
});
nearestIndex = dist.indexOf(Math.min.apply(Math, dist));
while(posed[nearestIndex])
{
dist[nearestIndex] = 99999999;
nearestIndex = dist.indexOf(Math.min.apply(Math, dist));
}
if(nearestIndex < 0) break;
posed[nearestIndex] = true;
DATAgrid[nearestIndex][0] = x;
DATAgrid[nearestIndex][1] = y;
}

Related

Calculate points on an arc of a circle using center, radius and 3 points on the circle

Given the center, radius and and 3 points on a circle, I want to draw an arc that starts at the first point, passing through the second and ends at the third by specifying the angle to start drawing and the amount of angle to rotate. To do this, I need to calculate the points on the arc. I want the number of points calculated to be variable so I can adjust the accuracy of the calculated arc, so this means I probably need a loop that calculates each point by rotating a little after it has calculated a point. I've read the answer to this question Draw arc with 2 points and center of the circle but it only solves the problem of calculating the angles because I don't know how 'canvas.drawArc' is implemented.
This question has two parts:
How to find the arc between two points that passes a third point?
How to generate a set of points on the found arc?
Let's start with first part. Given three points A, B and C on the (O, r) circle we want to find the arc between A and C that passes through B. To find the internal angle of the arc we need to calculate the oriented angles of AB and AC arcs. If angle of AB was greater than AC, we are in wrong direction:
Va.x = A.x - O.x;
Va.y = A.y - O.y;
Vb.x = B.x - O.x;
Vb.y = B.y - O.y;
Vc.x = C.x - O.x;
Vc.y = C.y - O.y;
tb = orientedAngle(Va.x, Va.y, Vb.x, Vb.y);
tc = orientedAngle(Va.x, Va.y, Vc.x, Vc.y);
if tc<tb
tc = tc - 2 * pi;
end
function t = orientedAngle(x1, y1, x2, y2)
t = atan2(x1*y2 - y1*x2, x1*x2 + y1*y2);
if t<0
t = t + 2 * pi;
end
end
Now the second part. You said:
I probably need a loop that calculates each point by rotating a little
after it has calculated a point.
But the question is, how little? Since the perimeter of the circle increases as its radius increase, you cannot reach a fixed accuracy with a fixed angle. In other words, to draw two arcs with the same angle and different radii, we need a different number of points. What we can assume to be [almost] constant is the distance between these points, or the length of the segments we draw to simulate the arc:
segLen = someConstantLength;
arcLen = abs(tc)*r;
segNum = ceil(arcLen/segLen);
segAngle = tc / segNum;
t = atan2(Va.y, Va.x);
for i from 0 to segNum
P[i].x = O.x + r * cos(t);
P[i].y = O.y + r * sin(t);
t = t + segAngle;
end
Note that although in this method A and C will certainly be created, but point B will not necessarily be one of the points created. However, the distance of this point from the nearest segment will be very small.

How to calculate angle of rotation of a rectangle, given its 4 points

I have a set of 2D points. I did eigenvectors estimation of its covariance. Made transformation to new basis and found the bounding box there.
For simplicity giving code in octave below.
Points are given as: points variable with shape of Nx2
mycov = cov(points);
[V, E] = eig(mycov);
new_basis_points = V*points';
Then in the code I estimate max and min values for each axis and make four points set:
points = [[minX, minY],
[minX, maxY],
[maxX, minY],
[maxX, maxY]];
Now I transform back to old basis:
old_basis_bounding_box = V'*points';
These calculations are correct, I get four corner points in old basis. But now I want to estimate the angle of rotation of the rectangle between its side and X axis.
The problem is, that the order of points in old_basis_bounding_box is not defined. So I'm not sure which two points to select to make angle estimation.
How should I proceed?
I believe the angle alpha (marked green in the image) is what you are looking for. Assuming the lowest point of the rectangle is O(0, 0), this angle can be easily calculated as cos-1(a/sqrt(a^2+b^2)), where B(a,b) is the point with lowest positive slope. As of D ≠ O (where D is the point with the lowest y-axis coordinate), just move the whole thing by the vector OD so that D = O.
Don't forget to separately handle the case when the rectangle already aligned to the axis, when you may get division by zero.
My pseudo-code:
struct Point
{
double x, y, angle;
Point (double x, double y): x(x), y(y) {}
};
bool SortByY (Point a, Point b)
{
return a.y < b.y;
}
bool SortByAngle (Point a, Point b)
{
return a.angle < b.angle;
}
double GetRotationAngle(vector<Point> points)
{
sort (points.begin(), points.end(), SortByY);
// If there are 2 points lie on the same y-axis coordinates, simply return 0
if (points[0].y == points[1].y) return 0;
Point D = points[0];
for (int i=1; i<4; i++)
{
// Move the whole thing by vector OD
double a = points[i].x -= D.x;
double b = points[i].y -= D.y;
// Keep in mind that in C++, the acos function returns value in radians, you may need to convert to degrees for your purposes.
points[i].angle = acos(a / sqrt(a*a+b*b));
}
sort (points.begin()+1, points.end(), SortByAngle);
return points[1].angle;
}

Calculating the Area of Intersection Between a Plane and Rectangular Prism

If I have a plane, let's say the xy plane, and a rectangular prism that can be arbitrarily rotated/translated in 3 dimensions. Are there any cool algorithms/methods that can be used to determine the area of intersection between the two?
One approach would be to explicitly find the polygonal region of intersection R between the prism and the plane, triangulate R and sum the areas of the triangles to give the total intersection area.
The vertices of the intersecting polygon R can be found by performing a series of line-plane intersection tests between the edges of the prism and the plane.
Based on the relative orientation of the plane/prism, the intersecting polygon could take a number of different configurations (i.e. it won't always be a rectangle!). Given a regular prism the intersecting region should always be convex though, allowing the triangulation to be obtained as a simple fan.
Given a triangulation of R the total area of intersection is simply the sum of the triangle areas.
Once you have the polygonal region of intersection, you don't need to triangulate it to compute its area. There's a much simpler algorithm:
float area = 0.0f;
// Run through all segments
for (int i = 0; i < corners.Length; i++)
{
// Get end points of segments
Vector2 A = corners[i];
Vector2 B = corners[(i+1) % corners.Length];
// Add the signed(!) area of a quadrangle with two corners A, B
// and two corners with same y values on the y axis
//
// |---------A
// | + /
// |-------B
//
// |-------B
// | - \
// |---------A
//
area += 0.5f * (A.x + B.x) * (B.y - A.y);
}
Cf. http://alienryderflex.com/polygon_area/

Algorithm to generate random 2D polygon

I'm not sure how to approach this problem. I'm not sure how complex a task it is. My aim is to have an algorithm that generates any polygon. My only requirement is that the polygon is not complex (i.e. sides do not intersect). I'm using Matlab for doing the maths but anything abstract is welcome.
Any aid/direction?
EDIT:
I was thinking more of code that could generate any polygon even things like this:
I took #MitchWheat and #templatetypedef's idea of sampling points on a circle and took it a bit farther.
In my application I need to be able to control how weird the polygons are, ie start with regular polygons and as I crank up the parameters they get increasingly chaotic. The basic idea is as stated by #templatetypedef; walk around the circle taking a random angular step each time, and at each step put a point at a random radius. In equations I'm generating the angular steps as
where theta_i and r_i give the angle and radius of each point relative to the centre, U(min, max) pulls a random number from a uniform distribution, and N(mu, sigma) pulls a random number from a Gaussian distribution, and clip(x, min, max) thresholds a value into a range. This gives us two really nice parameters to control how wild the polygons are - epsilon which I'll call irregularity controls whether or not the points are uniformly space angularly around the circle, and sigma which I'll call spikeyness which controls how much the points can vary from the circle of radius r_ave. If you set both of these to 0 then you get perfectly regular polygons, if you crank them up then the polygons get crazier.
I whipped this up quickly in python and got stuff like this:
Here's the full python code:
import math, random
from typing import List, Tuple
def generate_polygon(center: Tuple[float, float], avg_radius: float,
irregularity: float, spikiness: float,
num_vertices: int) -> List[Tuple[float, float]]:
"""
Start with the center of the polygon at center, then creates the
polygon by sampling points on a circle around the center.
Random noise is added by varying the angular spacing between
sequential points, and by varying the radial distance of each
point from the centre.
Args:
center (Tuple[float, float]):
a pair representing the center of the circumference used
to generate the polygon.
avg_radius (float):
the average radius (distance of each generated vertex to
the center of the circumference) used to generate points
with a normal distribution.
irregularity (float):
variance of the spacing of the angles between consecutive
vertices.
spikiness (float):
variance of the distance of each vertex to the center of
the circumference.
num_vertices (int):
the number of vertices of the polygon.
Returns:
List[Tuple[float, float]]: list of vertices, in CCW order.
"""
# Parameter check
if irregularity < 0 or irregularity > 1:
raise ValueError("Irregularity must be between 0 and 1.")
if spikiness < 0 or spikiness > 1:
raise ValueError("Spikiness must be between 0 and 1.")
irregularity *= 2 * math.pi / num_vertices
spikiness *= avg_radius
angle_steps = random_angle_steps(num_vertices, irregularity)
# now generate the points
points = []
angle = random.uniform(0, 2 * math.pi)
for i in range(num_vertices):
radius = clip(random.gauss(avg_radius, spikiness), 0, 2 * avg_radius)
point = (center[0] + radius * math.cos(angle),
center[1] + radius * math.sin(angle))
points.append(point)
angle += angle_steps[i]
return points
def random_angle_steps(steps: int, irregularity: float) -> List[float]:
"""Generates the division of a circumference in random angles.
Args:
steps (int):
the number of angles to generate.
irregularity (float):
variance of the spacing of the angles between consecutive vertices.
Returns:
List[float]: the list of the random angles.
"""
# generate n angle steps
angles = []
lower = (2 * math.pi / steps) - irregularity
upper = (2 * math.pi / steps) + irregularity
cumsum = 0
for i in range(steps):
angle = random.uniform(lower, upper)
angles.append(angle)
cumsum += angle
# normalize the steps so that point 0 and point n+1 are the same
cumsum /= (2 * math.pi)
for i in range(steps):
angles[i] /= cumsum
return angles
def clip(value, lower, upper):
"""
Given an interval, values outside the interval are clipped to the interval
edges.
"""
return min(upper, max(value, lower))
#MateuszKonieczny here is code to create an image of a polygon from a list of vertices.
vertices = generate_polygon(center=(250, 250),
avg_radius=100,
irregularity=0.35,
spikiness=0.2,
num_vertices=16)
black = (0, 0, 0)
white = (255, 255, 255)
img = Image.new('RGB', (500, 500), white)
im_px_access = img.load()
draw = ImageDraw.Draw(img)
# either use .polygon(), if you want to fill the area with a solid colour
draw.polygon(vertices, outline=black, fill=white)
# or .line() if you want to control the line thickness, or use both methods together!
draw.line(vertices + [vertices[0]], width=2, fill=black)
img.show()
# now you can save the image (img), or do whatever else you want with it.
There's a neat way to do what you want by taking advantage of the MATLAB classes DelaunayTri and TriRep and the various methods they employ for handling triangular meshes. The code below follows these steps to create an arbitrary simple polygon:
Generate a number of random points equal to the desired number of sides plus a fudge factor. The fudge factor ensures that, regardless of the result of the triangulation, we should have enough facets to be able to trim the triangular mesh down to a polygon with the desired number of sides.
Create a Delaunay triangulation of the points, resulting in a convex polygon that is constructed from a series of triangular facets.
If the boundary of the triangulation has more edges than desired, pick a random triangular facet on the edge that has a unique vertex (i.e. the triangle only shares one edge with the rest of the triangulation). Removing this triangular facet will reduce the number of boundary edges.
If the boundary of the triangulation has fewer edges than desired, or the previous step was unable to find a triangle to remove, pick a random triangular facet on the edge that has only one of its edges on the triangulation boundary. Removing this triangular facet will increase the number of boundary edges.
If no triangular facets can be found matching the above criteria, post a warning that a polygon with the desired number of sides couldn't be found and return the x and y coordinates of the current triangulation boundary. Otherwise, keep removing triangular facets until the desired number of edges is met, then return the x and y coordinates of triangulation boundary.
Here's the resulting function:
function [x, y, dt] = simple_polygon(numSides)
if numSides < 3
x = [];
y = [];
dt = DelaunayTri();
return
end
oldState = warning('off', 'MATLAB:TriRep:PtsNotInTriWarnId');
fudge = ceil(numSides/10);
x = rand(numSides+fudge, 1);
y = rand(numSides+fudge, 1);
dt = DelaunayTri(x, y);
boundaryEdges = freeBoundary(dt);
numEdges = size(boundaryEdges, 1);
while numEdges ~= numSides
if numEdges > numSides
triIndex = vertexAttachments(dt, boundaryEdges(:,1));
triIndex = triIndex(randperm(numel(triIndex)));
keep = (cellfun('size', triIndex, 2) ~= 1);
end
if (numEdges < numSides) || all(keep)
triIndex = edgeAttachments(dt, boundaryEdges);
triIndex = triIndex(randperm(numel(triIndex)));
triPoints = dt([triIndex{:}], :);
keep = all(ismember(triPoints, boundaryEdges(:,1)), 2);
end
if all(keep)
warning('Couldn''t achieve desired number of sides!');
break
end
triPoints = dt.Triangulation;
triPoints(triIndex{find(~keep, 1)}, :) = [];
dt = TriRep(triPoints, x, y);
boundaryEdges = freeBoundary(dt);
numEdges = size(boundaryEdges, 1);
end
boundaryEdges = [boundaryEdges(:,1); boundaryEdges(1,1)];
x = dt.X(boundaryEdges, 1);
y = dt.X(boundaryEdges, 2);
warning(oldState);
end
And here are some sample results:
The generated polygons could be either convex or concave, but for larger numbers of desired sides they will almost certainly be concave. The polygons are also generated from points randomly generated within a unit square, so polygons with larger numbers of sides will generally look like they have a "squarish" boundary (such as the lower right example above with the 50-sided polygon). To modify this general bounding shape, you can change the way the initial x and y points are randomly chosen (i.e. from a Gaussian distribution, etc.).
For a convex 2D polygon (totally off the top of my head):
Generate a random radius, R
Generate N random points on the circumference of a circle of Radius R
Move around the circle and draw straight lines between adjacent points on the circle.
As #templatetypedef and #MitchWheat said, it is easy to do so by generating N random angles and radii. It is important to sort the angles, otherwise it will not be a simple polygon. Note that I am using a neat trick to draw closed curves - I described it in here. By the way, the polygons might be concave.
Note that all of these polygons will be star shaped. Generating a more general polygon is not a simple problem at all.
Just to give you a taste of the problem - check out
http://www.cosy.sbg.ac.at/~held/projects/rpg/rpg.html
and http://compgeom.cs.uiuc.edu/~jeffe/open/randompoly.html.
function CreateRandomPoly()
figure();
colors = {'r','g','b','k'};
for i=1:5
[x,y]=CreatePoly();
c = colors{ mod(i-1,numel(colors))+1};
plotc(x,y,c);
hold on;
end
end
function [x,y]=CreatePoly()
numOfPoints = randi(30);
theta = randi(360,[1 numOfPoints]);
theta = theta * pi / 180;
theta = sort(theta);
rho = randi(200,size(theta));
[x,y] = pol2cart(theta,rho);
xCenter = randi([-1000 1000]);
yCenter = randi([-1000 1000]);
x = x + xCenter;
y = y + yCenter;
end
function plotc(x,y,varargin)
x = [x(:) ; x(1)];
y = [y(:) ; y(1)];
plot(x,y,varargin{:})
end
Here is a working port for Matlab of Mike Ounsworth solution. I did not optimized it for matlab. I might update the solution later for that.
function [points] = generatePolygon(ctrX, ctrY, aveRadius, irregularity, spikeyness, numVerts)
%{
Start with the centre of the polygon at ctrX, ctrY,
then creates the polygon by sampling points on a circle around the centre.
Randon noise is added by varying the angular spacing between sequential points,
and by varying the radial distance of each point from the centre.
Params:
ctrX, ctrY - coordinates of the "centre" of the polygon
aveRadius - in px, the average radius of this polygon, this roughly controls how large the polygon is, really only useful for order of magnitude.
irregularity - [0,1] indicating how much variance there is in the angular spacing of vertices. [0,1] will map to [0, 2pi/numberOfVerts]
spikeyness - [0,1] indicating how much variance there is in each vertex from the circle of radius aveRadius. [0,1] will map to [0, aveRadius]
numVerts - self-explanatory
Returns a list of vertices, in CCW order.
Website: https://stackoverflow.com/questions/8997099/algorithm-to-generate-random-2d-polygon
%}
irregularity = clip( irregularity, 0,1 ) * 2*pi/ numVerts;
spikeyness = clip( spikeyness, 0,1 ) * aveRadius;
% generate n angle steps
angleSteps = [];
lower = (2*pi / numVerts) - irregularity;
upper = (2*pi / numVerts) + irregularity;
sum = 0;
for i =1:numVerts
tmp = unifrnd(lower, upper);
angleSteps(i) = tmp;
sum = sum + tmp;
end
% normalize the steps so that point 0 and point n+1 are the same
k = sum / (2*pi);
for i =1:numVerts
angleSteps(i) = angleSteps(i) / k;
end
% now generate the points
points = [];
angle = unifrnd(0, 2*pi);
for i =1:numVerts
r_i = clip( normrnd(aveRadius, spikeyness), 0, 2*aveRadius);
x = ctrX + r_i* cos(angle);
y = ctrY + r_i* sin(angle);
points(i,:)= [(x),(y)];
angle = angle + angleSteps(i);
end
end
function value = clip(x, min, max)
if( min > max ); value = x; return; end
if( x < min ) ; value = min; return; end
if( x > max ) ; value = max; return; end
value = x;
end

Algorithm to detect intersection of two rectangles?

I'm looking for an algorithm to detect if two rectangles intersect (one at an arbitrary angle, the other with only vertical/horizontal lines).
Testing if a corner of one is in the other ALMOST works. It fails if the rectangles form a cross-like shape.
It seems like a good idea to avoid using slopes of the lines, which would require special cases for vertical lines.
The standard method would be to do the separating axis test (do a google search on that).
In short:
Two objects don't intersect if you can find a line that separates the two objects. e.g. the objects / all points of an object are on different sides of the line.
The fun thing is, that it's sufficient to just check all edges of the two rectangles. If the rectangles don't overlap one of the edges will be the separating axis.
In 2D you can do this without using slopes. An edge is simply defined as the difference between two vertices, e.g.
edge = v(n) - v(n-1)
You can get a perpendicular to this by rotating it by 90°. In 2D this is easy as:
rotated.x = -unrotated.y
rotated.y = unrotated.x
So no trigonometry or slopes involved. Normalizing the vector to unit-length is not required either.
If you want to test if a point is on one or another side of the line you can just use the dot-product. the sign will tell you which side you're on:
// rotated: your rotated edge
// v(n-1) any point from the edge.
// testpoint: the point you want to find out which side it's on.
side = sign (rotated.x * (testpoint.x - v(n-1).x) +
rotated.y * (testpoint.y - v(n-1).y);
Now test all points of rectangle A against the edges of rectangle B and vice versa. If you find a separating edge the objects don't intersect (providing all other points in B are on the other side of the edge being tested for - see drawing below). If you find no separating edge either the rectangles are intersecting or one rectangle is contained in the other.
The test works with any convex polygons btw..
Amendment: To identify a separating edge, it is not enough to test all points of one rectangle against each edge of the other. The candidate-edge E (below) would as such be identified as a separating edge, as all points in A are in the same half-plane of E. However, it isn't a separating edge because the vertices Vb1 and Vb2 of B are also in that half-plane. It would only have been a separating edge if that had not been the case
http://www.iassess.com/collision.png
Basically look at the following picture:
If the two boxes collide, the lines A and B will overlap.
Note that this will have to be done on both the X and the Y axis, and both need to overlap for the rectangles to collide.
There is a good article in gamasutra.com which answers the question (the picture is from the article).
I did similar algorithm 5 years ago and I have to find my code snippet to post it here later
Amendment: The Separating Axis Theorem states that two convex shapes do not overlap if a separating axis exists (i.e. one where the projections as shown do not overlap). So "A separating axis exists" => "No overlap". This is not a bi-implication so you cannot conclude the converse.
In Cocoa you could easily detect whether the selectedArea rect intersects your rotated NSView's frame rect.
You don't even need to calculate polygons, normals an such. Just add these methods to your NSView subclass.
For instance, the user selects an area on the NSView's superview, then you call the method DoesThisRectSelectMe passing the selectedArea rect. The API convertRect: will do that job. The same trick works when you click on the NSView to select it. In that case simply override the hitTest method as below. The API convertPoint: will do that job ;-)
- (BOOL)DoesThisRectSelectMe:(NSRect)selectedArea
{
NSRect localArea = [self convertRect:selectedArea fromView:self.superview];
return NSIntersectsRect(localArea, self.bounds);
}
- (NSView *)hitTest:(NSPoint)aPoint
{
NSPoint localPoint = [self convertPoint:aPoint fromView:self.superview];
return NSPointInRect(localPoint, self.bounds) ? self : nil;
}
m_pGladiator's answer is right and I prefer to it.
Separating axis test is simplest and standard method to detect rectangle overlap. A line for which the projection intervals do not overlap we call a separating axis. Nils Pipenbrinck's solution is too general. It use dot product to check whether one shape is totally on the one side of the edge of the other. This solution is actually could induce to n-edge convex polygons. However, it is not optmized for two rectangles.
the critical point of m_pGladiator's answer is that we should check two rectangles' projection on both axises (x and y). If two projections are overlapped, then we could say these two rectangles are overlapped. So the comments above to m_pGladiator's answer are wrong.
for the simple situation, if two rectangles are not rotated,
we present a rectangle with structure:
struct Rect {
x, // the center in x axis
y, // the center in y axis
width,
height
}
we name rectangle A, B with rectA, rectB.
if Math.abs(rectA.x - rectB.x) < (Math.abs(rectA.width + rectB.width) / 2)
&& (Math.abs(rectA.y - rectB.y) < (Math.abs(rectA.height + rectB.height) / 2))
then
// A and B collide
end if
if any one of the two rectangles are rotated,
It may needs some efforts to determine the projection of them on x and y axises. Define struct RotatedRect as following:
struct RotatedRect : Rect {
double angle; // the rotating angle oriented to its center
}
the difference is how the width' is now a little different:
widthA' for rectA: Math.sqrt(rectA.width*rectA.width + rectA.height*rectA.height) * Math.cos(rectA.angle)
widthB' for rectB: Math.sqrt(rectB.width*rectB.width + rectB.height*rectB.height) * Math.cos(rectB.angle)
if Math.abs(rectA.x - rectB.x) < (Math.abs(widthA' + widthB') / 2)
&& (Math.abs(rectA.y - rectB.y) < (Math.abs(heightA' + heightB') / 2))
then
// A and B collide
end if
Could refer to a GDC(Game Development Conference 2007) PPT www.realtimecollisiondetection.net/pubs/GDC07_Ericson_Physics_Tutorial_SAT.ppt
The accepted answer about the separating axis test was very illuminating but I still felt it was not trivial to apply. I will share the pseudo-code I thought, "optimizing" first with the bounding circle test (see this other answer), in case it might help other people. I considered two rectangles A and B of the same size (but it is straightforward to consider the general situation).
1 Bounding circle test:
function isRectangleACollidingWithRectangleB:
if d > 2 * R:
return False
...
Computationally is much faster than the separating axis test. You only need to consider the separating axis test in the situation that both circles collide.
2 Separating axis test
The main idea is:
Consider one rectangle. Cycle along its vertices V(i).
Calculate the vector Si+1: V(i+1) - V(i).
Calculate the vector Ni using Si+1: Ni = (-Si+1.y, Si+1.x). This vector is the blue from the image. The sign of the dot product between the vectors from V(i) to the other vertices and Ni will define the separating axis (magenta dashed line).
Calculate the vector Si-1: V(i-1) - V(i). The sign of the dot product between Si-1 and Ni will define the location of the first rectangle with respect to the separating axis. In the example of the picture, they go in different directions, so the sign will be negative.
Cycle for all vertices j of the second square and calculate the vector Sij = V(j) - V(i).
If for any vertex V(j), the sign of the dot product of the vector Sij with Ni is the same as with the dot product of the vector Si-1 with Ni, this means both vertices V(i) and V(j) are on the same side of the magenta dashed line and, thus, vertex V(i) does not have a separating axis. So we can just skip vertex V(i) and repeat for the next vertex V(i+1). But first we update Si-1 = - Si+1. When we reach the last vertex (i = 4), if we have not found a separating axis, we repeat for the other rectangle. And if we still do not find a separating axis, this implies there is no separating axis and both rectangles collide.
If for a given vertex V(i) and all vertices V(j), the sign of the dot product of the vector Sij with Ni is different than with the vector Si-1 with Ni (as occurs in the image), this means we have found the separating axis and the rectangles do not collide.
In pseudo-code:
function isRectangleACollidingWithRectangleB:
...
#Consider first rectangle A:
Si-1 = Vertex_A[4] - Vertex_A[1]
for i in Vertex_A:
Si+1 = Vertex_A[i+1] - Vertex_A[i]
Ni = [- Si+1.y, Si+1.x ]
sgn_i = sign( dot_product(Si-1, Ni) ) #sgn_i is the sign of rectangle A with respect the separating axis
for j in Vertex_B:
sij = Vertex_B[j] - Vertex_A[i]
sgn_j = sign( dot_product(sij, Ni) ) #sgnj is the sign of vertex j of square B with respect the separating axis
if sgn_i * sgn_j > 0: #i.e., we have the same sign
break #Vertex i does not define separating axis
else:
if j == 4: #we have reached the last vertex so vertex i defines the separating axis
return False
Si-1 = - Si+1
#Repeat for rectangle B
...
#If we do not find any separating axis
return True
You can find the code in Python here.
Note:
In this other answer they also suggest for optimization to try before the separating axis test whether the vertices of one rectangle are inside the other as a sufficient condition for colliding. However, in my trials I found this intermediate step to actually be less efficient.
Check to see if any of the lines from one rectangle intersect any of the lines from the other. Naive line segment intersection is easy to code up.
If you need more speed, there are advanced algorithms for line segment intersection (sweep-line). See http://en.wikipedia.org/wiki/Line_segment_intersection
One solution is to use something called a No Fit Polygon. This polygon is calculated from the two polygons (conceptually by sliding one around the other) and it defines the area for which the polygons overlap given their relative offset. Once you have this NFP then you simply have to do an inclusion test with a point given by the relative offset of the two polygons. This inclusion test is quick and easy but you do have to create the NFP first.
Have a search for No Fit Polygon on the web and see if you can find an algorithm for convex polygons (it gets MUCH more complex if you have concave polygons). If you can't find anything then email me at howard dot J dot may gmail dot com
Here is what I think will take care of all possible cases.
Do the following tests.
Check any of the vertices of rectangle 1 reside inside rectangle 2 and vice versa. Anytime you find a vertex that resides inside the other rectangle you can conclude that they intersect and stop the search. THis will take care of one rectangle residing completely inside the other.
If the above test is inconclusive find the intersecting points of each line of 1 rectangle with each line of the other rectangle. Once a point of intersection is found check if it resides between inside the imaginary rectangle created by the corresponding 4 points. When ever such a point is found conclude that they intersect and stop the search.
If the above 2 tests return false then these 2 rectangles do not overlap.
If you're using Java, all implementations of the Shape interface have an intersects method that take a rectangle.
Well, the brute force method is to walk the edges of the horizontal rectangle and check each point along the edge to see if it falls on or in the other rectangle.
The mathematical answer is to form equations describing each edge of both rectangles. Now you can simply find if any of the four lines from rectangle A intersect any of the lines of rectangle B, which should be a simple (fast) linear equation solver.
-Adam
You could find the intersection of each side of the angled rectangle with each side of the axis-aligned one. Do this by finding the equation of the infinite line on which each side lies (i.e. v1 + t(v2-v1) and v'1 + t'(v'2-v'1) basically), finding the point at which the lines meet by solving for t when those two equations are equal (if they're parallel, you can test for that) and then testing whether that point lies on the line segment between the two vertices, i.e. is it true that 0 <= t <= 1 and 0 <= t' <= 1.
However, this doesn't cover the case when one rectangle completely covers the other. That you can cover by testing whether all four points of either rectangle lie inside the other rectangle.
This is what I would do, for the 3D version of this problem:
Model the 2 rectangles as planes described by equation P1 and P2, then write P1=P2 and derive from that the line of intersection equation, which won't exist if the planes are parallel (no intersection), or are in the same plane, in which case you get 0=0. In that case you will need to employ a 2D rectangle intersection algorithm.
Then I would see if that line, which is in the plane of both rectangles, passes through both rectangles. If it does, then you have an intersection of 2 rectangles, otherwise you don't (or shouldn't, I might have missed a corner case in my head).
To find if a line passes through a rectangle in the same plane, I would find the 2 points of intersection of the line and the sides of the rectangle (modelling them using line equations), and then make sure the points of intersections are with in range.
That is the mathematical descriptions, unfortunately I have no code to do the above.
Another way to do the test which is slightly faster than using the separating axis test, is to use the winding numbers algorithm (on quadrants only - not angle-summation which is horrifically slow) on each vertex of either rectangle (arbitrarily chosen). If any of the vertices have a non-zero winding number, the two rectangles overlap.
This algorithm is somewhat more long-winded than the separating axis test, but is faster because it only require a half-plane test if edges are crossing two quadrants (as opposed to up to 32 tests using the separating axis method)
The algorithm has the further advantage that it can be used to test overlap of any polygon (convex or concave). As far as I know, the algorithm only works in 2D space.
Either I am missing something else why make this so complicated?
if (x1,y1) and (X1,Y1) are corners of the rectangles, then to find intersection do:
xIntersect = false;
yIntersect = false;
if (!(Math.min(x1, x2, x3, x4) > Math.max(X1, X2, X3, X4) || Math.max(x1, x2, x3, x4) < Math.min(X1, X2, X3, X4))) xIntersect = true;
if (!(Math.min(y1, y2, y3, y4) > Math.max(Y1, Y2, Y3, Y4) || Math.max(y1, y2, y3, y4) < Math.min(Y1, Y2, Y3, Y4))) yIntersect = true;
if (xIntersect && yIntersect) {alert("Intersect");}
I implemented it like this:
bool rectCollision(const CGRect &boundsA, const Matrix3x3 &mB, const CGRect &boundsB)
{
float Axmin = boundsA.origin.x;
float Axmax = Axmin + boundsA.size.width;
float Aymin = boundsA.origin.y;
float Aymax = Aymin + boundsA.size.height;
float Bxmin = boundsB.origin.x;
float Bxmax = Bxmin + boundsB.size.width;
float Bymin = boundsB.origin.y;
float Bymax = Bymin + boundsB.size.height;
// find location of B corners in A space
float B0x = mB(0,0) * Bxmin + mB(0,1) * Bymin + mB(0,2);
float B0y = mB(1,0) * Bxmin + mB(1,1) * Bymin + mB(1,2);
float B1x = mB(0,0) * Bxmax + mB(0,1) * Bymin + mB(0,2);
float B1y = mB(1,0) * Bxmax + mB(1,1) * Bymin + mB(1,2);
float B2x = mB(0,0) * Bxmin + mB(0,1) * Bymax + mB(0,2);
float B2y = mB(1,0) * Bxmin + mB(1,1) * Bymax + mB(1,2);
float B3x = mB(0,0) * Bxmax + mB(0,1) * Bymax + mB(0,2);
float B3y = mB(1,0) * Bxmax + mB(1,1) * Bymax + mB(1,2);
if(B0x<Axmin && B1x<Axmin && B2x<Axmin && B3x<Axmin)
return false;
if(B0x>Axmax && B1x>Axmax && B2x>Axmax && B3x>Axmax)
return false;
if(B0y<Aymin && B1y<Aymin && B2y<Aymin && B3y<Aymin)
return false;
if(B0y>Aymax && B1y>Aymax && B2y>Aymax && B3y>Aymax)
return false;
float det = mB(0,0)*mB(1,1) - mB(0,1)*mB(1,0);
float dx = mB(1,2)*mB(0,1) - mB(0,2)*mB(1,1);
float dy = mB(0,2)*mB(1,0) - mB(1,2)*mB(0,0);
// find location of A corners in B space
float A0x = (mB(1,1) * Axmin - mB(0,1) * Aymin + dx)/det;
float A0y = (-mB(1,0) * Axmin + mB(0,0) * Aymin + dy)/det;
float A1x = (mB(1,1) * Axmax - mB(0,1) * Aymin + dx)/det;
float A1y = (-mB(1,0) * Axmax + mB(0,0) * Aymin + dy)/det;
float A2x = (mB(1,1) * Axmin - mB(0,1) * Aymax + dx)/det;
float A2y = (-mB(1,0) * Axmin + mB(0,0) * Aymax + dy)/det;
float A3x = (mB(1,1) * Axmax - mB(0,1) * Aymax + dx)/det;
float A3y = (-mB(1,0) * Axmax + mB(0,0) * Aymax + dy)/det;
if(A0x<Bxmin && A1x<Bxmin && A2x<Bxmin && A3x<Bxmin)
return false;
if(A0x>Bxmax && A1x>Bxmax && A2x>Bxmax && A3x>Bxmax)
return false;
if(A0y<Bymin && A1y<Bymin && A2y<Bymin && A3y<Bymin)
return false;
if(A0y>Bymax && A1y>Bymax && A2y>Bymax && A3y>Bymax)
return false;
return true;
}
The matrix mB is any affine transform matrix that converts points in the B space to points in the A space. This includes simple rotation and translation, rotation plus scaling, and full affine warps, but not perspective warps.
It may not be as optimal as possible. Speed was not a huge concern. However it seems to work ok for me.
Here is a matlab implementation of the accepted answer:
function olap_flag = ol(A,B,sub)
%A and B should be 4 x 2 matrices containing the xy coordinates of the corners in clockwise order
if nargin == 2
olap_flag = ol(A,B,1) && ol(B,A,1);
return;
end
urdl = diff(A([1:4 1],:));
s = sum(urdl .* A, 2);
sdiff = B * urdl' - repmat(s,[1 4]);
olap_flag = ~any(max(sdiff)<0);
This is the conventional method, go line by line and check whether the lines are intersecting. This is the code in MATLAB.
C1 = [0, 0]; % Centre of rectangle 1 (x,y)
C2 = [1, 1]; % Centre of rectangle 2 (x,y)
W1 = 5; W2 = 3; % Widths of rectangles 1 and 2
H1 = 2; H2 = 3; % Heights of rectangles 1 and 2
% Define the corner points of the rectangles using the above
R1 = [C1(1) + [W1; W1; -W1; -W1]/2, C1(2) + [H1; -H1; -H1; H1]/2];
R2 = [C2(1) + [W2; W2; -W2; -W2]/2, C2(2) + [H2; -H2; -H2; H2]/2];
R1 = [R1 ; R1(1,:)] ;
R2 = [R2 ; R2(1,:)] ;
plot(R1(:,1),R1(:,2),'r')
hold on
plot(R2(:,1),R2(:,2),'b')
%% lines of Rectangles
L1 = [R1(1:end-1,:) R1(2:end,:)] ;
L2 = [R2(1:end-1,:) R2(2:end,:)] ;
%% GEt intersection points
P = zeros(2,[]) ;
count = 0 ;
for i = 1:4
line1 = reshape(L1(i,:),2,2) ;
for j = 1:4
line2 = reshape(L2(j,:),2,2) ;
point = InterX(line1,line2) ;
if ~isempty(point)
count = count+1 ;
P(:,count) = point ;
end
end
end
%%
if ~isempty(P)
fprintf('Given rectangles intersect at %d points:\n',size(P,2))
plot(P(1,:),P(2,:),'*k')
end
the function InterX can be downloaded from: https://in.mathworks.com/matlabcentral/fileexchange/22441-curve-intersections?focused=5165138&tab=function
I have a simplier method of my own, if we have 2 rectangles:
R1 = (min_x1, max_x1, min_y1, max_y1)
R2 = (min_x2, max_x2, min_y2, max_y2)
They overlap if and only if:
Overlap = (max_x1 > min_x2) and (max_x2 > min_x1) and (max_y1 > min_y2) and (max_y2 > min_y1)
You can do it for 3D boxes too, actually it works for any number of dimensions.
Enough has been said in other answers, so I'll just add pseudocode one-liner:
!(a.left > b.right || b.left > a.right || a.top > b.bottom || b.top > a.bottom);
Check if the center of mass of all the vertices of both rectangles lies within one of the rectangles.

Resources