Reputation: 4302
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?
Upvotes: 2
Views: 2576
Reputation: 316
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;
}
Upvotes: 3