{-|
Module      : Codec.Picture.Geometry
Description : Essential geometry for 2D shapes
Copyright   : (C) Richard Cook, 2018
Licence     : MIT
Maintainer  : rcook@rcook.org
Stability   : stable
Portability : portable

Essential geometric functions to generate 2D shapes and paths
-}

module Codec.Picture.Geometry
    ( FPoint2D
    , Point2D
    , centroid
    , clockwise
    , closed
    ) where

import           Data.Foldable (foldl')
import           Data.List (sortBy)
import           Numeric.Extras (fmod)

-- | 2D coordinate
type Point2D = (Int, Int)

-- | 2D coordinate
type FPoint2D = (Double, Double)

-- | Compute the centroid of a polygon
centroid ::
    [Point2D]           -- ^ sequence of vertices
    -> Maybe FPoint2D   -- ^ centroid
centroid [] = Nothing
centroid ps =
    let (totalX, totalY, n') = foldl' (\(accX, accY, n) (x, y) -> (accX + x, accY + y, n + 1)) (0, 0, 0 :: Int) ps
        n'' = fromIntegral n'
    in Just (fromIntegral totalX / n'', fromIntegral totalY / n'')

-- | Closed sequence of vertices
closed ::
    [Point2D]       -- ^ sequence of vertices
    -> [Point2D]    -- ^ sequence of vertices
closed [] = []
closed ps@(p : _ ) = ps ++ [p]

-- | Sequence of vertices in clockwise order relative to its centroid
clockwise ::
    [Point2D]       -- ^ sequence of vertices
    -> [Point2D]    -- ^ sequence of vertices
clockwise ps =
    case centroid ps of
        Just (cx, cy) -> sortBy (\(ax, ay) (bx, by) ->
            let a1 = angle (fromIntegral ax) (fromIntegral ay) cx cy
                a2 = angle (fromIntegral bx) (fromIntegral by) cx cy
            in compare a2 a1) ps
        Nothing -> []

angle :: Double -> Double -> Double -> Double -> Double
angle x y cx cy = fmod (degrees (atan2 (x - cx) (y - cy)) + 360) 360

degrees :: Double -> Double
degrees rad = rad * pi / 180