But the output of unangle is on [-pi/2, 3pi/2], while the output of atan2 is on [-pi,pi]:
> do printf "unangle, atan2, angle\n" ; sequence_ [ printf "%.2f %.2f %.2f\n" (unangle v) (atan2 y x) ang | ang <- [ -2*pi, -2*pi + pi/4 .. 2*pi ], let v = angle ang, let V2 x y = v ] :: IO ()
unangle, atan2, angle
0.00 0.00 -6.28
0.79 0.79 -5.50
1.57 1.57 -4.71
2.36 2.36 -3.93
3.14 -3.14 -3.14
3.93 -2.36 -2.36
-1.57 -1.57 -1.57
-0.79 -0.79 -0.79
0.00 0.00 0.00
0.79 0.79 0.79
1.57 1.57 1.57
2.36 2.36 2.36
3.14 3.14 3.14
3.93 -2.36 3.93
4.71 -1.57 4.71
-0.79 -0.79 5.50
-0.00 -0.00 6.28
You could keep the old range but I suspect it's not necessary:
-- | output on [-pi/2, 3pi/2]
unangle (V2 x y) = case atan2 y x of
theta
| theta < -pi/2 -> theta + 2*pi
| otherwise -> theta
A bigger problem with the new definition is that the type will change. Some users of unangle need new RealFloat instances:
unangle :: (Floating a, Ord a) => V2 a -> a
atan2 :: RealFloat a => a -> a -> a
instance Floating a => Floating (V2 a) -- exists
instance RealFloat a => RealFloat (V2 a) where
atan2 = liftA2 atan2 -- missing for good reason.. how to write encode/decodeFloat?
OverlappingInstances, TypeFamilies or maybe rewrite rules are another option to get atan2 for Float and Double, but keep asin for other types.
I propose
unangle (V2 x y) = atan2 y x
because it's shorter and numerically better:But the output of unangle is on [-pi/2, 3pi/2], while the output of atan2 is on [-pi,pi]:
You could keep the old range but I suspect it's not necessary:
A bigger problem with the new definition is that the type will change. Some users of unangle need new RealFloat instances:
OverlappingInstances, TypeFamilies or maybe rewrite rules are another option to get atan2 for Float and Double, but keep asin for other types.