packages feed

Monadius-0.99: src/Util.hs

-- | Various numeric utility functions, particularly dealing with Complex
-- numbers and shapes.
module Util (
  ComplexShape(..),
  Shape(..),
  angleAccuracy,
  filterJust,
  infinite,
  innerProduct,
  intToGLdouble,
  isDebugMode,
  modifyArray,
  padding,
  putDebugStrLn,
  regulate,
  square,
  unitVector,
  ) where

import           Data.Array                (Array (), Ix (), (!), (//))
import           Data.Complex
import           Data.Maybe
import           Graphics.Rendering.OpenGL

-- | Switch this to True to get debug outputs. Be careful: you get a crash under
-- Microsoft Windows, because the console is not available.
isDebugMode :: Bool
isDebugMode = False

putDebugStrLn :: String -> IO ()
putDebugStrLn str = if isDebugMode then putStrLn str else return ()

filterJust :: [Maybe a] -> [a]
filterJust = map fromJust.filter isJust

-- | Modify array 'a' at index 'i' by function 'f'
modifyArray :: Ix i => i -> (e -> e) -> Array i e -> Array i e
modifyArray i f a = a // [(i,f $ a!i)]

class ComplexShape s where
  -- | Collision check
  (>?<) :: s -> s -> Bool
  -- | Translation by a vector
  (+>) :: (Complex GLdouble) -> s -> s

instance ComplexShape Shape where
  a >?< b = case (a,b) of
    (Circular{},Circular{}) -> magnitude (center a - center b) < radius a + radius b
    (Circular{},Rectangular{}) -> b >?< a
    (Rectangular{},Circular{}) -> a >?< Rectangular{bottomLeft = center b - vr,topRight = center b + vr} where
      vr = radius b :+ radius b
    (Rectangular{bottomLeft=aL:+aB,topRight=aR:+aT},Rectangular{bottomLeft=bL:+bB,topRight=bR:+bT}) ->
      and [aL < bR, aB < bT, aR > bL, aT > bB]
    (Shapes{children = ss}, c) -> or $ map (>?< c)  ss
    (d, Shapes{children = ss}) -> or $ map (d >?<)  ss
  v +> a = case a of
    Circular{}    -> a{center = center a + v}
    Rectangular{} -> a{bottomLeft = bottomLeft a + v, topRight = topRight a + v}
    Shapes{}      -> a{children = map (v +>) $ children a}

data Shape = Circular {center :: Complex GLdouble, radius :: GLdouble} |
             Rectangular {bottomLeft :: Complex GLdouble, topRight :: Complex GLdouble} |
             Shapes {children :: [Shape]}

-- | Put a Rectangle coordinates into normal order so that collision will go properly.
regulate :: Shape -> Shape
regulate Rectangular{bottomLeft=(x1:+y1),topRight=(x2:+y2) }= Rectangular (min x1 x2:+min y1 y2) (max x1 x2:+max y1 y2)
regulate ss@Shapes{} = ss{children = map regulate $ children ss}
regulate x = x

intToGLdouble :: Int -> GLdouble
intToGLdouble = fromIntegral

unitVector :: Complex GLdouble -> Complex GLdouble
unitVector z
    | magnitude z <= 0.00000001 = 1:+0
    | otherwise                 = z / abs z

angleAccuracy :: Int -> Complex GLdouble -> Complex GLdouble
angleAccuracy division z = mkPolar r theta
    where
      (r,t)=polar z
      theta = (intToGLdouble $ round (t / (2*pi) * d))/d*2*pi
      d = intToGLdouble division

innerProduct :: Complex GLdouble -> Complex GLdouble -> GLdouble
innerProduct a b = realPart $ a * (conjugate b)

padding :: Char -> Int -> String -> String
padding pad minLen str = replicate (minLen - length str) pad ++ str

infinite :: Int
infinite = 9999999

square :: (Num a) => a -> a
square a = a * a