2D compass angle from model-view-matrix - c++

I have a game where there is a 2D compass from 0-360 degrees that tells you the angle the camera is facing regardless of height.
I am trying to figure out how to get that angle using the model-view matrix. My algorithm works but not 100% of the time.
I have the following code for grabbing the angles from the model-view matrix.
Vector3D<float> GetEulerAngles(float ModelView[16])
{
float X = atan2(ModelView[9], ModelView[10]) * (180 / PI);
float Y = atan2(-ModelView[8], sqrt(pow(ModelView[0], 2) + pow(ModelView[4], 2))) * (180 / PI);
float Z = atan2(ModelView[4], ModelView[0]) * (180 / PI);
return {X < 0 ? 360 - X : X, Y < 0 ? 360 - Y : Y, Z < 0 ? 360 - Z : Z};
}
/*
Works so long as the camera is at its highest point (looking down on the player - 66deg angle)
-------
OR any height and: 90 < CompassAngle < 360 is true.
*/
int GetCompassAngle()
{
glGetFloatv(GL_MODELVIEW_MATRIX, &ModelView[0]);
Vector3D<float> angles = GetEulerAngles(ModelView);
return round(to_degrees(Z));
}
/*
Works so long as the camera is at any height (and: 0 < CompassAngle < 90)
*/
int GetCompassAngle()
{
glGetFloatv(GL_MODELVIEW_MATRIX, &ModelView[0]);
Vector3D<float> angles = GetEulerAngles(ModelView);
return round(360 - to_degrees(Y));
}
When I lower the camera, the second GetCompassAngle works. It reads the "Y" from the euler angles. However, if the camera is at the highest point, it reads the "Z" from the euler angles.
These work but when rotating and changing the camera angle changes the value and sometimes it gives me the wrong compass angle.
How can I combine them in some way or get the compass angle regardless of camera height?
Here are some numbers if it helps my case:
CompassHeight (Max - 65 deg from floor):
CompassDeg (X, Y, Z):
0deg: 65.0537253622628 358.814165052872 1.07537657735905
90deg: 88.1642315504792 312.258382165551 85.6596194029266
180deg: 115.286564608444 358.615368311296 178.747763300624
270deg: 90.1881102037367 47.9243745800853 269.562655218025
//Notice the "Z" values are the right compass angle or close enough.
CompassHeight (Low - 13 deg from floor):
CompassDeg (X, Y, Z):
0deg: 13.7624148875505 1.69169652884413 359.597596807979
90deg: 77.9527238194348 283.654247204305 77.5930785465022
180deg: 166.486754945655 355.694293448629 178.99462624173
270deg: 87.8507577921787 77.1021010565408 272.207848194156

Related

how to compensate point coordinates transformation when doing a rotaion around another point in an image?

I'm generating an image with text in C++ using library called ImageMagick, and the DrawableRotation function does a rotation around the point of coordinates (0;0). (because it's an image it's top left corner )
The issue here is that I need to rotate my text label by a certain degree to put it on top of rectagle that has the same angle. ( text in box )
But the boxes are drown using the 4 points coordinates, I have their relative angle, which meens it's always positive, but some boxes are vertical, others are horizontal, and others are angled in between.
here's an exemple:
exemple of 3 boxes with different angles and their text
I would like to know if Image magick has an other way to rotate the text around itself or if there is a mathematical way to rotate the text so it has the good angle and then calculate the values I need to had to the coordinates to put it back at it's original coordinates.
I tried manually adding values to compensate the change in x;y but as all boxes have different angles, it's not dynamic enough, some labels get lost randomly on the image.
Well, I couldn't find a way to overcome this using the ImageMagick library directly, so what I do is make the rotation transformation around the origin before using the rotation formula from ImageMagick but in the opposite direction. But I also needed to map the angle (in degrees) from 90 to -90 so the text is always in the best direction to read. I'll post sample code for the rotation down below :
std::pair<float, float> MyImage::coorRotation(float x, float y, float angle)
{
float x_ = x*cos(angle) - y*sin(angle);
float y_ = x*sin(angle) + y*cos(angle);
return (std::make_pair(x_, y_));
}
for the rotation compensation and :
void MyImage::drawTextOnShapes()
float degrees = std::abs((atan2(this.shape.delta y, this.shape.delta x) * 180.0) / PI); // delta y = y2 - y1 and delta x = x2 - x1
if (degrees > 90)
degrees -= 180; // to map between 90 and -90
float radian = (degrees * PI) / 180;
float x = this.shape.center.x;
float y = this.shape.center.y;
std::pair coords = std::make_pair(x, y);
this->img.strokeColor("white");
coords = this->coorRotation(x, y, radian);
drawlist.push_back(DrawableRotation(-degrees));
stream << std::fixed << std::setprecision(2) << wall.length; // to get a .10 precision on the float output
drawlist.push_back(DrawableText(coords.first - textLenInPixels/2, coords.second, stream.str().append("m"))); // m for meters
this->img.draw(drawlist);
drawlist.clear();
this->saveImage();
}
And here's the result:

Implement an Ellipse Structural Element

I want to implement the following using OpenCV (I'll post my attempt at the bottom of the post). I am aware that OpenCV has a function for something like this, but I want to try to write my own.
In an image (Mat) (the coordinate system is at the top left, since it is an image) of width width and height height, I want to display a filled ellipsewith the following properties:
it should be centered at (width/2, height/2)
the image should be binary, so the points corresponding to the ellipse should have a value of 1 and others should be 0
the ellipse should be rotated by angle radians around the origin (or degrees, this does not matter all that much, I can convert)
ellipse: semi-major axis parameter is a and semi-minor axis parameter is b and these two parameters also represent the size of these axes in the picture, so "no matter" the width and height, the ellipse should have a major axis of size 2*a and a minor axis of size 2*b
Ok, so I've found an equation similar to this (https://math.stackexchange.com/a/434482/403961) for my purpose. My code is as follows.. it does seem to do pretty well on the rotation side, but, sadly, depending on the rotation angle, the SIZE (major axis, not sure about the minor) visibly increases/decreases, which is not normal, since I want it to have the same size, independent of the rotation angle.
NOTE The biggest size is seemingly achieved when the angle is 45 or -45 degrees and the smallest for angles like -90, 0, 90.
Code:
inline double sqr(double x)
{
return x * x;
}
Mat ellipticalElement(double a, double b, double angle, int width, int height)
{
// just to make sure I don't use some bad values for my parameters
assert(2 * a < width);
assert(2 * b < height);
Mat element = Mat::zeros(height, width, CV_8UC1);
Point center(width / 2, height / 2);
for(int x = 0 ; x < width ; x++)
for(int y = 0 ; y < height ; y++)
{
if (sqr((x - center.x) * cos(angle) - (y - center.y) * sin(angle)) / sqr(a) + sqr((x - center.x) * sin(angle) - (y - center.y) * cos(angle)) / sqr(b) <= 1)
element.at<uchar>(y, x) = 1;
}
return element;
}
A pesky typo sneaked in your inequality. The first summand must be
sqr((x - center.x) * cos(angle) + (y - center.y) * sin(angle)) / sqr(a)
Note the plus sign instead of minus.

Plot from Cartesian to polar

I'm using the Left and Right audio channels to create a Lissajous Vectorscope. Left is x and Right is y, both which never goes beyond 1 and -1 values. These coordinates are also shifted at a 45 degree angle to give me the following view.
So I'm doing a very simple
// converting x and y value from (-1 - 1) to (0 - 1)
float x = LeftChannelValue/2 + 0.5
float y = RightChannelValue/2 + 0.5
// multiplying the width and height with X and Y to get a proper square
// width and height have to be the same value
float new_X = x*(width*0.5)
float new_Y = y*(height*0.5)
// doing two dimensional rotating to 45 degrees so it's easier to read
float cosVal = cos(0.25*pi)
float sinVal = sin(0.25*pi)
float finalX = (((new_X*cosVal)-(new_Y *sinVal))) + (width*0.5) //adding to translate back to origin
float finalY = ((new_X*sinVal) + (new_Y *cosVal))
This gives me the results on that picture.
How would I graph the polar coordinates so that it doesn't look like a square, it looks like a circle?
I'm trying to get this view but am absolutely confused about how that would correlate with the left and right. I'm using https://en.wikipedia.org/wiki/Polar_coordinate_system as a reference.
I figured out what I wanted.
I was trying to plot those coordinates in a polar graph. I was going about it all wrong.
I eventually realized that in order for me to convert the x,y coordinates, I needed my own definition for what a radius and an angle should represents in my x,y chart. In my case, I wanted the radius to be the largest absolute value of x and y
The only problem was trying to figure out how to calculate an angle using x and y values.
This is how I wanted my circle to work,
when x = y, the angle is 0.
when x = 1 & y = 0, then angle is 45.
when x = 1 & y = -1, then angle is 90.
when x = 0 & y = 1, then angle is -45.
when x = -1 & y = 1, then angle is -90.
given this information, you can figure out the rest of the coordinates for the circle up to 180 & - 180 degree angle.
I had to use conditions (if else statements) to properly figure out the correct angle given x and y.
And then to graph the polar coordinate, you just convert using the cos and sin conversion to x, y coordinates.
I like to program, I'm just not good with calculus.

How to get the Euler rotation of a rigid body between 0 to 360 in Bullet Physics?

I am currently trying to get the rotation of an object. I am using C++ and Bullet Physics. This is my code:
btScalar x, y, z;
body[0]->getCenterOfMassTransform().getBasis().getEulerZYX(z, y, x);
However, as I rotate the object around clockwise the number I get from the y (y is vertical in Bullet) axis goes from 0 to -90 to 0 to 90 and finally back to 0 for every quarter rotation. It is close but what I need is for it to go all the way from 0 to 360.
Bullet documentation says:
void getEulerZYX (btScalar &yaw, btScalar &pitch, btScalar &roll, unsigned int solution_number=1) const
and
solution_number Which solution of two possible solutions ( 1 or 2) are possible values
this is because euler angles are ambigous. have you tried solution 2?
I had the same problem. I using LibGDX with Bullet engine, so my code sample on Java, but I'm sure, that it will works on C++ too. Here is my solution (for Z axis):
body.getWorldTransform().getRotation(mRotation);
// That gives you an angle in all range but excluding (85, 95) and (-95, 85). For other axis you can try to get Pitch or Yaw.
float roll = mRotation.getRoll();
// That gives you an angle in range [0, 240). Clockwise and counterclockwise directions isn't detected.
float angle = mRotation.getAngleAround(0, 0, 1);
// Usually 0, but on (85, 95) and (-95, 85) becomes 1 and -1.
int gimbalPole = mRotation.getGimbalPole();
// Using roll (pitch/yaw for other axis) if it's defined, and using angle with gimble pole otherwise.
float rotation = (gimbalPole == 0) ? roll : angle * gimbalPole;
Obtained rotation will be in range (-180, 180). It can be easily converted to [0, 360) range:
if (rotation < 0) rotation += 360;

2D Euclidean vector rotations

I have a euclidean vector a sitting at the coordinates (0, 1).
I want to rotate a by 90 degrees (clockwise) around the origin: (0, 0).
If I have a proper understanding of how this should work, the resultant (x, y) coordinates after the rotation should be (1, 0).
If I were to rotate it by 45 degrees (still clockwise) instead, I would have expected the resultant coordinates to be (0.707, 0.707).
theta = deg2rad(angle);
cs = cos(theta);
sn = sin(theta);
x = x * cs - y * sn;
y = x * sn + y * cs;
Using the above code, with an angle value of 90.0 degrees, the resultant coordinates are: (-1, 1).
And I am so damn confused.
The examples seen in the following links represent the same formula shown above surely?
What have I done wrong?
Or have I misunderstood how a vector is to be rotated?
Rotating a vector 90 degrees is particularily simple.
(x, y) rotated 90 degrees around (0, 0) is (-y, x).
If you want to rotate clockwise, you simply do it the other way around, getting (y, -x).
you should remove the vars from the function:
x = x * cs - y * sn; // now x is something different than original vector x
y = x * sn + y * cs;
create new coordinates becomes, to avoid calculation of x before it reaches the second line:
px = x * cs - y * sn;
py = x * sn + y * cs;
Rotate by 90 degress around 0,0:
x' = -y
y' = x
Rotate by 90 degress around px,py:
x' = -(y - py) + px
y' = (x - px) + py
Sounds easier to do with the standard classes:
std::complex<double> vecA(0,1);
std::complex<double> i(0,1); // 90 degrees
std::complex<double> r45(sqrt(2.0),sqrt(2.0));
vecA *= i;
vecA *= r45;
Vector rotation is a subset of complex multiplication. To rotate over an angle alpha, you multiply by std::complex<double> { cos(alpha), sin(alpha) }
You're calculating the y-part of your new coordinate based on the 'new' x-part of the new coordinate. Basically this means your calculating the new output in terms of the new output...
Try to rewrite in terms of input and output:
vector2<double> multiply( vector2<double> input, double cs, double sn ) {
vector2<double> result;
result.x = input.x * cs - input.y * sn;
result.y = input.x * sn + input.y * cs;
return result;
}
Then you can do this:
vector2<double> input(0,1);
vector2<double> transformed = multiply( input, cs, sn );
Note how choosing proper names for your variables can avoid this problem alltogether!