{-# LANGUAGE NamedFieldPuns #-}

-- | Geometric utility functions.
module Geometry
  ( Point(..)
  , Line(..)
  , Angle(..)
  , intersectLineLine
  , distanceFromOrigin
  , distanceLineFromOrigin
  , perpendicularThroughPoint
  , angleFromOrigin
  , normalizeAngle
  , lengthOfRayUntilIntersect
  ) where

import           Data.Fixed (mod')

data Point =
  Point Double
        Double

data Line = Line
  { Line -> Double
slope     :: Double
  , Line -> Double
intercept :: Double
  }

newtype Angle =
  Radians Double

intersectLineLine :: Line -> Line -> Point
intersectLineLine :: Line -> Line -> Point
intersectLineLine Line
a Line
b =
  let x :: Double
x = (Line -> Double
intercept Line
a Double -> Double -> Double
forall a. Num a => a -> a -> a
- Line -> Double
intercept Line
b) Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ (Line -> Double
slope Line
b Double -> Double -> Double
forall a. Num a => a -> a -> a
- Line -> Double
slope Line
a)
      y :: Double
y = Line -> Double
slope Line
a Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
x Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Line -> Double
intercept Line
a
  in Double -> Double -> Point
Point Double
x Double
y

distanceFromOrigin :: Point -> Double
distanceFromOrigin :: Point -> Double
distanceFromOrigin (Point Double
x Double
y) = Double -> Double
forall a. Floating a => a -> a
sqrt (Double
x Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
x Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double
y Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
y)

distanceLineFromOrigin :: Line -> Double
distanceLineFromOrigin :: Line -> Double
distanceLineFromOrigin Line {Double
slope :: Line -> Double
slope :: Double
slope, Double
intercept :: Line -> Double
intercept :: Double
intercept} =
  Double -> Double
forall a. Num a => a -> a
abs Double
intercept Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double -> Double
forall a. Floating a => a -> a
sqrt (Double
slope Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
slope Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double
1)

perpendicularThroughPoint :: Line -> Point -> Line
perpendicularThroughPoint :: Line -> Point -> Line
perpendicularThroughPoint Line {Double
slope :: Line -> Double
slope :: Double
slope} (Point Double
x Double
y) =
  let slope' :: Double
slope' = -Double
1 Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
slope
      intercept' :: Double
intercept' = Double
y Double -> Double -> Double
forall a. Num a => a -> a -> a
- Double
slope' Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
x
  in Line {slope :: Double
slope = Double
slope', intercept :: Double
intercept = Double
intercept'}

angleFromOrigin :: Point -> Angle
angleFromOrigin :: Point -> Angle
angleFromOrigin (Point Double
x Double
y) = Double -> Angle
Radians (Double -> Angle) -> Double -> Angle
forall a b. (a -> b) -> a -> b
$ Double -> Double -> Double
forall a. RealFloat a => a -> a -> a
atan2 Double
y Double
x

normalizeAngle :: Angle -> Angle
normalizeAngle :: Angle -> Angle
normalizeAngle (Radians Double
theta) = Double -> Angle
Radians (Double -> Angle) -> Double -> Angle
forall a b. (a -> b) -> a -> b
$ Double -> Double -> Double
forall a. Real a => a -> a -> a
mod' Double
theta (Double
2 Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
forall a. Floating a => a
pi)

lengthOfRayUntilIntersect :: Angle -> Line -> Double
lengthOfRayUntilIntersect :: Angle -> Line -> Double
lengthOfRayUntilIntersect (Radians Double
theta) Line {Double
slope :: Line -> Double
slope :: Double
slope, Double
intercept :: Line -> Double
intercept :: Double
intercept} =
  Double
intercept Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ (Double -> Double
forall a. Floating a => a -> a
sin Double
theta Double -> Double -> Double
forall a. Num a => a -> a -> a
- Double
slope Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double -> Double
forall a. Floating a => a -> a
cos Double
theta)