views:

161

answers:

6

Given four points in the plane, A,B,X,Y, I wish to determine which of the following two angles is smaller ∢ABX or ∢ABY.

The angle ∢ABX is defined as the angle of BX, when AB is translated to lie on the open segment (-∞,0]. Intuitively when saying ∢ABX I mean the angle you get when you turn left after visiting vertex B.

I'd rather not use cos or sqrt, in order to preserve accuracy, and to minimize performance (the code would run on an embedded system).

In the case where A=(-1,0),B=(0,0), I can compare the two angles ∢ABX and ∢ABY, by calculating the dot product of the vectors X,Y, and watch its sign.

What I can do in this case is:

  1. Determine whether or not ABX turns right or left
  2. If ABX turns left check whether or not Y and A are on the same side of the line on segment BX. If they are - ∢ABX is a smaller than ABY.
  3. If ABX turns right, then Y and A on the same side of BX means that ∢ABX is larger than ∢ABY.

But this seems too complicated to me.

Any simpler approach?

A: 
duffymo
That's a good point. The angle `∢ABX` is defined as the angle of `BX`, when `AB` is translated to lie on the open segment (-∞,0]. Intuitively I mean the angle `ABX` when you turn left after visiting vertex `B`.
Elazar Leibovich
A: 

Use the law of cosines: a**2 + b**2 - 2*a*b*cos(phi) = c**2

where a = |ax|, b =|bx| (|by|), c=|ab| (|ay|) and phi is your angle ABX (ABY)

sizzzzlerz
How do you get these distances without sqrt?
The idea of "no square root" has to go.
duffymo
You don't need sqrt. You compare the cos(phi1) with cos(phi2). THe larger of the two is the larger angle.
sizzzzlerz
Really? What do you do about the discontinuity at x = 0?
duffymo
You will need sqrt to compute the `2*a*b` term (which you need to do to solve for `cos(phi)`). Also, the larger of `cos(phi1)`, `cos(phi2)` will correspond to the *smaller* angle.
Peter Milley
D'oh! You're right, Peter. Never mind.
sizzzzlerz
+1  A: 

Center the origin on B by doing

X = X - B
Y = Y - B
A = A - B

EDIT: you also need to normalise the 3 vectors

A = A / |A|
X = X / |X|
Y = Y / |Y|

Find the two angles by doing

acos(A dot X)
acos(A dot Y)

===

I don't understand the point of the loss of precision. You are just comparing, not modifying in any way the coordinates of the points...

nico
You have assumed that the magnitudes of A and X are 1 (or reciprocals of each other). The same goes for A and Y.
Justin Peel
@Justin Peel: yes I know, I was editing the answer...
nico
+2  A: 

Here's some pseudocode. Doesn't detect the case when both angles are the same. Also doesn't deal with angle orientation, e.g. assumes all angles are <= 180 degrees.

v0 = A-B
v1 = X-B
v2 = Y-B

dot1 = dot(v0, v1)
dot2 = dot(v0, v2)

if(dot1 > 0)
  if(dot2 < 0)
    // ABX is smaller
  if(dot1 * dot1 / dot(v1,v1) > dot2 * dot2 / dot(v2, v2) )
    // ABX is smaller
  // ABY is smaller

if(dot2 > 0)
  // ABY is smaller
if(dot1 * dot1 / dot(v1,v1) > dot2 * dot2 / dot(v2,v2) )
  // ABY is smaller
// ABX is smaller

Note that much of this agonizing pain goes away if you allow taking two square roots.

A: 

I am not sure if you can get away without using sqrt. Simple:

AB = A-B/|A-B|
XB = X-B/|X-B|
YB = Y-B/|Y-B|

if(dot(XB,AB) > dot (YB,AB)){
 //<ABY is grater
}
else
{
...
}
Yogi
I described an approach in the question which doesn't use square root. I'll be glad to hear why won't my approach work.
Elazar Leibovich
+1  A: 

You might want to check out Rational Trigonometry. The ideas of distance and angle are replaced by quadrance and spread, which don't involve sqrt and cos. See the bottom of that webpage to see how spread between two lines is calculated. The subject has its own website and even a youtube channel.

brainjam