0

I need to calculate the angle between 3 points. I've done that using vectors, it looks like it's working, but sometimes I get NaN as a result. To calculate the angle I used the arcos(dot(v1,v2)/(length(v1)*length(v2))) formula. Here's the code:

private static double angleBetween(Point previous, Point center, Point next) {
        Vector2D vCenter = new Vector2D(center.x, center.y );
        Vector2D vPrev = new Vector2D(previous.x, previous.y );
        Vector2D vNext = new Vector2D(next.x, next.y );

        Vector2D a = vCenter.minus(vPrev);
        Vector2D b = vCenter.minus(vNext);

        double dot = Vector2D.dot(a, b);

        double la = a.length();

        double lb = b.length();

        double l = la*lb;

        double acos = Math.acos(dot/l);

        double deg = Math.toDegrees(acos);

        return deg;

        }

Vector2D class:

public double length() {
        return Math.sqrt(x * x + y * y);
    };

public static double dot(Vector2D v1, Vector2D v2) {
        return v1.x * v2.x + v1.y * v2.y;
    };

public Vector2D minus(Vector2D v) {
        return new Vector2D(x - v.x, y - v.y);
    };

Debugging the program I've discovered why this happens. for example let be:

center = (127,356)
previous = (117,358)
next = (137,354)

//calculating the vectors
a = (-10,2) //center - prev
b = (10,-2) //center - next

dot = -10*10 + 2*-2 = 104

la = sqrt(-10*-10 + 2*2) = sqrt(104) = see attachment
lb = sqrt(10*10 + -2*-2) = sqrt(104) = see attachment

l = la * lb = see attachment

acos = NaN because dot/l>1 because I lost precision because of sqrt() which didn't give me the exact value therefore la*lb isn't 104.

now as far as I know double is the most precise number type in java. How can I solve this problem?

attchment: la,lb,l values

PS It may looks like a very rare situation, but I'm experiencing quite a lot of them, so I can't just ignore it.

jack_the_beast
  • 1,838
  • 4
  • 34
  • 67
  • java.lang.BigDecimal has infinite precision in Java, but the problem is that functions such as sqrt, arccos, etc. cannot be calculated with finite precision. But you could round the result to some reasonable precision if necessary. – ike3 Apr 29 '16 at 13:14

2 Answers2

1

The best way to solve this problem is to use an appropriate data type like java.math.BigDecimal and define a precision for you computation using an instance of type java.math.MathContext. For instance:

double l = la*lb;
BigDecimal lWrapper = new BigDecimal(l, new MathContext(5));
l = lWrapper.doubleValue();

There is an other way to work around this problem. Use the following formula:

angle = atan(length(crossProduct(a, b)) / dotProduct(a, b)) // Because the domain of definition of the tan function is R

Derivation of the formula:

  cos(angle) = dotProduct(a, b)   / (length(a) * length(b)) and
  sin(angle) = length(crossProduct(a, b)) / (length(a) * length(b))

One has

 tan(angle) = sin(angle) / cos(angle)

so

tan(angle) = length(crossProduct(a, b)) / (length(a) * length(b)) / dotProduct(a, b)   / (length(a) * length(b))
tan(angle) = length(crossProduct(a, b)) / dotProduct(a, b)

Applying the invert function of tan:

angle = atan(length(crossProduct(a, b)) / dotProduct(a, b))

The cross product of A, B ∈ ℜ2:

|| A x B || = det(A,B) = ((A.x * B.y) - (A.y * B.x))

Remarks:

  1. ||x|| is the length of the vector x ⇔ length(a)
  2. ∀ A, B ∈ ℜ2: || A x B || equal the determinant of A, B
  3. You can use the sign(|| A x B ||) to find out the orientation
Ayoub Falah
  • 484
  • 3
  • 19
  • Hi, that makes sense but I'm struggling on calculating the cross product of 2D vectors, as far as I know cross product is only defined for 3D vectors at least. Am I missing something? how should I calculate it? – jack_the_beast May 02 '16 at 10:44
  • Yes, the **cross product** is only defined for vectors in 3D. But for each vector V = (x, y) in 2D there is an equivalent representation of V in 3D, namely V = (x y 0). Hence you can use this interpretation to perform the cross product. – Ayoub Falah May 05 '16 at 13:26
  • 1
    I updated my answer. I added the formula that can be used to compute the cross product and some usefuls remarks – Ayoub Falah May 05 '16 at 13:47
0

The angle between vectors that are colinear is 180 degrees. Using arccos won't work very well because arccos uses pythagoras' theorem to derive the angle and pythagoras' theorem only works with triangles. Two colinear vectors do not make a triangle.

The simplest answer is to check for Nan and special case it.

double deg = Math.toDegrees(Double.isNaN(acos) ? Math.PI : acos);
OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213