box2d difference between angles of two bodies - math

Working with box2d and cocos2d, I've calculated two vectors:
one is the vector pointing in the direction the car is travelling. (center of the vehicle to it's north point).
two is the vector pointing in the direction of the target. (center to the vehicle to the target center.)
I need to rotate the vehicle to also point in the direction of the target. This is done by setting the vehicle's steering either 90 degrees to turn right or -90 to turn left.
At the moment i'm querying the box2d body of the vehicle for its angle, then calculating the angle of the target direction vector using:
if(vector.y == 0){ vector.y = 0.000001f; }
float baseRadians = atan(vector.x/vector.y);
if(vector.y < 0){ baseRadians += PI_CONSTANT; } //Adjust for -Y
return -1 * baseRadians;
The following is crude and where I need help...!
I then compare the angle of the vehicle against the angle returned from the direction vector and set the steering as follows:
if(vehicleAngle < targetAngle)
{
steeringInput = -90.0;
}
else if(vehicleAngle > targetAngle)
{
steeringInput = 90.0;
}
else
{
steeringInput = 0.0;
}
The problem is two fold:
The target angle jumps from -4.71 to 1.57 radians, causing the vehicle to switch to the wrong direction
The vehicle angle is continous and keeps increasing rather than staying within a range.
Is there a way to get both the target vector and the vehicle angle in a set range where, for example, if the target angle is 0-360 and the van angle is 0-360, i can then compare them accurately?
Any other way to do this...? Any help appreciated!

Use atan2 instead of atan in your code. It gets the angle from one object to another.

Using the atan2f function instead I managed to get the vehicle vector angle and target vector angle.
my new implementation is as follows:
//get the direction the vehicle is facing and the direction the target is facing
CGPoint vehicleDirectionVector = ccpNormalize(ccpSub(ccp(vehicleBonnetTip.x*PTM_RATIO, vehicleBonnetTip.y*PTM_RATIO), ccp(vehicleCenter.x*PTM_RATIO, vehicleCenter.y*PTM_RATIO)));
CGPoint targetDirectionVector = ccpNormalize(ccpSub(ccp(origTarget.x, origTarget.y), ccp(vehicleCenter.x*PTM_RATIO, vehicleCenter.y*PTM_RATIO)));
vehicleAngle = atan2f(vehicleDirectionVector.x,vehicleDirectionVector.y);
targetAngle = atan2f(targetDirectionVector.x,targetDirectionVector.y);
if ( targetAngle < -M_PI / 2 && vehicleAngle > M_PI / 2 ) targetAngle += M_PI * 2;
if ( targetAngle > M_PI / 2 && vehicleAngle < -M_PI / 2 ) targetAngle -= M_PI * 2;
if ( vehicleAngle < -M_PI / 2 && targetAngle > M_PI / 2 ) vehicleAngle += M_PI * 2;
if ( vehicleAngle > M_PI / 2 && targetAngle < -M_PI / 2 ) vehicleAngle -= M_PI * 2;
CCLOG(#"vehicle angle is %f, target angle is %f", vehicleAngle, targetAngle);
Then to set my steering, crudely (i'm still working on this one..) i do:
if(vehicleAngle < targetAngle)
{
steeringDegrees = 90.0;
}
else if(vehicleAngle > targetAngle)
{
steeringDegrees = -90.0;
}
else
{
steeringDegrees = 0.0;
}

Related

How can I extract the angle from a function that determines if a circle is colliding with a line?

So I have the following code that works fine
I've taken it from https://codereview.stackexchange.com/questions/192477/circle-line-segment-collision
I am now trying to figure out where along this function I can determine the angle in which the circle is hitting the line. Instead of returning true I'd like it to return the angle, since I didn't write the function myself going through it to try and figure this out myself has been a bit of a struggle. Wondering if someone excellent at math can help me figure this out at first glance. Thank you!
// Function to check intercept of line seg and circle
// A,B end points of line segment
// C center of circle
// radius of circle
// returns true if touching or crossing else false
function doesLineInterceptCircle(A, B, C, radius) {
var dist;
const v1x = B.x - A.x;
const v1y = B.y - A.y;
const v2x = C.x - A.x;
const v2y = C.y - A.y;
// get the unit distance along the line of the closest point to
// circle center
const u = (v2x * v1x + v2y * v1y) / (v1y * v1y + v1x * v1x);
// if the point is on the line segment get the distance squared
// from that point to the circle center
if(u >= 0 && u <= 1){
dist = (A.x + v1x * u - C.x) ** 2 + (A.y + v1y * u - C.y) ** 2;
} else {
// if closest point not on the line segment
// use the unit distance to determine which end is closest
// and get dist square to circle
dist = u < 0 ?
(A.x - C.x) ** 2 + (A.y - C.y) ** 2 :
(B.x - C.x) ** 2 + (B.y - C.y) ** 2;
}
return dist < radius * radius;
}

Convert standard 360-degree to atan2

I need to reverse calculation of standard 360 angle to atan2.
It's my standard angle calulation
Standard angle = (int) Math.toDegrees(Math.atan2(y,x));
if(angle < 0)
angle += 360;
I need Math.atan2 value from angle how to achieve that.
To translate degrees range 0..360 into radians range -Pi..Pi:
if(angle > 180)
angle -= 360;
angle_radians = Math.toRadians(angle);
If your angle may be beyond standard range (you did not mentioned this), use integer modulo instead of loops
angle = angle % 360
(check how modulo operator works with negative numbers in your language)
Though your question is not very clear, it seems that you want to retrieve the angle before the statement
if (angle < 0)
angle += 360;
As atan2 returns a value in the range [-π,π), i.e. [-180°,180°), the subrange [-180°,0°) is mapped to [180°,360°). Then you invert with
if (angle >= 180°)
angle-= 360;
You also need to convert back to radians.
A general solution with O(1) performance:
double generalModulus(double value, double modulus, double offset) {
return value - Math.floor((value-offset)/modulus) * modulus + offset;
}
double normalizeAngleDegrees(double angleInDegrees, double offset) {
return generalModulus(angleInDegrees, 360.0, offset);
}
double normalizeAngleRadians(double angleInRadians, double offset) {
return generalModulus(angleInRadians, 2.0*Math.PI, offset);
}
To use:
double normDeg = normalizeAngleDegrees(angle, -180); // produces angle in range [-180, 180)
double normRad = normalizeAngleRadians(angle, 0); // produces angle in range [0, 2*Pi)
My Own answer
int normalizeAngle(int angle)
{
int newAngle = angle;
while (newAngle <= -180) newAngle += 360;
while (newAngle > 180) newAngle -= 360;
return newAngle;
}

Radius of a box in a given direction

Let's say I have an axis-aligned box. I also have a location outside that box.
How do I get the point on the boundaries of the box that is along the line from the center of the box to that point? (And thus, know how far the box extends in that direction).
EDIT: preferably, if the point is inside the box, it should just return the point... but that's something I could easily calculate myself.
2D diagram (Though the answer will preferably work in 3D as well):
point3d getIntersection(point3d topfrontleft, point3d backbottomright, point3d externpt)
{
//find the middle
point3d middle(topfrontleft/2+backbottomright/2);
//slide box and source as if the "middle" was at the origin
topfrontleft -= middle;
backbottomright-= middle;
externpt-= middle;
//scale source as if the box is the unit square
externpt/= topfrontleft;
//find the largest magnitude of the source offset
point3d mag = abs(externpt);
auto max = std::max(mag.x,std::max(mag.y,mag.z));
//if it's inside the box, don't scale
if (max < 1)
max = 1;
//scale the source so that it touches the box
externpt/= max;
//then scale and slide that touching point back to original coordinates
externpt= externpt* topfrontleft + middle;
return externpt;
}
http://coliru.stacked-crooked.com/a/9d9504964bc650bb
I'm not experienced with 3d math, so there's probably faster and more effective ways. No idea.
As a bonus, this algorithm doesn't make any reference to the number of dimensions except for finding the largest magnitude, so should work in both 2d, 3d, and any other theoretical number of dimensions.
Here is some code that does what I said in comments modified for rectangular boxes.
#include <stdio.h>
#include <math.h>
// Find intersection p between line A->B and box.
// Point A must be the box center.
// The box is [x_left, y_bottom, x_right, y_top].
void find_box_intersect(double *box, double *a, double *b, double *p)
{
double dx = b[0] - a[0];
double dy = b[1] - a[1];
if (fabs(dx * (box[3] - box[1])) > fabs(dy * (box[2] - box[0]))) {
p[0] = dx > 0 ? box[2] : box[0];
p[1] = a[1] + dy * (p[0] - a[0]) / dx;
} else {
p[1] = dy > 0 ? box[3] : box[1];
p[0] = a[0] + dx * (p[1] - a[1]) / dy;
}
}
int main(void) {
double box[] = { 1, 2,
2, 4 };
double p[2], a[] = { 0.5 * (box[0] + box[2]),
0.5 * (box[1] + box[3]) };
int i, n = 16;
for (i = 0; i < n; i++) {
double theta = 2 * 3.14159 * i / n;
double b[] = { a[0] + cos(theta), a[1] + sin(theta) };
find_box_intersect(box, a, b, p);
printf("%.2f, %.2f\n", p[0], p[1]);
}
return 0;
}
The 3d generalization of this is straightforward. Rather than the if statement that determines only 2 cases, there will be a 3 case if-else chain that determines 3: left-right, top-bottom, front-back.
Output:
2.00, 3.00
2.00, 3.21
2.00, 3.50
1.91, 4.00
1.50, 4.00
1.09, 4.00
1.00, 3.50
1.00, 3.21
1.00, 3.00
1.00, 2.79
1.00, 2.50
1.09, 2.00
1.50, 2.00
1.91, 2.00
2.00, 2.50
2.00, 2.79
Short answer: Intersect the line with the (right) border of the box.
Longer answer:
x is easy: It's at the right border of the box.
for y, solve this: y/line_height = (line_width - box_width/2) / line_width
then add the y of the line lower point
This is assuming the line intersects the right border, as in your picture.
To figure which border the line intersects, compare the ratio of line_height/line_width, and the signs, to the ratio of box_height/box_width.
If the box is square and you're only interested in the radius, and not the intersection point, you can throw away the signs of line_width and line_height, sort them, and just solve one case.
If the box is not square, you can still throw away the signs, but need to compare the ratios to pick one of two remaining cases (in 2D).
Assuming that you know the length and width of the rect, if the angle is pointing left or right, then x is known, so you only need to solve for y (r * sin(angle)); if the angle is pointing up or down, then y is known so you solve for x (r * cos(angle)).
// where the center of the rect = 0, 0 and angles are in degrees 0..360
// determine the 45's first
if (angle in (45, 135, 225, 315)) {
return { x:cos(angle) * (width / 2), y:sin(angle) * (height / 2) };
}
if (angle < 45 || angle > 315) {
// pointing right
return { x:(width / 2), y:sin(angle) * (height / 2) };
}
if (angle > 45 && angle < 135) {
// pointing up
return { x:cos(angle) * (width / 2), y:(height / 2) };
}
if (angle > 135 && angle < 225) {
// pointing left
return { x:(width / -2), y:sin(angle) * (height / 2) };
}
if (angle > 225 && angle < 315) {
// pointing down
return { x:cos( angle ) * (width / 2), y:(height / -2) };
}
There are undoubtedly more elegant mathematical solutions to this problem, but this one works for me--understandable, testable, etc.

Sampling points in a vector

I have a vector _pts that contains values for (x,y,z), i.e. a point in 3D. Starting with _pts[0], I want to select those points whose distance between previously selected points is bigger than sampleRadius.
Here is my code but apparently something's wrong because is selecting a lot of points instead of just selecting a few.
Can anyone see what am I doing wrong? Probably you would need more code to see what can be missed, but I would also appreciate any idea on how can I implement this.
float distance;
bool distanceIsOk;
//PICK A POINT IN VECTOR _pts
for (int cPIdx = 0; cPIdx < _pts.size(); cPIdx++) {
distanceIsOk = true;
//CHECK DISTANCE AGAINST PREVIOUSLY PICKED POINTS
for (int dPIdx = 0; dPIdx < indeces.size(); dPIdx++) {
distance = sqrt(
(_pts[cPIdx].v[0] - _pts[dPIdx].v[0])*(_pts[cPIdx].v[0] - _pts[dPIdx].v[0]) +
(_pts[cPIdx].v[1] - _pts[dPIdx].v[1])*(_pts[cPIdx].v[1] - _pts[dPIdx].v[1]) +
(_pts[cPIdx].v[2] - _pts[dPIdx].v[2])*(_pts[cPIdx].v[2] - _pts[dPIdx].v[2])
);
//IF DISTANCE IS <= SUBSAMPLERADIUS FOR AT LEAST ONE PREVIOUSLY SELECTED POINT
if (distance <= subsampleRadius) {
//DISCARD THE POINT
distanceIsOk = false;
dPIdx += indeces.size();
}
}
//OTHERWISE INCLUDE THAT POINT
if (distanceIsOk == true) {
indeces.push_back(cPIdx);
}
}
Found the error. Instead of
distance = sqrt(
(_pts[cPIdx].v[0] - _pts[dPIdx].v[0])*(_pts[cPIdx].v[0] - _pts[dPIdx].v[0]) +
(_pts[cPIdx].v[1] - _pts[dPIdx].v[1])*(_pts[cPIdx].v[1] - _pts[dPIdx].v[1]) +
(_pts[cPIdx].v[2] - _pts[dPIdx].v[2])*(_pts[cPIdx].v[2] - _pts[dPIdx].v[2])
it should be
distance = sqrt(
(_pts[cPIdx].v[0] - _pts[indeces[dPIdx]].v[0])*(_pts[cPIdx].v[0] - _pts[indeces[dPIdx]].v[0]) +
(_pts[cPIdx].v[1] - _pts[indeces[dPIdx]].v[1])*(_pts[cPIdx].v[1] - _pts[indeces[dPIdx]].v[1]) +
(_pts[cPIdx].v[2] - _pts[indeces[dPIdx]].v[2])*(_pts[cPIdx].v[2] - _pts[indeces[dPIdx]].v[2])

Finding roll, yaw and pitch of a camera, having it's position, target and up vectors

I'm trying to find the yaw, pitch and roll angles of a camera, assuming that I have the position of the camera, it's look_at point (target point) and it's up vector. My best try was by using the following code
zaxis = lookat-position
xaxis = cross(up, xaxis)
yaxos = cross(zxis, xaxis)
Then I find the angles between each axis and the normal vectors (1,0,0) (0,1,0) and (0,0,1)
and assign them to roll, yaw and pitch, but it doesn't seem to work
Any ideas, what I'm doing wrong?
Thanks in advance :)
You won't be able to get the roll angle - as that could be anything, but you can get the elevation and azimuth (pitch and yaw). I've found some old C code which I'll translate to pseudo code, so assuming that your vector isn't zero length:
Vector3 v = lookat - position;
double length = v.Length();
double elevation = asin(v.y / length);
double azimuth;
if (abs(v.z) < 0.00001)
{
// Special case
if (v.x > 0)
{
azimuth = pi/2.0;
}
else if (v.x < 0)
{
azimuth = -pi/2.0;
}
else
{
azimuth = 0.0;
}
}
else
{
azimuth = atan2(v.x, v.z);
}

Resources