Related
Im trying to find out the angle (in degrees) between two 2D vectors. I know I need to use trig but I'm not too good with it. This is what I'm trying to work out (the Y axis increases downward):
I'm trying to use this code at the moment, but it's not working at all (calculates random angles for some reason):
private float calcAngle(float x, float y, float x1, float y1)
{
float _angle = (float)Math.toDegrees(Math.atan2(Math.abs(x1-x), Math.abs(y1-y)));
Log.d("Angle","Angle: "+_angle+" x: "+x+" y: "+y+" x1: "+x1+" y1: "+y1);
return _angle;
}
These are my results (There constant when providing a constant position, but when I change the position, the angle changes and I can't find any link between the two angles):
Position 1:
x:100 y:100
x1:50 y1:50
Angle: 45
Position 2:
x:92 y:85
x1:24 y1:16
Angle: 44.58
Position 3:
x:44 y: 16
x1:106 y1:132
Angle: 28.12
Edit: Thanks everyone who answered and helped me figure out that was wrong! Sorry the title and the question was confusing.
You first have to understand how to compute angle between two vectors and there are several of them. I will give you what I think is the simplest.
Given v1 and v2, their dot product is: v1x * v2x + v1y * v2y
The norm of a vector v is given by: sqtr(vx^2+vy^2)
With this information, please take this definition:
dot(v1, v2) = norm(v1) * norm(v2) * cos(angle(v1, v2))
Now, you solve for angle(v1, v2):
angle(v1, v2) = acos( dot(v1, v2) / (norm(v1) * norm(v2)) )
Finally, taking the definitions given at the beginning, then you end up with:
angle(v1, v2) = acos( (v1x * v2x + v1y * v2y) / (sqrt(v1x^2+v1y^2) * sqrt(v2x^2+v2y^2)) )
Again, there are many ways to do this, but I like this one because it is helpful for dot product given angle and norm, or angle, given vectors.
The answer will be in radians, but you know that pi radians (that is 3.14 radians) are 180 degrees, so you simply multiply by the conversion factor 180/pi.
Aha! Turns out I just needed to flip my angle and use atan2. This is my final code:
private float calcAngle(float x, float y, float x1, float y1)
{
float _angle = (float)Math.toDegrees(Math.atan2(x1-x, y-y1));
return _angle;
}
Thanks everyone for helping me figure this out and also for helping me to understand what I'm actually doing! :)
Do not take the absolute value of the arguments to atan2. The whole point of atan2 is that it uses the signs of its arguments to work out which qaudrant the angle is in. By taking the absolute values you are forcing atan2 to only return values between 0 and pi/2 instead of -pi to pi.
It looks like Niall figured it out, but I'll finish my explanation, anyways. In addition to explaining why the solution works, my solution has two advantages:
Potential division by zero within atan2() is avoided
Return value is always positive in the range 0 to 360 degrees
atan2() returns the counter-clockwise angle relative to the positive X axis. Niall was looking for the clockwise angle relative to the positive Y axis (between the vector formed by the two points and the positve Y axis).
The following function is adapted from my asteroids game where I wanted to calculate the direction a ship/velocity vector was "pointing:"
// Calculate angle between vector from (x1,y1) to (x2,y2) & +Y axis in degrees.
// Essentially gives a compass reading, where N is 0 degrees and E is 90 degrees.
double bearing(double x1, double y1, double x2, double y2)
{
// x and y args to atan2() swapped to rotate resulting angle 90 degrees
// (Thus angle in respect to +Y axis instead of +X axis)
double angle = Math.toDegrees(atan2(x1 - x2, y2 - y1));
// Ensure result is in interval [0, 360)
// Subtract because positive degree angles go clockwise
return (360 - angle) % 360;
}
It should be :
atan( abs(x1 - x)/abs(y1 - y) )
abs stands for absolute (to avoid negative values)
I believe the equation for the angle between two vectors should look more like:
toDegrees(acos((x*x1+y*y1)/(sqrt(x*x+y*y)*sqrt(x1*x1+y1*y1))))
Your above equation will calculate the angle made between the vector p1-p2 and the line made by extending an orthogonal from the point p2 to the vector p1.
The dot product of two vectors V1 and V2 is equal to |V1|*|V2|cos(theta). Therefore, theta is equal to acos((V1 dot V2)/(|V1||V2|)). V1 dot V2 is V1.xV2.x+V1.yV2.y.
The magnitude of V (i.e., |V|) is the pathogorean theorem... sqrt(V.x^2 + V.y^2)
My first guess would be to calculate the angle of each vector with the axes using atan(y/x) and then subtract those angels and take the absolute value, that is:
abs(atan(y/x) - atan(y1/x1))
Are you using integers? Cast the arguments as doubles, and I would use fabs on the result, not the arguments. The result will be in radians; to get degrees, use:
res *= (360.0/(2.0*Math.PI));
The angle of the second vector relative to the first = atan2(y2,x2) - atan2(y1,x1).
http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/index.htm
I have a bit of a problem categorizing points based on relative normals.
What I would like to do is use the information I got below to fit a simplified polygon to the points, with a bias towards 90 degree angles to an extent.
I have the rough (although not very accurate) normal lines for each point, but I'm not sure how to separate the data base on closeness of points and closeness of the normals. I plan to do a linear regression after chunking the points for each face, as the normal lines sometimes does not fit well with the actual faces (although they are close to each other for each face)
Example:
alt text http://a.imageshack.us/img842/8439/ptnormals.png
Ideally, I would like to be able to fit a rectangle around this data. However, the polygon does not need to be convex, nor does it have to be aligned with the axis.
Any hints as to how to achieve something like this would be awesome.
Thanks in advance
I am not sure if this is what you are looking for, but here's my attempt at solving the problem as I understood it:
I am using the angles of the normal vectors to find points belonging to each side of the rectangle (left, right, up, down), then simply fit a line to each.
%# create random data (replace those with your actual data)
num = randi([10 20]);
pT = zeros(num,2);
pT(:,1) = rand(num,1);
pT(:,2) = ones(num,1) + 0.01*randn(num,1);
aT = 90 + 10*randn(num,1);
num = randi([10 20]);
pB = zeros(num,2);
pB(:,1) = rand(num,1);
pB(:,2) = zeros(num,1) + 0.01*randn(num,1);
aB = 270 + 10*randn(num,1);
num = randi([10 20]);
pR = zeros(num,2);
pR(:,1) = ones(num,1) + 0.01*randn(num,1);
pR(:,2) = rand(num,1);
aR = 0 + 10*randn(num,1);
num = randi([10 20]);
pL = zeros(num,2);
pL(:,1) = zeros(num,1) + 0.01*randn(num,1);
pL(:,2) = rand(num,1);
aL = 180 + 10*randn(num,1);
pts = [pT;pR;pB;pL]; %# x/y coords
angle = mod([aT;aR;aB;aL],360); %# angle in degrees [0,360]
%# plot points and normals
plot(pts(:,1), pts(:,2), 'o'), hold on
theta = angle * pi / 180;
quiver(pts(:,1), pts(:,2), cos(theta), sin(theta), 0.4, 'Color','g')
hold off
%# divide points based on angle
[~,bin] = histc(angle,[0 45 135 225 315 360]);
bin(bin==5) = 1; %# combine last and first bin
%# fit line to each segment
hold on
for i=1:4
%# indices of points in this segment
idx = ( bin == i );
%# x/y or y/x
if i==2||i==4, xx=1; yy=2; else xx=2; yy=1; end
%# fit line
coeff = polyfit(pts(idx,xx), pts(idx,yy), 1);
fit(:,1) = 0:0.05:1;
fit(:,2) = polyval(coeff, fit(:,1));
%# plot fitted line
plot(fit(:,xx), fit(:,yy), 'Color','r', 'LineWidth',2)
end
hold off
I'd try the following
Cluster the points based on proximity and similar angle. I'd use single-linkage hierarchical clustering (LINKAGE in Matlab), since you don't know a priori how many edges there will be. Single linkage favors linear structures, which is exactly what you're looking for. As the distance criterion between two points you can use the euclidean distance between point coordinates multiplied by a function of the angle that increases very steeply as soon as the angle differs more than, say, 20 or 30 degrees.
Do (robust) linear regression into the data. Using the normals may or may not help. My guess is that they won't help too much. For simplicity, you may want to disregard the normals initially.
Find the intersections between the lines.
If you have to, you can always try and improve the fit, for example by constraining opposite lines to be parallel.
If that fails, you could try and implement the approach in THIS PAPER, which allows fitting multiple straight lines at once.
You could get the mean value for the X and Y coordinates for each side and then just make lines based on that.
I'm using Electro in Lua for some 3D simulations, and I'm running in to something of a mathematical/algorithmic/physics snag.
I'm trying to figure out how I would find the "spin" of a sphere of a sphere that is spinning on some axis. By "spin" I mean a vector along the axis that the sphere is spinning on with a magnitude relative to the speed at which it is spinning. The reason I need this information is to be able to slow down the spin of the sphere by applying reverse torque to the sphere until it stops spinning.
The only information I have access to is the X, Y, and Z unit vectors relative to the sphere. That is, each frame, I can call three different functions, each of which returns a unit vector pointing in the direction of the sphere model's local X, Y and Z axes, respectively. I can keep track of how each of these change by essentially keeping the "previous" value of each vector and comparing it to the "new" value each frame. The question, then, is how would I use this information to determine the sphere's spin? I'm stumped.
Any help would be great. Thanks!
My first answer was wrong. This is my edited answer.
Your unit vectors X,Y,Z can be put together to form a 3x3 matrix:
A = [[x1 y1 z1],
[x2 y2 z2],
[x3 y3 z3]]
Since X,Y,Z change with time, A also changes with time.
A is a rotation matrix!
After all, if you let i=(1,0,0) be the unit vector along the x-axis, then
A i = X so A rotates i into X. Similarly, it rotates the y-axis into Y and the
z-axis into Z.
A is called the direction cosine matrix (DCM).
So using the DCM to Euler axis formula
Compute
theta = arccos((A_11 + A_22 + A_33 - 1)/2)
theta is the Euler angle of rotation.
The magnitude of the angular velocity, |w|, equals
w = d(theta)/dt ~= (theta(t+dt)-theta(t)) / dt
The axis of rotation is given by e = (e1,e2,e3) where
e1 = (A_32 - A_23)/(2 sin(theta))
e2 = (A_13 - A_31)/(2 sin(theta))
e3 = (A_21 - A_12)/(2 sin(theta))
I applaud ~unutbu's, answer, but I think there's a simpler approach that will suffice for this problem.
Take the X unit vector at three successive frames, and compare them to get two deltas:
deltaX1 = X2 - X1
deltaX2 = X3 - X2
(These are vector equations. X1 is a vector, the X vector at time 1, not a number.)
Now take the cross-product of the deltas and you'll get a vector in the direction of the rotation vector.
Now for the magnitude. The angle between the two deltas is the angle swept out in one time interval, so use the dot product:
dx1 = deltaX1/|deltaX1|
dx2 = deltax2/|deltaX2|
costheta = dx1.dx2
theta = acos(costheta)
w = theta/dt
For the sake of precision you should choose the unit vector (X, Y or Z) that changes the most.
I have the need to determine the bounding rectangle for a polygon at an arbitrary angle. This picture illustrates what I need to do:
alt text http://kevlar.net/RotatedBoundingRectangle.png
The pink rectangle is what I need to determine at various angles for simple 2d polygons.
Any solutions are much appreciated!
Edit:
Thanks for the answers, I got it working once I got the center points correct. You guys are awesome!
To get a bounding box with a certain angle, rotate the polygon the other way round by that angle. Then you can use the min/max x/y coordinates to get a simple bounding box and rotate that by the angle to get your final result.
From your comment it seems you have problems with getting the center point of the polygon. The center of a polygon should be the average of the coordinate sums of each point. So for points P1,...,PN, calculate:
xsum = p1.x + ... + pn.x;
ysum = p1.y + ... + pn.y;
xcenter = xsum / n;
ycenter = ysum / n;
To make this complete, I also add some formulas for the rotation involved. To rotate a point (x,y) around a center point (cx, cy), do the following:
// Translate center to (0,0)
xt = x - cx;
yt = y - cy;
// Rotate by angle alpha (make sure to convert alpha to radians if needed)
xr = xt * cos(alpha) - yt * sin(alpha);
yr = xt * sin(alpha) + yt * cos(alpha);
// Translate back to (cx, cy)
result.x = xr + cx;
result.y = yr + cx;
To get the smallest rectangle you should get the right angle. This can acomplished by an algorithm used in collision detection: oriented bounding boxes.
The basic steps:
Get all vertices cordinates
Build a covariance matrix
Find the eigenvalues
Project all the vertices in the eigenvalue space
Find max and min in every eigenvalue space.
For more information just google OBB "colision detection"
Ps: If you just project all vertices and find maximum and minimum you're making AABB (axis aligned bounding box). Its easier and requires less computational effort, but doesn't guarantee the minimum box.
I'm interpreting your question to mean "For a given 2D polygon, how do you calculate the position of a bounding rectangle for which the angle of orientation is predetermined?"
And I would do it by rotating the polygon against the angle of orientation, then use a simple search for its maximum and minimum points in the two cardinal directions using whatever search algorithm is appropriate for the structure the points of the polygon are stored in. (Simply put, you need to find the highest and lowest X values, and highest and lowest Y values.)
Then the minima and maxima define your rectangle.
You can do the same thing without rotating the polygon first, but your search for minimum and maximum points has to be more sophisticated.
To get a rectangle with minimal area enclosing a polygon, you can use a rotating calipers algorithm.
The key insight is that (unlike in your sample image, so I assume you don't actually require minimal area?), any such minimal rectangle is collinear with at least one edge of (the convex hull of) the polygon.
Here is a python implementation for the answer by #schnaader.
Given a pointset with coordinates x and y and the degree of the rectangle to bound those points, the function returns a point set with the four corners (and a repetition of the first corner).
def BoundingRectangleAnglePoints(x,y, alphadeg):
#convert to radians and reverse direction
alpha = np.radians(alphadeg)
#calculate center
cx = np.mean(x)
cy = np.mean(y)
#Translate center to (0,0)
xt = x - cx
yt = y - cy
#Rotate by angle alpha (make sure to convert alpha to radians if needed)
xr = xt * np.cos(alpha) - yt * np.sin(alpha)
yr = xt * np.sin(alpha) + yt * np.cos(alpha)
#Find the min and max in rotated space
minx_r = np.min(xr)
miny_r = np.min(yr)
maxx_r = np.max(xr)
maxy_r = np.max(yr)
#Set up the minimum and maximum points of the bounding rectangle
xbound_r = np.asarray([minx_r, minx_r, maxx_r, maxx_r,minx_r])
ybound_r = np.asarray([miny_r, maxy_r, maxy_r, miny_r,miny_r])
#Rotate and Translate back to (cx, cy)
xbound = (xbound_r * np.cos(-alpha) - ybound_r * np.sin(-alpha))+cx
ybound = (xbound_r * np.sin(-alpha) + ybound_r * np.cos(-alpha))+cy
return xbound, ybound
Given a point (pX, pY) and a circle with a known center (cX,cY) and radius (r), what is the shortest amount of code you can come up with to find the point on the circle closest to (pX, pY) ?
I've got some code kind of working but it involves converting the circle to an equation of the form (x - cX)^2 + (y - cY)^2 = r^2 (where r is radius) and using the equation of the line from point (pX, pY) to (cX, cY) to create a quadratic equation to be solved.
Once I iron out the bugs it'll do, but it seems such an inelegant solution.
where P is the point, C is the center, and R is the radius, in a suitable "mathy" language:
V = (P - C); Answer = C + V / |V| * R;
where |V| is length of V.
OK, OK
double vX = pX - cX;
double vY = pY - cY;
double magV = sqrt(vX*vX + vY*vY);
double aX = cX + vX / magV * R;
double aY = cY + vY / magV * R;
easy to extend to >2 dimensions.
i would make a line from the center to the point, and calc where that graph crosses the circle oO i think not so difficult
Solve it mathematically first, then translate into code. Remember that the shortest line between a point and the edge of a circle will also pass through its center (as stated by #litb).
The shortest distance point lies at the intersection of circumference and line passing through the center and the input point. Also center, input and output points lie on a straight line
let the center be (xc, yc) and shortest point from input (xi, yi) be (x,y) then
sqrt((xc-x)^2 + (yc-y)^2) = r
since center, input and output points lie on a straight line, slope calculated between
any of two of these points should be same.
(yc-yi)/(xc-xi) = (y-yc)/(x-xc)
4.solving equations 2&3 should give us the shortest point.
Trig functions, multiply by r, and add pX or pY as appropriate.
Treat the centre of the circular as your origin, convert the co-ordinates of (pX, pY) to polar co-ordinates, (theta, r') replace r' with the original circle's r and convert back to cartesian co-ordinates (and adjust for the origin).
You asked for the shortest code, so here it is. In four lines it can be done, although there is still a quadratic.
I've considered the point to be outside the circle.
I've not considered what happens if the point is directly above or below the circle center, that is cX=pX.
m=(cY-pY)/(cX-pX); //slope
b=cY-m*cX; //or Py-m*Px. Now you have a line in the form y=m*x+b
X=( (2mcY)*((-2*m*cY)^2-4*(cY^2+cX^2-b^2-2*b*cY-r^2)*(-1-m^2))^(1/2) )/(2*(cY^2+cX^2-b^2-2*bc*Y-r^2));
Y=mX+b;
1] Get an equation for a line connecting the point and the circle center.
2] Move along the line a distance of one radius from the center to find the point on the circle. That is: radius=a^2+b^2 which is: r=((cY-Y)+(cX-X))^(1/2)
3] Solve quadratically. X=quadratic_solver(r=((cY-Y)+(cX-X))^(1/2),X) which if you substitute in Y=m*X+b you get that hell above.
4] X and Y are your results on the circle.
I am rather certain I have made an error somewhere, please leave a comment if anyone finds something. Of course it is degenerate, one answer is furthest from your point and the other is closest.
Easy way to think about it in terms of a picture, and easy to turn into code: Take the vector (pX - cX, pY - cY) from the center to the point. Divide by its length sqrt(blah blah blah), multiply by radius. Add this to (cX, cY).
Here is a simple method I use in unity... for the math kn00bs amongst us.
Its dependent on the transform orientation but it works nicely. I am doing a postion.z = 0 but just fatten the axis of the 2d circle you are not using.
//Find closest point on circle
Vector3 closestPoint = transform.InverseTransformPoint(m_testPosition.position);
closestPoint.z = 0;
closestPoint = closestPoint.normalized * m_radius;
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(transform.TransformPoint(closestPoint), 0.01f);