How exactly does atan2 work?

I'm trying to code a steering behavior thingy with SFML using little sprites of fish lol. The trouble is the fish follow very strange directions and if they are following the correct direction (to my mouse cursor) and then I move the mouse across them to the opposite side they just can't turn around.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void Fish::calcVectors()
{
	desired.setMagnitude(MAX_SPEED);
	desired.setDirection(atan2(-(target.y - pos.y), target.x - pos.x));

	steering = desired - velocity;
	steering.limit(MAX_STEERING_FORCE);
}

void Fish::seek(Point target_)
{
	target = target_;
	calcVectors();
	applyVelocity(steering);
}
Last edited on
You haven’t supplied enough information to properly diagnose the problem.

Angles are in radians. The circle is centered on +X:

    0    == +X

    PI/2 == +Y

    PI   == -X

   -PI/2 == -Y

Hope this helps.
@dudeawesomeness1,
Why do you have that extra minus sign in the first argument to atan2 on line 4?

atan2(y,x) (note the order and number of arguments) will tell you the positive angle (i.e. anticlockwise for right-handed coordinates) between the positive x axis and the position vector (x,y).

It is better than using atan(y/x) when there is a possibility of dividing by 0 and/or where it is impossible to distinguish between the two angles between 0 and 2.pi that have the same tan value.

As @Duthomas has noted, it will return an angle in radians, so make sure that the rest of your code (which we can't see) is consistent with that.
Last edited on
@lastchance
The unary minus inverts the Y axis, so +Y is up.
Duthomas wrote:
The unary minus inverts the Y axis, so +Y is up.


I always draw a Y axis upwards! I don't need anything to invert it.
Last edited on
That may be, but most modern computer displays map +Y down.
https://en.wikipedia.org/wiki/Atan2

you may also enjoy quaternions. Those map 3-d space into 4-d space to avoid trig anomalies (like going straight up, rotate, and fall back, what direction does it point? Its lost in the zeros!)
I am using this with SFML and the window coordinates have positive Y going down with the origin at the top left of the window, so I need to flip the Y.
Also the direction should be in radians.

What's happening is the fish are taking very strange are usually incorrect trajectories and at some point the steering force becomes zero when the desired and velocity have the same magnitude.

This PhysVector is a class I have written and I use it everywhere I need vectors and it works in other projects. This fish program does work properly if the fish are just going straight in any direction and bouncing off the walls at the same angle from the wall at which they hit the wall.

you may also enjoy quaternions

lol, it certainly would be nice to not have to deal with trig functions. Maybe you could reference some place that I could learn how to use these in my code. Also aren't quaternions for 3d space not 2d space like with normal complex numbers?
what direction does it point?

um... I guess what you mean is they point up (or down) when I move the cursor over them in the X direction). It's like when I move the cursor over they hit a circular force field around the cursor but are fine going the other way. The only way to make them turn around is to make them hit the wall so they bounce back facing the opposite direction but then they have the same problem in the opposite direction. This works for the Y too or whatever direction they like going at the time (lol).

I didn't really know what to show but here's a deeper look at the functions used in the code I have shown.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
inline void setMagnitude(double speed) {
	magnitude = abs(speed);
}
inline void setDirection(double angleRad) {
	direction = angleRad;
	calcUnitVec();
}

void PhysVector::calcUnitVec()
{
	unitVector.i = cos(direction);
	unitVector.j = -sin(direction);
}

PhysVector operator-(const PhysVector & rhs) {
	return PhysVector(get_i() - rhs.get_i(), get_j() - rhs.get_j(), true);
}

void PhysVector::limit(double max_mag)
{
	if (abs(magnitude) > abs(max_mag)) {
		if ((magnitude < 0 && max_mag < 0) || (magnitude >= 0 && max_mag >= 0))
			magnitude = max_mag;
		else if (magnitude < 0)
			magnitude = -max_mag;
		else if (max_mag < 0)
			magnitude = abs(max_mag);
	}
}
You don’t need quaternions. They exist to avoid gimbal lock in 3D affine transformations. And they don’t absolve you of trigonometric functions. You just have to pack them in a transformation matrix instead.

Most of your math looks fine — I do not know why you are keeping the velocity in (magnitude + unit vector) instead of combining them into a single vector value. If you have a direction, then magnitude should never be negative.

IMO, a good way to figure out what is going on is to just put an output somewhere (either print to the console or put a trace on the variables with a debugger) and watch the values of a single fish.

From that you should get a good idea of what triggers the invalid values, then spend some time watching the code that does that.
limit could be a little simpler. std::max() & min ... eg magnitude = min(abs(max_mag), abs(magnitude) ) //if its bigger than max, set to max. And you can get the sign as well.. char sign = (magnitude/abs(magnitude)); ... answer*= sign; //correct the sign without all the logic.

not important to your issue, just noted in passing.

magnitude is assigned via abs. do you need to worry about its sign?

I don't see a math error. I agree with above: print trace it to see what happened.





Ok, I was using the steering force to move the fish and I wasn't updating velocity, so it was using the original values for velocity regardless of steering.
Yeah! Glad you figured it out!
Topic archived. No new replies allowed.