Bug 1316 : PVector.angleDistance() returns NaN in 1.0.6
Last modified: 2009-11-22 09:45




Status:
RESOLVED
Resolution:
FIXED -
Priority:
P2
Severity:
normal

 

Reporter:
Dan Bernier
Assigned To:
fry

Attachment Type Created Size Actions

Description:   Opened: 2009-08-31 10:36
Processing version 1.0.3
MS Vista, Dell Latitude D820

Based on this discussion:
http://processing.org/discourse/yabb2/YaBB.pl?num=1244554381/0#0

When calculating the angle between equal vectors, Processing often returns
NaN, and it seems to be a rounding error -- internally, it uses
PVector.mag(), which casts its result to a float. Leaving it as a double
eliminates the problem, and returns a number very close to zero (the
expected result).

Some code that demonstrates the problem, and the fix:

void setup() {
// some offending vectors
PVector[] vs = new PVector[4];
vs[0] = new PVector(-1, -1);
vs[1] = new PVector(1 , -1);
vs[2] = new PVector(1 , 1);
vs[3] = new PVector(-1, 1);

for (int i = 0; i < vs.length; i++) {
println(vs[i] + "\t default: " + PVector.angleBetween(vs[i], vs[i]));
println(vs[i] + "\t doubles: " + angleBetween(vs[i], vs[i]));
println();
}
}

double mag(PVector v) {
return Math.sqrt(v.x*v.x + v.y*v.y);
}

float angleBetween(PVector v1, PVector v2) {
double dot = v1.dot(v2);
float theta = (float) Math.acos(dot / (mag(v1) * mag(v2)));
return theta;
}
Additional Comment #1 From fry 2009-08-31 12:10
k, here's the implementation that uses doubles for calculation:

static public float angleBetween(PVector v1, PVector v2) {
double dot = v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
double v1mag = Math.sqrt(v1.x * v1.x + v1.y * v1.y + v1.z * v1.z);
double v2mag = Math.sqrt(v2.x * v2.x + v2.y * v2.y + v2.z * v2.z);
return (float) Math.acos(dot / (v1mag * v2mag));
}

and it's been updated in svn for the next release of processing.
Additional Comment #2 From nervoussystem 2009-11-18 14:16
I noticed that this can still be a problem with the native Math.acos method in java
when dealing with vectors very close to parallel regardless of float versus double
arithmetic. I got around it by adding a check for a dot product close to -1 or 1
(normalized) and rounding it off.

I'm not advocated adding such a check into processing, but it is useful for people to
be aware of the potentially difficult to track down error.

-Jesse

(In reply to comment #1)
>
>
>
> Additional Comment #1 From
> fry
> 2009-08-31 12:10
>
> <!--
> addReplyLink(1); //-->[reply]
>
>
>
>
> k, here's the implementation that uses doubles for calculation:
>
> static public float angleBetween(PVector v1, PVector v2) {
> double dot = v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
> double v1mag = Math.sqrt(v1.x * v1.x + v1.y * v1.y + v1.z * v1.z);
> double v2mag = Math.sqrt(v2.x * v2.x + v2.y * v2.y + v2.z * v2.z);
> return (float) Math.acos(dot / (v1mag * v2mag));
> }
>
> and it's been updated in svn for the next release of processing.
>
>
Additional Comment #3 From fry 2009-11-22 09:45
That's a reference issue, I've filed bug #1375 to address it.