module Data.Geometry.Angle where import Data.Fixed (mod') newtype Angle = Angle Double deriving (Eq, Show, Read, Ord) ccwFrom :: Angle -> Angle -> Bool (Angle x) `ccwFrom` (Angle y) | x > y && x - y < pi = True | x < y && y - x >= pi = True | otherwise = False ccwEqFrom :: Angle -> Angle -> Bool a1 `ccwEqFrom` a2 = a1 == a2 || a1 `ccwFrom` a2 cwFrom :: Angle -> Angle -> Bool cwFrom a b = not $ ccwEqFrom a b cwEqFrom :: Angle -> Angle -> Bool cwEqFrom a b = not $ ccwFrom a b instance Num Angle where fromInteger = mkAngle . fromInteger (+) = error "Angles can't be added" (*) = error "Angles can't be multiplied" abs = error "Angles don't have absolute values" signum = error "Angles don't have signum" negate = error "Angle can't be negated" instance Fractional Angle where fromRational = mkAngle . fromRational recip = error "Angles don't have a reciprocal" sin' :: Angle -> Double sin' (Angle x) = sin x cos' :: Angle -> Double cos' (Angle x) = cos x data AngleSpan = AngleSpan !Angle !Angle deriving (Eq, Show, Read) mkAngle :: Double -> Angle mkAngle ang | ang >= 0 && ang < 2 * pi = Angle ang | otherwise = Angle $! ang `mod'` (2 * pi) angleInSpan :: Angle -> AngleSpan -> Bool angleInSpan ang (AngleSpan first second) = (ang `ccwEqFrom` first && second `ccwEqFrom` ang) || (ang `ccwEqFrom` first && first `ccwFrom` second) || (ang `cwEqFrom` second && second `cwFrom` first) compareRelative :: Angle -> Angle -> Angle -> Ordering compareRelative orig a b | a == b = EQ | a `ccwFrom` orig && b `cwFrom` orig = LT | b `ccwFrom` orig && a `cwFrom` orig = GT | a `ccwFrom` b = GT | otherwise = LT