packages feed

edge-0.8.2: Moving.hs

module Moving where

import Data.WrapAround
import qualified Data.WrapAround as W
import Trigonometry
import Data.List (find)
import Control.Monad (join)
import Common

maxExpectedVelocity = 1000 -- should equal max velocity of fastest object in arena

calcNewVelocity
  :: Velocity -> Double -> Angle -> Double -> Time -> (Double, Double)
calcNewVelocity oVelocity accelerationRate thrustAngle maxVelocity t =
  let (vXo, vYo) = oVelocity in
  let acceleration = t * accelerationRate in
  let dX = cos thrustAngle * acceleration in
  let dY = sin thrustAngle * acceleration in
  let vXn = vXo + dX in
  let vYn = vYo + dY in
  let magVN' = sqrt (vXn ** 2 + vYn ** 2) in
  let magVN = if magVN' == 0 then 0.1 -- erm... necessary?
                            else magVN' in
  let aVN = if vXn >= 0 then asin (vYn / magVN)
                       else pi - asin (vYn / magVN) in
  let finalMag = min magVN maxVelocity in
  let finalX = cos aVN * finalMag in
  let finalY = sin aVN * finalMag in
  (finalX, finalY)

idealNewLocation :: WrapMap -> WrapPoint -> Velocity -> Time -> WrapPoint
idealNewLocation wmap oLocation (velX, velY) t =
  addPoints' wmap oLocation velocity'
  where velocity' = (velX * t, velY * t)

class Locatable a where
  center :: a -> WrapPoint

class (Locatable a) => Moving a where

  velocity :: a -> Velocity

class (Moving a) => Colliding a where

  collisionRadius :: a -> Double

data Collision = Collision { time :: Time -- since start of collision detection
                                           -- window frame
                           , center1 :: WrapPoint -- at point of collision
                           , center2 :: WrapPoint -- likewise
                           }

-- Double refers to time window for checking for collision
collision :: (Colliding a, Colliding b)
  => WrapMap
  -> Time
  -> a
  -> b
  -> Maybe Collision
collision wmap tw obj1 obj2 =
  let r1 = collisionRadius obj1 in
  let r2 = collisionRadius obj2 in
  let v1 = velocity obj1 in
  let v2 = velocity obj2 in
  let dD = Trigonometry.distance v1 v2 in
  let ts = fromIntegral $ ceiling (dD / (r1 + r2)) in
  let ti = tw / ts in
  let tpoints = [ x * ti | x <- [0..ts] ] in
  let o1 = center obj1 in
  let o2 = center obj2 in
  let points = [ ( addPoints' wmap o1 (mulSV t v1)
                 , addPoints' wmap o2 (mulSV t v2)
                 ) | t <- tpoints ] in
  let distances = [ W.distance wmap p1 p2 | (p1, p2) <- points ] in
  let zipped = zip3 tpoints points distances in
  do (t, (p1, p2), _) <- find (\(_, _, d) -> d <= r1 + r2) zipped
     Just Collision { time = t
                    , center1 = p1
                    , center2 = p2
                    }

collisionWindow :: (Colliding a, Colliding b) => WrapMap -> Double -> a -> b -> Bool
collisionWindow wmap range obj1 obj2 =
  W.distance wmap (center obj1) (center obj2) <= range

collisionScan :: (Colliding a, Colliding b) => WrapMap -> a -> [b] -> Time -> Maybe Collision
collisionScan wmap c cs t =
  let relAsteroids =
        [ a | a <- cs
        , let range = max (maxExpectedVelocity * 2 * t)
                          (collisionRadius c + collisionRadius a) in
              collisionWindow wmap range c a
        ] in
  let collisions = map (collision wmap t c) relAsteroids in
  join (find (\a -> case a of
                      Nothing -> False
                      otherwise -> True) collisions)