C, getting the Cartesian coordinate from the degrees of rotation
I've been working on business applications for too long ... and finished too long ago :) I was recently tasked with writing a little robotics simulation using C (although this question is more mathematical / algorithm than C) where I have two units (tank bots), starting with the X and Y coordinates on the playing field.
The panel now has keys to rotate them and a key to move them forward. I am now facing a minor brain crisis when translating from degrees of rotation to the next X, Y coordinate to get into a Cartesian playing field.
Due to limitations in HW, only a fixed point is available for the actual movement, but calculations can be done using float values.
I wrote the following code from memory only:
/* Recalculate to radians */
int radians;
/* Use sin and cos to get a vector (new x and y coords). Translate from polar to
cartesian coordinates */
radians = (int) _tanks[0].rotationAngle * (M_PI / 180);
_tanks[0].x += _tanks[0].speed * cos(radians);
_tanks[0].y += _tanks[0].speed * sin(radians);
radians = (int) _tanks[1].rotationAngle * (M_PI / 180);
_tanks[1].x += _tanks[1].speed * cos(radians);
_tanks[1].y += _tanks[1].speed * sin(radians);
Unfortunately, it seems like my brain hasn't really been updated on polar coordinate math and geometry after all these years of writing pure business software, so it doesn't seem to work as intended.
For example, if rotationAngle is 180, the next x / y is on the left instead, causing the bot to tip over :)
What I want is a motion pattern similar to the old Micro Machines games, if you remember where the next point would be in front of the object's face, so it moves (speed) the number of steps there.
Can someone please tell me where I went wrong here ...
Also, if there is a smoother way to do this in C than the pure math attempt I just wrote (bad at that), give me a hint.
EDIT:
Tried to add:
float radians;
radians = (45 - _tanks[0].rotationAngle) * (M_PI / 180);
_tanks[0].x += (int) (_tanks[0].speed * cos(radians));
_tanks[0].y += (int) (_tanks[0].speed * sin(radians));
as per the answer below, since 0 degrees is indeed the positive y-axis. But this also gives incorrect results. The 180 degree movement now starts from the left side. There should be a 180 degree movement along the negative Y axis.
One more code:
_Tank struct initiator -
tanks[0].acc = 0;
tanks[0].dec = 0;
tanks[0].rotationAngle = 180;
tanks[0].speed = 0;
tanks[0].x = 400;
tanks[0].y = 150;
tanks[0].turretRotationAngle = 180;
The degree of rotation is just a number (fixed integer) and I wrap it like following a 360 degree circle, like so:
switch(direction) {
case 0:
tank->rotationAngle -= degrees;
if(tank->rotationAngle < 1) {
tank->rotationAngle = 360;
}
break;
case 1:
tank->rotationAngle += degrees;
if(tank->rotationAngle > 360) {
tank->rotationAngle = 0;
}
break;
}
One key for clockwise rotation, one for counterclockwise.
The rotation works, but the movement does not occur as described ...
Debugging results:
Initial state (no movement due to 0 speed) -
radians = -2.3561945
x = 400
y = 150
speed = 0
After moving (speed> 0) -
radians = -2.3561945 (the same since i only press the move button)
x = 399
y = 149
speed = 2
It seems strange. The X coordinate shouldn't change at all if the rotation is 180 degrees from the original source right? Only Y should change in the opposite direction. I would translate the change instead, if the velocity is 2, the vector length should be 2, so the change will be 2 steps in the direction the object is facing, so y = y + 2 and x = x + 0 to rotate 180 degrees around the object ?
I feel like I'm getting there :)
Further EDIT:
It seems that ALMOST correctly matches the lines of what I need for the game board if I do this:
radians = (_tanks[0].rotationAngle - 90) * (M_PI / 180);
Note -90 ...
However, when the speed drops, it appears to be malfunctioning, but at least it is moving in the right direction.
source to share
For example, if rotationAngle is 180, the next x / y is on the left instead, causing the bot to tip over :)
Yes, this is what your code does: your code is correct, except for the question int radians
that user3386109 mentions above, assuming 0 ° is the positive x-axis, 90 ° is the positive y-axis, 180 ° is the negative x-axis, 270 ° (or -90 °) is the negative y-axis.
I am assuming that you want 0 ° to be the positive y-axis instead? And - do you want 90 ° to be the positive x-axis (so your angles go clockwise around the circle) or negative x-axis (so they continue counterclockwise)? For the first (clockwise) case, just change _tanks[...].rotationAngle
to (90 - _tanks[...].rotationAngle)
(to "flip" around the 45 ° line); for the latter (counterclockwise) case, just change it to (_tanks[...].rotationAngle + 90)
(to "rotate" it 90 ° from the origin).
source to share
@ruakh and @ user3386109 have a good discussion about angles and phase.
In addition, for a "smoother way to do it in C" also consider:
-
Use
round()
, otherwise the code introduces bias. (Assuming_tanks[1].x
- some integer)double radians = _tanks[0].rotationAngle * (M_PI / 180); _tanks[0].x += (int) round(_tanks[0].speed * cos(radians));
-
Use
float
rather thandouble
as additional precision with longer computation times is not required.float radians = _tanks[0].rotationAngle * (float)((M_PI / 180)); _tanks[0].x += (int) roundf(_tanks[0].speed * cosf(radians)); // note function names
-
If processing time is limited, an integer lookup table with 360
int
scaled sine and cosine values can be used rather than all floating point math._tanks[0].x += (_tanks[0].speed * LUT_cos[_tanks[0].rotationAngle])/scale;
source to share