Is there a fast way to calculate the smallest delta between two rotation values? - iphone

There are two views:
viewA and viewB. Both are rotated.
The coordinate system for rotation is weird: It goes from 0 to 179,999999 or -179,99999 degrees. So essentially 179,99999 and -179,99999 are very close together!
I want to calculate how much degrees or radians are between these rotations.
For example:
viewA is rotated at 20 degrees
viewB is rotated at 30 degrees
I could just do: rotationB - rotationA = 10.
But the problem with this formula:
viewA is rotated at 179 degrees
viewB is rotated at -179 degrees
that would go wrong: rotationB - rotationA = -179 - 179 = -358
358 is plain wrong, because they are very close together in reality. So one thing I could do maybe is to check if the absolute result value is bigger than 180, and if so, calculate it the other way around to get the short true delta. But I feel this is plain wrong and bad, because of possible floating point errors and unprecision. So if two views are rotated essentially equally at 179,99999999999 degrees I might get a weird 180 or a 0 if I am lucky.
Maybe there's a genius-style math formular with PI, sine or other useful stuff to get around this problem?

EDIT: Original answer (with Mod) was wrong. would have given 180 - right answer in certain circumstances (angles 30 and -20 for example would give answer of 130, not correct answer of 50):
Two correct answers for all scenarios:
If A1 and A2 are two angles (between -179.99999 and 179.99999,
and Abs means take the Absolute Value,
The angular distance between them, is expressed by:
Angle between = 180 - Abs(Abs(A1 - A2) - 180)
Or, using C-style ternary operator:
Angle between = A1 < 180 + A2? A1 - A2: 360 + A1 - A2

Judging from the recent questions you've asked, you might want to read up on the unit circle. This is a fundamental concept in trigonometry, and it is how angles are calculated when doing rotations using CGAffineTransforms or CATransform3Ds.
Basically, the unit circle goes from 0 to 360 degrees, or 0 to 2 * pi (M_PI is the constant used on the iPhone) radians. Any angle greater than 360 degrees is the same as that angle minus a multiple of 360 degrees. For example, 740 degrees is the same as 380 degrees, which is the same as 20 degrees, when it comes to the ending position of something rotated by that much.
Likewise, negative degrees are the same as if you'd added a multiple of 360 degrees to them. -20 degrees is the same as 340 degrees.
There's no magic behind any of these calculations, you just have to pay attention to when something crosses the 0 / 360 degree point on the circle. In the case you describe, you can add 360 to any negative values to express them in positive angles. When subtracting angles, if the ending angle is less than the starting angle, you may also need to add 360 to the result to account for crossing the zero point on the unit circle.

Let's try this again:
There are two angles between A and B. One of them is
θ1 = A - B
The other is
θ2 = 360 - θ1
So just take the minimum of those two.

In addition to Brad Larson's excellent answer I would add that you can do:
CGFloat adjustAngle(angle) { return fmod(angle + 180.0, 360.0); }
...
CGFloat difference = fmod(adjustAngle(angle1) - adjustAngle(angle2), 360.0);

Take the difference, add 360, and mod by 360.

Related

4x4 matrix rotation unexpected: 200°+45°=115°

I would like to make a rotation using 4x4 matrix in Swift, but it has unexpected behavior: 200 degrees + 45 degrees = 115 degrees, and not 245
let degree200 = Angle(degrees: 200).radians
let degree45 = Angle(degrees: 45).radians
// 200 degrees + 45 degrees
let rotationMatrix = float4x4(simd_quatf(angle: Float(degree200+degree45), axis: SIMD3<Float>(0, 1, 0)))
// it prints 115 degree, and not 245
print(Angle(radians: Double(simd_quatf(rotationMatrix).angle)).degrees)
I assume that's a typo, and you in fact meant -115 degrees? (remainder(245, 360)) When using quaternions & Matrices to express orientations, you can only expect to see values of -180 to +180 degrees when converting those values back to Euler angles.
In general it is impossible to convert back to Euler angles from either a quaternion or matrix and get the original input values back. You either store the original Euler angles and present those to the user, or you will have to have a known starting Euler value and apply an Euler filter to obtain approximately correct results.
The only correct way to get your expected result is to NOT print the value after conversion to quats:
print((degree200 + degree45). degrees)
Well I know 115 and 245 are 360. Just a guess but maybe you're rotating the wrong way?? Maybe try negative values and see what happens.

Angles in Matlab

i need to calculate some expression for all angles from 0 to 90 degrees increments 10 degrees (of cause expression depends on some trigonometrical function).
It looks like:
for alpha = 0:10:90
func(alpha) = c * sin(alpha)
end
Who know how to work with degrees, tell, please
It should be:
for 0:pi/18:pi/2

point projection into yx rotated plane

I want to simulate depth in a 2D space, If I have a point P1 I suppose that I need to project that given point P1 into a plane x axis rotated "theta" rads clockwise, to get P1'
It seems that P1'.x coord has to be the same as the P1.x and the P1'.y has to b shorter than P1.y. In a 3D world:
cosa = cos(theta)
sina = sin(theta)
P1'.x = P1.x
P1'.y = P1.y * cosa - P1.z * sina
P1'.z = P1.y * sina + P1.z * cosa
Is my P1.z = 0? I tried it and P1'.y = P1.y * cosa doesn't result as expected
Any response would be appreciated, Thanks!
EDIT: What I want, now I rotate camera and translate matrix
EDIT 2: an example of a single line with a start1 point and a end1 point (it's an horizontal line, result expected is a falling line to the "floor" as long as tilt angle increases)
I think it's a sign error or an offset needed (java canvas drawing (0,0) is at top-left), because my new line with a tilt of 0 is the one below of all and with a value of 90º the new line and the original one match
The calculation you are performing is correct if you would like to perform a rotation around the x axis clockwise. If you think of your line as a sheet of paper, a rotation of 0 degrees is you looking directly at the line.
For the example you have given the line is horizontal to the x axis. This will not change on rotation around the x axis (the line and the axis around which it is rotating are parallel to one another). As you rotate between 0 and 90 degrees the y co-ordinates of the line will decrease with P1.y*cos(theta) down to 0 at 90 degrees (think about the piece of paper we have been rotating around it's bottom edge, the x axis, at 90 degrees the paper is flat, and the y axis is perpendicular to the page, thus both edges of the page have the same y co-ordinate, both the side that is the "x-axis" and the opposite parallel side will have y=0).
Thus as you can see for your example this has worked correctly.
EDIT: The reason that multiplying by 90 degrees does not give an exactly zero answer is simply floating point rounding

Draw Camera Range with Postgis

i am working on some camera data. I have some points which consist of azimuth, angle, distance, and of course coordinate field attributes. In postgresql postgis I want to draw shapes like this with functions.
how can i draw this pink range shape?
at first should i draw 360 degree circle then extracting out of my shape... i dont know how?
I would create a circle around the point(x,y) with your radius distance, then use the info below to create a triangle that has a larger height than the radius.
Then using those two polygons do an ST_Intersection between the two geometries.
NOTE: This method only works if the angle is less than 180 degrees.
Note, that if you extend the outer edges and meet it with a 90 degree angle from the midpoint of your arc, you have a an angle, and an adjacent side. Now you can SOH CAH TOA!
Get Points B and C
Let point A = (x,y)
To get the top point:
point B = (x + radius, y + (r * tan(angle)))
to get the bottom point:
point C = (x + radius, y - (r * tan(angle)))
Rotate your triangle to you azimouth
Now that you have the triangle, you need to rotate it to your azimuth, with a pivot point of A. This means you need point A at the origin when you do the rotation. The rotation is the trickiest part. Its used in computer graphics all the time. (Actually, if you know OpenGL you could get it to do the rotation for you.)
NOTE: This method rotates counter-clockwise through an angle (theta) around the origin. You might have to adjust your azimuth accordingly.
First step: translate your triangle so that A (your original x,y) is at 0,0. Whatever you added/subtracted to x and y, do the same for the other two points.
(You need to translate it because you need point A to be at the origin)
Second step: Then rotate points B and C using a rotation matrix. More info here, but I'll give you the formula:
Your new point is (x', y')
Do this for points B and C.
Third step: Translate them back to the original place by adding or subtracting. If you subtracted x last time, add it this time.
Finally, use points {A,B,C} to create a triangle.
And then do a ST_Intersection(geom_circle,geom_triangle);
Because this takes a lot of calculations, it would be best to write a program that does all these calculations and then populates a table.
PostGIS supports curves, so one way to achieve this that might require less math on your behalf would be to do something like:
SELECT ST_GeomFromText('COMPOUNDCURVE((0 0, 0 10), CIRCULARSTRING(0 10, 7.071 7.071, 10 0), (10 0, 0 0))')
This describes a sector with an origin at 0,0, a radius of 10 degrees (geographic coordinates), and an opening angle of 45°.
Wrapping that with additional functions to convert it from a true curve into a LINESTRING, reduce the coordinate precision, and to transform it into WKT:
SELECT ST_AsText(ST_SnapToGrid(ST_CurveToLine(ST_GeomFromText('COMPOUNDCURVE((0 0, 0 10), CIRCULARSTRING(0 10, 7.071 7.071, 10 0), (10 0, 0 0))')), 0.01))
Gives:
This requires a few pieces of pre-computed information (the position of the centre, and the two adjacent vertices, and one other point on the edge of the segment) but it has the distinct advantage of actually producing a truly curved geometry. It also works with segments with opening angles greater than 180°.
A tip: the 7.071 x and y positions used in the example can be computed like this:
x = {radius} cos {angle} = 10 cos 45 ≈ 7.0171
y = {radius} sin {angle} = 10 sin 45 ≈ 7.0171
Corner cases: at the antimeridian, and at the poles.

Point in Tilt Direction - iPhone

In my cocos2d game, I have my player sprite and I want to have him move in the direction I tilt my iPhone. I can deal with that, the hardest bit which I can't work out is:
How do I make my sprite rotate to point in the direction I am tilting? This is represented very well in the 'Tilt to Live' game on the app store. I want controls just like that.
My sprite(for those unfamiliar with cocos2d) does have a rotation value if that helps.
Thanks.
If you don't like the above, here's a simpler way to get a reasonable result!
Hold the iPad in front of you, let LR be the left / right tilt, TA the towards you / away from you tilt. So LR runs from -90 to 90, TA from -90 to 90. (TA negative is leaning towards your belly.)
Display both those numbers on your screen, and move the device around, so you are certain you have that right to begin with. You won't be able to do anything until that is working.
The solutionAngle will be like a clock hand, clockwise, with 12 distant from you.
Go through this decision chain:
If both LR and TA is zero, the machine is flat. Act appropriately.
If LR is flat (0), the answer is either 0 or 180, depending on the sign of TA.
If TA is flat (0), the answer is either 90 or 270, depending on the sign of LR.
Otherwise:
adjustmentAngle = arctan( sin(TA) / sin(LR) )
// (NB, that should run from -90 to +90)
if ( LR > 0 ) finalResult = 90 - adjustmentAngle
if ( LR < 0 ), finalResult = 270 + adjustmentAngle
I think that will do it! Hope it helps!
IMO...... be sure to smooth the result over time, for a good feel.
.
setting the angle...
"the only thing I am unsure of currently (concerning your own idea) is how do I apply it to my player? Do I merely make the player rotation value equal to the adjustmentAngle?" .. hi Josh, yes simply set the rotation to the final angle you calculate using the above! Fortunately it's that simple.
If you ever have to convert back/fore between degrees and radians, just paste in these lines of code that everyone uses:
#include <math.h>
static inline float degreestoradians (double degrees) {return degrees * M_PI/180;}
static inline float radianstodegrees (double degrees) {return degrees * 180/M_PI;}
.
where are the axes?...
PS, here's the incredibly handy diagram you may want to bookmark:
http://developer.apple.com/library/ios/#documentation/uikit/reference/UIAcceleration_Class/Reference/UIAcceleration.html
.
converting from accelerometer to angles...
"the accelerometer doesn't provide the raw data in angles. How do get from the raw data"
Quite right, I forgot to mention it sorry. This is an everyday problem...
distanceFactor = square root of (x^2 + y^2 + z^2)
angle X axis = acos ( x / distanceFactor )
angle y axis = acos ( y / distanceFactor )
angle z axis = acos ( z / distanceFactor) )
You must TEST this by writing the three angles on the screen and then moving it around, in other words "physically unit test" that section you write, before you proceed!
here is one of many answers from SO: UIAccelerationValue angle
BTW as you can probably see, you can get a rough result by taking the ratio of simply the raw x by raw y value, rather than the ratio of the two sines, in the 'adjustmentAngle' expression ... but anyway don't worry about that for now.
And finally!
IMPORTANT Readers should note that the amazing new Core Motion system, handles a lot of this for you, depending on your needs. Check it out!!!!!
Hope it helps!