Trouble determining angle between vectors / points

I’m trying to use nalgebra to manage some basic movement and detection in a 2D plane. The functions I need are:

  • determine the angle between two position vectors (should I use a point instead a pos vector?)
  • advance a position vector by some magnitude (e.g. move an object along its current heading by X units)

I’m able to use atan2 and some other ugliness to manually create the angle between two points, but no matter what I try for a source and target vector (or point by using coords), the angle returns 0.

Any help would be greatly appreciated!

Hi!

It generally makes more sense to use points for positions.
Did you try the v1.angle(&v2) method for computing the angle between the two vectors v1 and v2?

Yes, this is the code I’ve got:

pub fn heading_to_target(source: &Vector2<f32>, target: &Vector2<f32>) -> f32 {
    let angle = source.angle(target);
    println!("angle {} - {} = {}", source, target, angle);
    angle
}

This returns 0 for the angle no matter what vectors I pass.

And this is what I’m using to do it the “old fashioned way” with atan2:

pub fn heading(x: f32, y: f32, tx: f32, ty: f32) -> f32 {
    (ty - y).atan2(tx - x).to_degrees()
}

This is surprising! Could you please provide an example of output of your println on the heading_to_target?

Here’s one test:

angle 
  ┌   ┐
  │ 0 │
  │ 0 │
  └   ┘

 - 
  ┌     ┐
  │ 100 │
  │  50 │
  └     ┘

 = 0

And these are the vectors I pass:

 let source = Vector2::new(0.0f32, 0.0);
 let target = Vector2::new(100.0f32, 50.0);

 let heading = ScannerSystem::heading_to_target(&source, &target);

@sebcrozet - You think it has something to do with the normalization code?

if n1.is_zero() || n2.is_zero() {
        N::zero()

I’ve been able to get some large numbers to produce an angle, but small ones seem to be producing zero. If this is the case, how does one calculate the angle between points if one of the points is at or near the origin?

Thank you for the example!

I have a better understanding of what you are trying to do here. You are actually not trying to compute the angle between source and target. What you want is the angle between target - source and the x coordinate axis. There is no method dedicated to this in nalgebra. Something similar, but outputting a rotation matrix (or quaternion) is implemented for 3D but not for 2D). We will consider adding it for 2D too: I’ve created the issue nalgebra#492.

In the mean time, the heading can be computed in the following way:

// You can use a UnitComplex instead of Rotation2 if you prefer.
let heading = Rotation2::rotation_between(Vector2::x(), &(target - source));
let angle = heading.angle();

// For your second question:
let point = source + (Rotation2::new(angle) * Vector2::x()) * distance;

// Alternatively, if you just store the Rotation2 computed with `rotation_between` to avoid reconstructing it from the angle.
let point = source + (heading * Vector2::x()) * distance;

Also note that you could simply do the following to advance a source toward the heading:

let dir = (target - source).normalize();
let point = source + dir * distance;

Ugh I can’t believe I didn’t see that. You’re right, want I want is a heading for source to reach target, which is the relative difference between each of their rotations from the X axis. In the rotation between code where you’re subtracting target and source, are those Points or Vectors? Does it change how you calculate the heading if you use points instead of vectors for position?

Semantically speaking points should be seen as absolute location in space (assuming some coordinate system) while vectors are relative (or can be seen as a displacement between two points in space). So it would make more sense for the target and source to be point even if, in practice, representing them as vectors would yield the same results in the code I cited above. The difference of two poins actually returns a vector (because the vector represents the displacement from one point to the other).

Note however that the difference between points and vectors becomes very significant if you use transformation types (like Isometry2 or Similarity2) that include a translation part. Multiplying such a transformation to a vector will ignore the translation part while points will be affected by the translation part too.