Calculate rotated rectangle transform using bounding box coordinates - math

I have a Red container rotated by -13 degrees, inside this container there is a Pink Square also rotated by -13 degrees.
Using only these informations below I'm trying to find the pink square transform relative to the origin (top,left) (0,0)
The relative transform coordinate is how much I need to translate inside the parent. And the bounding box is just the size with rotation included (it's the black box on the screenshot)
Pink Square
size before rotation
height : 398
width : 398
size after rotation
height : 477
width : 477
Bounding box
x : 179
y : 230
Relative transform to parent
x : 0
y : 49
Rotation
-13 deg
Red Container
size before rotation
height : 632
width : 447
size after rotation
height : 716
width : 577
Bounding box
x : 179
y : 182.28
Relative transform to parent
x : 279
y : 182
Rotation
-13 deg
Here is what I tried to do
yCoordinate = pink.relativeTransform.y + redContainer.boundingBox.y
xCoordinate = pink.relativeTransform.x + redContainer.boundingBox.x
I managed to get the yCoordinate right but I can't get the x coordinate also I'm worried that this will works for all angles

If you represent the transforms as matrices, you will get the answer pretty easily (note that I will use the word transform to denote the entire transformation, including rotations, and not just the offset vector). Btw, your image shows a rotation in positive direction (in a mathematical sense), so I will assume that it is actually +13°.
To get the transformation matrix for a rotation of angle phi and offset vector (tx, ty), we can employ the following form:
/ cos(phi) -sin(phi) tx \
T = | sin(phi) cos(phi) ty |
\ 0 0 1 /
Hence, the transformation of the red rectangle with respect to the origin would be:
/ 0.974 -0.225 279 \
TRed = | 0.225 0.974 182 |
\ 0 0 1 /
The transformation of the pink square with respect to the red rectangle would be (no rotation relative to parent, just a translation):
/ 1 0 0 \
TPink = | 0 1 49 |
\ 0 0 1 /
To get the transformation of the pink square with respect to the origin, we just multiply the two matrices:
/ 0.974 0.225 267.977 \
TRed * TPink = | 0.225 0.974 229.744 |
\ 0 0 1 /
We can see that the first part is the same rotation as in TRed, i.e., a rotation by 13°. The translation (which is the vector you are looking for) is (267.977, 229.744).
In general, this translation vector is:
/ cos(phi) * tPinkX - sin(phi) * tPinkY + tRedX \
\ sin(phi) * tPinkX + cos(phi) * tPinkY + tRedY /

Related

Wrong graph when trying to rotate coordinates

Consider the following toy data:
clear
input double x1 float y1
0 0
.0013440860215053765 .02503477
.0013440860215053765 .05006954
.005376344086021506 .0751043
.009408602150537635 .10013908
.01747311827956989 .12482615
.03225806451612903 .1498609
.056451612903225805 .1748957
.07661290322580645 .19993046
.09946236559139784 .22496523
.15725806451612903 .25
.2110215053763441 .2750348
.32661290322580644 .3000695
.3803763440860215 .3251043
.4986559139784946 .3497914
.603494623655914 .3748261
.706989247311828 .3998609
.7661290322580645 .4248957
.8064516129032258 .4499305
.885752688172043 .4749652
.9099462365591398 .5
1 .5250348
.9811827956989247 .5500696
.8870967741935484 .5751043
.7661290322580645 .5997913
.6599462365591398 .6248261
.5873655913978495 .6498609
.5282258064516129 .6748957
.40053763440860213 .6999304
.3279569892473118 .7249652
.2163978494623656 .75
.15053763440860216 .7750348
.09408602150537634 .8000696
.06586021505376344 .8247566
.04973118279569892 .8497913
.024193548387096774 .8748261
.025537634408602152 .8998609
.006720430107526882 .9248957
.002688172043010753 .9499304
.004032258064516129 .9749652
0 1
end
twoway scatter y1 x1
When I try to rotate the entire graph by say 20 degrees counter-clockwise:
local theta = 0.349066
generate x2 = (x1 * cos(`theta') ) - (y1 * sin(`theta') )
generate y2 = (x1 * sin(`theta') ) - (y1 * cos(`theta') )
The coordinates transform as follows:
clear
input float(x2 y2)
0 0
-.007299372 -.023065284
-.01586177 -.04659027
-.020635087 -.06873614
-.025408404 -.09088202
-.026273714 -.11132205
-.02094281 -.12979028
-.006770712 -.14504059
.0036123034 -.16167
.016521374 -.17738
.06226916 -.1811377
.10422786 -.1862745
.20428585 -.1702649
.24624455 -.1754017
.3489475 -.1581459
.4389013 -.14581393
.527592 -.13394167
.57460284 -.13723963
.6039312 -.1469735
.6698875 -.1433759
.6840596 -.1586262
.76012 -.151351
.7338752 -.18131188
.6369009 -.23701614
.51478493 -.3015878
.4064434 -.3614295
.3296775 -.4097785
.26554185 -.45353055
.13699183 -.52072746
.06022594 -.5690765
-.05316776 -.630757
-.12361852 -.6768075
-.1852281 -.7196401
-.22019514 -.7524921
-.24391386 -.7815335
-.2764738 -.8137929
-.28377315 -.8368582
-.3100179 -.8668191
-.3223694 -.8917232
-.3296688 -.9147884
-.3420203 -.9396926
end
twoway scatter y2 x2
What am I missing?
Note that I have also tried to center the values first around a specific point.
In addition, I would also like the solution to account for different axes scales and graph aspect ratio.
For example:
clear
input float y double x
-2013 .001
-1941 .0010053763440860215
-1869 .0010053763440860215
-1797 .0010215053763440861
-1725 .0010376344086021505
-1654 .0010698924731182796
-1582 .0011290322580645162
-1510 .0012258064516129032
-1438 .0013064516129032257
-1366 .0013978494623655914
-1294 .0016290322580645162
-1222 .0018440860215053765
-1150 .0023064516129032257
-1078 .0025215053763440864
-1007 .0029946236559139786
-935 .003413978494623656
-863 .003827956989247312
-791 .004064516129032258
-719 .004225806451612904
-647 .004543010752688172
-575 .004639784946236559
-503 .005
-431 .0049247311827956995
-359 .004548387096774194
-288 .004064516129032258
-216 .0036397849462365592
-144 .003349462365591398
-72 .0031129032258064514
0 .0026021505376344085
72 .002311827956989247
144 .0018655913978494624
216 .0016021505376344087
288 .0013763440860215053
359 .0012634408602150537
431 .0011989247311827958
503 .0010967741935483872
575 .0011021505376344087
647 .0010268817204301076
719 .001010752688172043
791 .0010161290322580644
863 .001
end
twoway scatter y x
The y-xis of this graph is 4 inches while the x-axis is 5.5 inches (aspect ratio of 1.375).
I have consulted a number of posts including the following:
Programmatically rotate shapes using coordinates
How to rotate coordinate system?
I hope what I am trying to do is clear but I will be happy to clarify further.
The formulas for rotation are the following:
generate x2 = (x1 * cos(`theta') ) - (y1 * sin(`theta') )
generate y2 = (x1 * sin(`theta') ) + (y1 * cos(`theta') )
These perform rotation about point (0,0).
To make rotation about specific center point (cx, cy), one can apply the next approach:
generate x2 = cx + ((x1 - cx) * cos(`theta') ) - ((y1 - cy) * sin(`theta') )
generate y2 = cy + ((x1 - cx) * sin(`theta') ) + ((y1 - cy) * cos(`theta') )
The above formulas represent affine transformation matrix. To account for axes scale you have to multiply result matrix by scaling matrix - it is very simple, just multiply x or y by coefficient according to axis/axis ratio.
But seems you want to rotate already stretched visual representation. Say your plot is stretched along OX in 5 times. In this case at first multiply internal data x-coordinates by 5, make rotation (note - scale rotation center too), then divide by 5.
For your first example the x-axis is ~1.5 times longer. So we can multiply x-column by 1.5, rotate by 20 degrees, and divide by 1.5. If axes preserve their length, we should see the same plot rotated by 20 degrees. However, data ranges have changed, and plot is resized! Angle is not 20 degrees exactly. This effect will be more evident on the second example with huge axes scale difference.
If ranges remain the same after rotation, I expect that described approach should give correct angles. It might be checked with simple pixel plotting, but I'm afraid this simulation won't reproduce behavior of your plotting system. With automatic axes ranges, exact angles are not possible.

Simple 2D graph drawing - PostScript

Can you tell me an easy way to draw graph(2+x, sin(x), cos(x+3)/3.....) in PS format?
For example, I want to draw f(x) = 2+x, with the following values:
Table of values:
Value of X = -5 | -4 | -3 | -2 | -1 | -0 | 1 .....
Value of Y = -3 | -2 | -1 | 0 | 1 | 2 | 3 .....
How to draw this graph? Draw lineto, draw polygon or use curve command?
What do you think is the best solution?
There are a number of different ways you can do this. If you have a set of coordinates to plot, you can put them in an array, and draw the points while iterating through the array.
/XValues [ -5 -4 -3 -2 -1 0 1 ] def % eg. XValues 0 get ==> -5
/YValues [ -3 -2 -1 0 1 2 3 ] def % YValues 0 get ==> -3
XValues 0 get YValues 0 get % X[0] Y[0]
moveto % move to first point
1 1 XValues length 1 sub { % i push integer i = 1 .. length(XValues)-1 on each iteration
XValues % i XVal push X array
1 index % i XVal i copy i from stack
get % i x get ith X value from array
YValues % i x YVal
2 index % i x YVal i i is 1 position deeper now, so 2 index instead of 1
get % i x y
lineto % i line to next point
pop % discard index variable
} for
Now, of course in Postscript the origin is at the bottom left corner by default, and 72 points make an inch. So these values (-5, -4, -2, etc) are not even going to be visible. So you usually want to start by translating to the center of where you want to draw the graph.
/Center { 300 400 } def % roughly the middle of US letter-size paper
Center translate
Then you want to scale the coordinate system so the graph features are visible. Scalefactor = DesiredSize / ExistingSize.
You could scan the dataset to find the existing size.
/Xmin 1000000 def % really high
/Xmax -1000000 def % really low
XValues { % x forall pushes each X value
dup Xmin lt { % x lower than Xmin?
dup /Xmin exch def % x set Xmin
} if % x
dup Xmax gt { % x higher than Xmax?
/Xmax exch def % set Xmax
}{ % x else (lower than Xmax)
pop % discard X value
} ifelse
} forall
/Datasize Xmax Xmin sub def % X size is (Xmax-Xmin)
6 72 mul DataSize div % scalefactor 6*72/(Xmax-Xmin)
dup % scalefactor scalefactor use same scaling for x and y
scale
But there's a snag when you're doing line-drawing. The width of the lines you draw also depend upon the current coordinate space, so if you scale up the space by a large factor, your lines will become undesirably wide. You can either unscale back to the normal space after you describe the path but before you call stroke. Or, fix the linewidth at the same time you scale.
Since we know how much we've increased the line width (it's the same scaling factor), we can adjust the linewidth graphics parameter in the inverse direction.
1 % push 1 for division
6 72 mul DataSize div % 1 scalefactor 6*72/(Xmax-Xmin)
dup dup % 1 scalefactor scalefactor scalefactor
scale % 1 scalefactor
div % 1/scalefactor
currentlinewidth mul setlinewidth % adjust line width
Now, since this is a graph of a function, we don't actually need the tables of values. We can calculate values on the fly by evaluating the function.
/func { 3 add cos 3 div } def % f(x) = cos(3+x)/3 define function y=f(x)
/Xini -5 def % named loop control parameters
/Xfin 1 def
/Xstep 1 def
Xini dup dup func moveto % moveto first point
Xstep add Xstep Xfin { % x
dup % x x
func % x f(x)
lineto % line to next point
} for
stroke
Finally, if you can take the derivative of the function (create a function which calculates the slope of the original function at each point), then you can use my answer over on TeX.SE to draw the graph with many curve segments instead of lines.

pixel coordinates on diamond

I got an image with a couple of diamond put side by side like on the image below
The only coordinates I know on the image are the top corners (green text).
When I click on the image I get the coordinates of that point, but I'm not able to get which diamond I'm on.
For example I click on the red dot, how do I know that x:260, y:179 = the top diamond ?
And the blue belongs to the left ? etc...
Thank you very much for your help.
EDIT:
I finally used Canvas, but I think SVG would have worked as well for what I needed to do.
I see two possible approaches: direct check whether a point is inside a diamond and using affine transformations. I will describe both.
Direct point position check
To determine whether a point is inside a diamond you have to check its deviation from the middle point of a diamond. You have to put the X and Y deviations in proportion with the X and Y extents of the diamond, you will get two factors. For all points inside the diamond the sum of the modulo values for these factors is smaller or equal 1. In code this looks like this:
var dx = Math.abs(coords[0] - middle[0]);
var dy = Math.abs(coords[1] - middle[1]);
if (dx / size[0] + dy / size[1] <= 1)
alert("Inside diamond");
else
alert("Outside diamond");
So all you have to do now is determining the middle point for each diamond (size is the same in all cases) and checking whether the point you are testing is located inside them.
Working example: http://jsfiddle.net/z98hr/
Affine transformations
Using affine transformations you can change the corner coordinates of your top diamond into (0,0), (1,0), (0,1) and (1,1). If you then apply the same transformation to the point you need to test, determining which diamond it belongs to becomes trivial.
First you will need a translation vector to move the (225,2) point into the origin of coordinates. Let's say that you have four coordinates determining your top diamond (left and right coordinate, top and bottom coordinate):
var topDiamond = [[113, 2], [337, 227]];
Then the translation vector to move the top point of the diamond to the zero coordinate would be:
var translationVector = [-(topDiamond[0][0] + topDiamond[1][0]) / 2,
-topDiamond[0][1]];
You can apply it to the original coordinates like this:
function add(vector1, vector2)
{
return [vector1[0] + vector2[0], vector1[1] + vector2[1]];
}
topDiamond = [add(topDiamond[0], translationVector),
add(topDiamond[1], translationVector)];
Then you will need a rotation matrix:
var angle = -Math.atan2(topDiamond[1][1] - topDiamond[0][1],
topDiamond[1][0] - topDiamond[0][0]);
var rotMatrix = [[Math.cos(angle), -Math.sin(angle)],
[Math.sin(angle), Math.cos(angle)]];
After the multiplication with this matrix the points (225,2) and (337,114.5) are aligned on the X axis. But what you have now is a trapeze, you now need a horizontal shear transformation to get the other side of the diamond aligned on the Y axis:
function multiply(matrix, vector)
{
return [matrix[0][0] * vector[0] + matrix[0][1] * vector[1],
matrix[1][0] * vector[0] + matrix[1][1] * vector[1]];
}
var point = [topDiamond[0][0], (topDiamond[0][1] + topDiamond[1][1]) / 2];
point = multiply(rotMatrix, point);
var shearMatrix = [[1, -point[0] / point[1]], [0, 1]];
After multiplication with this matrix you have a rectangle now. Now you only need a scaling matrix to make sure that the X and Y coordinates of the corners have the value 0 and 1:
point = multiply(shearMatrix, point);
var point2 = [topDiamond[1][0], (topDiamond[0][1] + topDiamond[1][1]) / 2];
point2 = multiply(rotMatrix, point2);
point2 = multiply(shearMatrix, point2);
var scaleMatrix = [[1/point2[0], 0], [0, 1/point[1]]];
And there you have it, now you can apply these transformations to any point:
alert(
multiply(scaleMatrix,
multiply(shearMatrix,
multiply(rotMatrix,
add(translationVector, [260, 179])
)
)
)
);
This gives you 0.94,0.63 - both values are in the (0..1) range meaning that it is the top diamond. With [420,230] as input you get 1.88,0.14 - X in (1..2) range and Y in 0..1 range means right diamond. And so on.
Working example: http://jsfiddle.net/FzWHe/
In the retrospective, this was probably too much work for a simple geometrical figure like a diamond.
Essentially, what you have there is possibly an isometric view of 4 tiles (based on your comment about the diamonds appearing as trapezoids).
One quick way of doing this is to create 2 lines that are parallel with the "axes" of the "diamonds" (but still are crossing with each other...this is important as well). In the example image given, that would mean two lines that are vertical to each other but rotated by 45 degrees. In the isometric case, the lines will not be vertical to each other but at some other angle depending on your view.
Once you have these two lines you can create a "hitTest()" function that will be taking the coordinates of the point that was clicked and will be evaluating the two line equations. You are not really interested on the actual number returned by the line equations but only the signs. The sign shows you which side of the line does your point resides.
This means that your "diamonds" will correspond to these sign pairs (one sign for each line equation) [-,-], [-,+], [+,-], [+,+].
(Please note that the sign depends on the way that the line was defined, in other words for a given point P, the sign from some line equation (L) will be different if the line was defined as running "from left to right" or "from right to left", or more generally the sign will be the reverse for reciprocal directions.)
A bit more information about the form of the line equation you need can be obtained from here
Using matrices, you can derive a quick formula for which diamond is selected.
You want a transformation from (x,y) into "diamond-space". That is, a coordinate system where (0,0) is the top diamond, (1,0) is the one below to the right, and (0,1) below to the left.
A * x = y
where A is the transformation, x is the image coordinates, and y is the diamond-coordinates. To deal with the translation ((0,0) not being the same point in both spaces), you can add another row to the vectors, which is always 1.
You can transform multiple vectors at the same time, by putting them beside each other, so they form a matrix.
[ a b dx ] [ 225 337 113 ] [ 0 1 0 ]
[ c d dy ] * [ 2 114 114 ] = [ 0 0 1 ]
[ 0 0 1 ] [ 1 1 1 ] [ 1 1 1 ]
^ ^ ^-left ^-^-^--- new coordinates for each point
| '-right
'-top diamond
To solve for the coefficients in the first matrix, you need to divide by the second matrix (or multiply by the inverse).
[ a b dx ] [ 0 1 0 ] [ 225 337 113 ]^-1
[ c d dy ] = [ 0 0 1 ] * [ 2 114 114 ]
[ 0 0 1 ] [ 1 1 1 ] [ 1 1 1 ]
The result is:
[ a b dx ] [ (1/224) (1/224) (-227/224) ]
[ c d dy ] = [ (-1/224) (1/224) (223/224) ]
[ 0 0 1 ] [ 0 0 1 ]
To put this into program code:
function getDiamond(x, y) {
return [(x + y - 227) / 224, (-x + y + 223) / 224];
}
Example:
> getDiamond(260,179); // red
[0.9464285714285714, 0.6339285714285714]
> getDiamond(250,230); // green
[1.1294642857142858, 0.90625]
> getDiamond(189,250); // blue
[0.9464285714285714, 1.2678571428571428]
> getDiamond(420,230); // yellow
[1.8883928571428572, 0.14732142857142858]
If you look at the integer parts, you can see which diamond the coordinate corresponds to. The red one is at (0.94, 0.63) which is in region (0,0) pretty close to the edge of (1,0).
NB. The blue and green points in OP is drawn in the wrong location (or given wrong coordinates), so the result of my function places them in a different relative location.
If you do the calculations symbolically, you end up with this:
[ a b dx ] [ (y2 - y0)/M -(x2 - x0)/M -(x0*y2 - y0*x2)/M ]
[ c d dy ] = [-(y1 - y0)/M (x1 - x0)/M (x0*y1 - y0*x1)/M ]
[ 0 0 1 ] [ 0 0 1 ]
where M = x1*y2 - x2*y1 - y0*x1 + y0*x2 + x0*y1 - x0*y2.
Point 0 being the position of top diamond, point 1 being the position of right diamond, and point 2 being the position of left diamond.
Here is a function to calculate this:
function DiamondMaker(topx,topy, leftx,lefty, rightx,righty)
{
var M = topx*lefty - topx*righty +
leftx*righty - leftx*topy +
rightx*topy - rightx*lefty;
var a = -(topy - righty)/M;
var b = (topx - rightx)/M;
var dx = -(topx*righty - topy*rightx)/M;
var c = (topy - lefty)/M;
var d = -(topx - leftx)/M;
var dy = (topx*lefty - topy*leftx)/M;
return function(x, y) {
return [a * x + b * y + dx, c * x + d * y + dy];
};
}
var getDiamond = DiamondMaker(225,2, 337,114, 113,114);
// (same example as before)
All you need - just stady what is roration. Here is link: http://en.wikipedia.org/wiki/Rotation_(mathematics)
You should rotate you point in order to make sides of squares in parrallel with coordinate's grid. Point of rotaion should be 1 corner of dimonds you will threat as 0,0 diamond. After rotaion you can easily define how many daimond you point away from 0,0

How do I remove axis from a rotation matrix?

I have an opengl arbitrary rotation matrix and would like to remove the X & Y axis, leaving me with only the Z axis?
Is this possible? Any pointers on how to do it?
Just thinking out loud here, but can't you use the matrix to rotate a vector like (1,0,0), then do atan2(y,x) to see how much it's rotated and then build a new matrix to rotate through the Z axis by that much?
In a rotation that is only around the z-axis, the z axis should remain unchanged. So the above recommendation is sort of the reverse of what you want.
Let's assume you have an arbitrary OpenGL matrix:
| r_xx r_xy r_xz t_x |
| r_yx r_yy r_yz t_y |
M = | r_zx r_zy r_zz t_z |
| 0 0 0 1 |
Where the t_i elements are translations and the r_jk elements are components of rotation. You want a matrix that looks like this:
| cos(th) sin(th) 0 t_x |
|-sin(th) cos(th) 0 t_y |
| 0 0 1 t_z |
| 0 0 0 1 |
Unless the matrix has scaling factors or is close to a singularity, you should be able to get this by just zeroing out the z parts of the matrix and then re-normalizing the columns. Since an OpenGL matrix is column major order:
double xLen = sqrt(M[0]*M[0] + M[1]*M[1]); // Singularity if either of these
double yLen = sqrt(M[4]*M[4] + M[5]*M[5]); // is equal to zero.
M[0]/=xLen; M[1]/=xLen; M[2]=0; // Set the x column
M[4]/=yLen; M[5]/=yLen; M[6]=0; // Set the y column
M[8]=0; M[9]=0; M[10]=1; // Set the z column
//Don't change the translation column

How to map atan2() to degrees 0-360

atan2(y, x) has that discontinuity at 180° where it switches to -180°..0° going clockwise.
How do I map the range of values to 0°..360°?
here is my code:
CGSize deltaPoint = CGSizeMake(endPoint.x - startPoint.x, endPoint.y - startPoint.y);
float swipeBearing = atan2f(deltaPoint.height, deltaPoint.width);
I'm calculating the direction of a swiping touch event given the startPoint and endPoint, both XY point structs. The code is for the iPhone but any language that supports atan2f() will do.
Solution using Modulo
A simple solution that catches all cases.
degrees = (degrees + 360) % 360; // +360 for implementations where mod returns negative numbers
Explanation
Positive: 1 to 180
If you mod any positive number between 1 and 180 by 360, you will get the exact same number you put in. Mod here just ensures these positive numbers are returned as the same value.
Negative: -180 to -1
Using mod here will return values in the range of 180 and 359 degrees.
Special cases: 0 and 360
Using mod means that 0 is returned, making this a safe 0-359 degrees solution.
(x > 0 ? x : (2*PI + x)) * 360 / (2*PI)
Add 360° if the answer from atan2 is less than 0°.
Or if you don't like branching, negate the two parameters and add 180° to the answer.
(Adding 180° to the return value puts it nicely in the 0-360 range, but flips the angle. Negating both input parameters flips it back.)
#erikkallen is close but not quite right.
theta_rad = atan2(y,x);
theta_deg = (theta_rad/M_PI*180) + (theta_rad > 0 ? 0 : 360);
This should work in C++: (depending on how fmod is implemented, it may be faster or slower than the conditional expression)
theta_deg = fmod(atan2(y,x)/M_PI*180,360);
Alternatively you could do this:
theta_deg = atan2(-y,-x)/M_PI*180 + 180;
since (x,y) and (-x,-y) differ in angles by 180 degrees.
I have 2 solutions that seem to work for all combinations of positive and negative x and y.
1) Abuse atan2()
According to the docs atan2 takes parameters y and x in that order. However if you reverse them you can do the following:
double radians = std::atan2(x, y);
double degrees = radians * 180 / M_PI;
if (radians < 0)
{
degrees += 360;
}
2) Use atan2() correctly and convert afterwards
double degrees = std::atan2(y, x) * 180 / M_PI;
if (degrees > 90)
{
degrees = 450 - degrees;
}
else
{
degrees = 90 - degrees;
}
#Jason S: your "fmod" variant will not work on a standards-compliant implementation. The C standard is explicit and clear (7.12.10.1, "the fmod functions"):
if y is nonzero, the result has the same sign as x
thus,
fmod(atan2(y,x)/M_PI*180,360)
is actually just a verbose rewriting of:
atan2(y,x)/M_PI*180
Your third suggestion, however, is spot on.
Here's some javascript. Just input x and y values.
var angle = (Math.atan2(x,y) * (180/Math.PI) + 360) % 360;
This is what I normally do:
float rads = atan2(y, x);
if (y < 0) rads = M_PI*2.f + rads;
float degrees = rads*180.f/M_PI;
An alternative solution is to use the mod () function defined as:
function mod(a, b) {return a - Math.floor (a / b) * b;}
Then, with the following function, the angle between ini(x,y) and end(x,y) points is obtained. The angle is expressed in degrees normalized to [0, 360] deg. and North referencing 360 deg.
function angleInDegrees(ini, end) {
var radian = Math.atan2((end.y - ini.y), (end.x - ini.x));//radian [-PI,PI]
return mod(radian * 180 / Math.PI + 90, 360);
}
angle = Math.atan2(x,y)*180/Math.PI;
I have made a Formula for orienting angle into 0 to 360
angle + Math.ceil( -angle / 360 ) * 360;
double degree = fmodf((atan2(x, y) * (180.0 / M_PI)) + 360, 360);
This will return degree from 0°-360° counter-clockwise, 0° is at 3 o'clock.
A formula to have the range of values from 0 to 360 degrees.
f(x,y)=180-90*(1+sign(x))* (1-sign(y^2))-45*(2+sign(x))*sign(y)
-(180/pi())*sign(x*y)*atan((abs(x)-abs(y))/(abs(x)+abs(y)))
The R packages geosphere will calculate bearingRhumb, which is a constant bearing line given an origin point and easting/northing. The easting and northing must be in a matrix or vector. The origin point for a wind rose is 0,0. The following code seems to readily resolve the issue:
windE<-wind$uasE
windN<-wind$vasN
wind_matrix<-cbind(windE, windN)
wind$wind_dir<-bearingRhumb(c(0,0), wind_matrix)
wind$wind_dir<-round(wind$wind_dir, 0)
theta_rad = Math.Atan2(y,x);
if(theta_rad < 0)
theta_rad = theta_rad + 2 * Math.PI; //if neg., add 2 PI to it
theta_deg = (theta_rad/M_PI*180) ; //convert from radian to degree
//or
theta_rad = Math.Atan2(y,x);
theta_rad = (theta_rad < 0) ? theta_rad + 2 * Math.PI : theta_rad;
theta_deg = (theta_rad/M_PI*180) ;
-1 deg becomes (-1 + 360) = 359 deg
-179 deg becomes (-179 + 360) = 181 deg
For your application I suspect you don't need exact degrees and would prefer a more approximate compass angle, eg 1 of 16 directions? If so then this code avoids atan issues and indeed avoids floating point altogether. It was written for a video game so uses 8 bit and 16 bit integers:
/*
349.75d 11.25d, tan=0.2034523
\ /
\ Sector /
\ 0 / 22.5d tan = ?2 - 1
15 | 1 33.75
| / 45d, tan = 1
14 | 2 _56.25
| / 67.5d, tan = 1 + ?2
13 | 3
| __ 78.75
|
12---------------+----------------4 90d tan = infty
| __ 101.25
|
11 | 5
|
10 | 6
|
9 | 7
8
*/
// use signs to map sectors:
static const int8_t map[4][5] = { /* +n means n >= 0, -n means n < 0 */
/* 0: +x +y */ {0, 1, 2, 3, 4},
/* 1: +x -y */ {8, 7, 6, 5, 4},
/* 2: -x +y */ {0, 15, 14, 13, 12},
/* 3: -x -y */ {8, 9, 10, 11, 12}
};
int8_t sector(int8_t x, int8_t y) { // x,y signed in range -128:127, result 0:15 from north, clockwise.
int16_t tangent; // 16 bits
int8_t quadrant = 0;
if (x > 0) x = -x; else quadrant |= 2; // make both negative avoids issue with negating -128
if (y > 0) y = -y; else quadrant |= 1;
if (y != 0) {
// The primary cost of this algorithm is five 16-bit multiplies.
tangent = (int16_t)x*32; // worst case y = 1, tangent = 255*32 so fits in 2 bytes.
/*
determine base sector using abs(x)/abs(y).
in segment:
0 if 0 <= x/y < tan 11.25 -- centered around 0 N
1 if tan 11.25 <= x/y < tan 33.75 -- 22.5 NxNE
2 if tan 33.75 <= x/y < tan 56.25 -- 45 NE
3 if tan 56.25 <= x/y < tan 78.75 -- 67.5 ExNE
4 if tan 78.75 <= x/y < tan 90 -- 90 E
*/
if (tangent > y*6 ) return map[quadrant][0]; // tan(11.25)*32
if (tangent > y*21 ) return map[quadrant][1]; // tan(33.75)*32
if (tangent > y*47 ) return map[quadrant][2]; // tan(56.25)*32
if (tangent > y*160) return map[quadrant][3]; // tan(78.75)*32
// last case is the potentially infinite tan(90) but we don't need to check that limit.
}
return map[quadrant][4];
}

Resources