How to rotate a sprite to a point? - c++

I'm trying to to rotate a sprite, let's say it's a gun, to a point, by taking the smallest route possible on a 360° (right or left depending on what side the point is closer to) and I'm having a few problems.
On a circle, it jumps from 359° to 0° so I can't direcly use my target angle - current angle.
Leep in mind that I'm using SFML so it's executing the function everyframe while isRotating is true.
This is the information that is available to me :
//The angle in degrees of my sprite
float currentAngle = pSprite.getRotation();
//The angle is needs to be once the rotation is over
float targetAngle = float(atan2(deltaY,deltaX) * 180 / (atan(1)*4) + 180);
I'm using a speed variable to increment or decrement the value of the angle every frame.
distance = Speed*Time.asSeconds();
currentAngle += distance;

First, get the difference:
diff = target - current
diff is either the "short" angle (the one resulting in a shorter rotation), or the "long" angle (rotation in the other direction which is longer). Notice that:
you never need to rotate for more than (as an absolute value) 180 degrees to get from one angle to another.
the "short" angle and the "long" angle have opposite signs (+/-) and their absolute values add to 360.
Example: 0 - 359 = -359. This is the "long" angle. We know this because its absolute value is > 180. The "short" angle will have the opposite sign and its absolute value will add up to 360 with 359, so it is 1.
An easy way to calculate this is:
while (diff < -180)
diff += 360;
while (diff > 180)
diff -= 360;
diff is now the angle you are looking for. So if it is negative, multiply your Speed by -1.
The while (as opposed to if) is there in case the angles are not in [0, 360] - for example, you have current = 1440, target = 359.

Related

Ellipse rotated not centered

I am trying to draw a rotated ellipse not centered at the origin (in c++).
so far my code "works":
for (double i = 0; i <= 360; i = i + 1) {
theta = i*pi / 180;
x = (polygonList[compt]->a_coeff / 2) * sin(theta) + polygonList[compt]->centroid->datapointx;
y = (polygonList[compt]->b_coeff / 2) * cos(theta) + polygonList[compt]->centroid->datapointy;
xTmp = (x - polygonList[compt]->centroid->datapointx)* cos(angle1) - (y - polygonList[compt]->centroid->datapointy)*sin(angle1) + polygonList[compt]->centroid->datapointx;
yTmp = (x - polygonList[compt]->centroid->datapointx)* sin(angle1) + (y - polygonList[compt]->centroid->datapointy)*cos(angle1) + polygonList[compt]->centroid->datapointy;
}
PolygonList is a list of "bloc" which will be replaced by an ellipse of same area.
My issue is that the angles are not quite exact, as if I had to put a protractor that'd fit the shape of my ellipse, the protractor would obviously get squeezed, and so would be the angles (is that clear ?)
Here is an example: I am trying to set a point on the top ellipse (E1) which would be lying on a line drawn between the centroid of E1, and any point on the second ellipse (E2).On this example, the point on E2 lies at an angle of ~220-230 degree. I am able to catch this angle, the angle seems ok.
The problem is that if I try to project this point on E1 by using this angle of ~225 degree, I end up on the second red circle on top. it looks like my angle is now ~265 degree, but in fact, if I shape the protractor to fit in my ellipse, I get the right angle (~225) ,cf img 2)
it is a bit hard to see the angle on that re-shaped protractor, but it does show ~225 degree.
My conclusion is that the ellipse is drawn like if I had to drew a circle and then I'd compress it, which changes the distance between the angles.
Could someone tell me how I could fix that ?
PS: to draw those ellipses I just use a for loop which plots a dot at every angle (from 0 to 360). we clearly see on the first picture that the distance between the dots are different whether we are at 0 or at 90 degree.
your parametrisation is exactly that, a circle is a case of ellipse with both axes are equal. It sounds like you need use rational representation of ellipse instead of standard: https://en.m.wikipedia.org/wiki/Ellipse
So, I've asked the question above so that I could find a possible overlap between 2 ellipses by checking the distance between any point on E2 and its projection on E1: if the distance between the centroid of E1 and the projected dot on E1 is larger than the distance between the centroid of E1 to a dot on E2 I'll assume an overlap. I reckon this solution has never been tried (or I haven't search enough) and should work fine. But before working I needed to get those angles right.
I have found a way to avoid using angles and projected dots, by checking the foci:
the sum of the distance between the focus A and B to any point around an axis is constant (let's call it DE1 for E1).
I then check the distance between my foci and any point on E2. If that distance becomes less than DE1, I'll assume a connection.
So far it seems to work fine :)
I'll put that here for anyone in need.
Flo

rotating around objects

I've been trying to make one object orbiting another:
//childX,childY,childZ are my starting coordinates
//here I count distance to the middle of my coordinate plane
float r = (float) Math.sqrt(Math.pow(childX, 2)+Math.pow(childY, 2)+Math.pow(childZ,2));
//here i convert my angles to radians
float alphaToRad = (float) Math.toRadians(findParent(figure.parentId).rotate[1]);//up_down
float betaToRad = (float) Math.toRadians(findParent(figure.parentId).rotate[0]);//left_right
float newX = (float) (r*Math.cos(betaToRad)*Math.cos(alphaToRad));
float newY = (float) (r*Math.cos(betaToRad)*Math.sin(alphaToRad));
float newZ = (float) (r*Math.sin(betaToRad));'
I have coordinates of my starting point(5,5,0) and angles 0° and 0°, so it means, that coordinates shouldn't change after calculating the new ones. But the result is:
newX: 7.071068 newY: 0.0 newZ: 0.0
Every method I try to calculate new coordinates there is always this strange result. What is that 7.07 and how can I get correct result?
#edit
To make my new point relative to the old one I just added angles of old point to the new one:
float alphaToRad = (float) Math.toRadians(findParent(figure.parentId).rotate[1]) + Math.atan(childY/childX);
float betaToRad = (float) Math.toRadians(findParent(figure.parentId).rotate[0]) + Math.asin(childZ/r);
Everything now works like it should have. Solved
7.07 is the value of r in your code, which is the distance of your point from the origin:
sqrt(5 * 5 + 5 * 5) = sqrt(50) = 7.0711
With both angles being zero, all the cos() values will be 1.0, and the sin() values 0.0. Which means that newX becomes r, which is 7.07, and both newY and newZ become 0.0. Which is exactly what you got, so there is no mystery in this result.
What you're basically doing is place the point at a given direction and distance from the origin. The distance is the same as the original distance. The direction is given by the two angles, where both angles being 0.0 corresponds to the x-axis direction.
In other words, what you're missing is that you're not taking the original direction of the point relative to the origin into account. You're placing the point at an absolute direction, based on the two angles, instead of at a direction relative to the original direction of the point.
To rotate the point by the given angles, the easiest approach is to build rotation matrices from the angles, and apply them to your points.

Rotation Atan2 CCW CW continuity

This is what I have
I have a plane in 2D X,Y
I set his destination by clicking on the screen X', Y'
I calculate the angle it needs to turn to face this destination with:
// Calculate the angle between plane position and destination point
CVector3 facingVec = m_vDestination - m_vPosition;
fAngle = -Math::radiansToDegrees ( (float)atan2f(m_vDestination.x - m_vPosition.x, m_vDestination.y - m_vPosition.y) ) ;
//This doesn't work, when rotating from ex. 350 degree to 0
//plane has to go all the way around 360,350,340,330,
//...,120,...100,90,..down to zero
float angleToTurn = fAngle - m_fRotationAngle;
if(angleToTurn < 0)
{
angleToTurn += 360.0f;
}
m_fRotationAngle += (angleToTurn) / 5;
// Move the unit towards the calculated angle m_fRotationAngle
m_vDirection.x = (-sin(Math::degreesToRadians(m_fRotationAngle)));
m_vDirection.y = (cos(Math::degreesToRadians(m_fRotationAngle)));
m_vPosition += ( 2 * m_vDirection * fDelta);
This is how it looks like
YT Video - sorry for the demo version, i couldn't get anything free at this moment.
This is what I need
I need this to behave properly, let's say plane is rotated at angle 350.
I set the destination and new angle should be 15.
Instead of going: 350,340,330,320,310,300,290,...10,0,15
It should continue: 350,0,15
Hope you can help me out with this guys, I've already dropped bezier approach - and I'm struggling with this since few days now.
If I read this correctly, you're trying to find the smallest angle to interpolate between the two vectors? If so, the following algorithm should work:
Find the angle of the first vector, relative to the fixed vector [1, 0]. This is a1.
Find the angle of the second vector, relative to the fixed vector [1, 0]. This is a2.
Let da = a2 - a1.
if da > 180, da -= 360;
else if da < 180, da += 360;
You need to calculate the angles with respect to another third vector [1, 0] so you can determine weather to rotate left or right.
Edit: I saw your YouTube link was broken, now I see it's working again. I think my answer is what you're after.

Discontinuity in gluLookAt

This is how I calculate my line of sight vector and the up vector.
ly = sin(inclination);
lx = cos(inclination)*sin(azimuth);
lz = cos(inclination)*cos(azimuth);
uy = sin(inclination + M_PI / 2.0);
ux = cos(inclination + M_PI / 2.0)*sin(azimuth + M_PI);
uz = cos(inclination + M_PI / 2.0)*cos(azimuth + M_PI);
inclination is the angle of the line of sight vector from the xz plane and azimuth is the angle in the xz plane.
This works fine till my inclination reaches 225 degrees. At that point there is a discontinuity in the rotation for some reason. (Note By 225 degrees, I mean its past the upside-down point)
Any ideas as to why this is so?
EDIT: Never mind, figured it out. The azimuth does not need a 180 deg. tilt for the up vector.
I think you are talking of a limit angle of 90 degrees (pi). What you get is a normal behavior. When using gluLookAt, you specify an 'up' vector, used to determine the roll of the camera. In the special case where you are looking upside down, the 'up' vector is parallel to the eye direction vector, so it is not possible to determine the roll of the camera (this problem as an infinite number of solutions, so an arbitrary one is chosen by gluLookAt). May be you should compute this 'up' vector using your inclination and azimuth.

Convert Mouse pos into direction and back

I want to ask what would be the best formula to convert mouse X,Y position into one of 16 directiones from player position.
I work in c++ ,sfml 1.6 so I get every position easily, but I dont know how to convert them based on angle from player position or something. (I was never good on math and for more than 4 directions if statements looks too complex).
Also I want to send it to server which converts direction back into delta X,Y so he can do something like:
player.Move(deltaX * speed * GetElapsedTime(), ...Y);
The "easiest" way would be to convert your two sets of co-ordinates (one for current player position, one for current mouse position) into an angle relative to the player's position, where an angle of 0 is the line straight ahead of the player (or north, depending on how your game works). Then each of your sixteen directions would translate to a given 22.5 degree interval.
However, since you said you're bad at math, I imagine you're looking for something more concrete than that.
You could use atan2 to get the angle between the mouse position and the positive X axis:
#include <cmath>
float player_x = ...;
float player_y = ...;
float mouse_x = ...;
float mouse_y = ...;
float angle = std::atan2(mouse_y - player_y, mouse_x - player_x);
The angle returned by std::atan2() is a value between -M_PI (exclusive) and M_PI (inclusive):
-M_PI Left (-180°)
-0.5 * M_PI Down (-90°)
0 Right (0°)
0.5 * M_PI Up (90°)
M_PI Left (180°)
You can transform this value depending on how you want your mapping to "one of 16 directions", i.e., depending on what value you want to assign to which discrete direction.
Given the angle, getting a unit vector to represent the X/Y delta is quite easy, too:
float dx = std::cos(angle);
float dy = std::sin(angle);