{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeFamilies #-} module Apecs.Physics.Gloss ( BodyPicture (..), Camera (..), toPicture, drawWorld, applyView, mouseToWorld, v2ToTuple, defaultSimulate, ) where import Apecs import Apecs.Physics import Data.Foldable (fold) import Data.Semigroup (Semigroup(..)) import qualified Graphics.Gloss as G import Graphics.Gloss.Geometry.Angle (radToDeg) import qualified Graphics.Gloss.Interface.IO.Simulate as GS newtype BodyPicture = BodyPicture G.Picture deriving Monoid instance Semigroup BodyPicture where BodyPicture pa <> BodyPicture pb = BodyPicture (mappend pa pb) instance Component BodyPicture where type Storage BodyPicture = Map BodyPicture data Camera = Camera { gvOffset :: V2 Double , gvScale :: Double } instance Semigroup Camera where Camera p1 z1 <> Camera p2 z2 = Camera (p1 + p2) (z1 * z2) instance Monoid Camera where mempty = Camera 0 1 mappend = (<>) instance Component Camera where type Storage Camera = Global Camera applyView :: Camera -> G.Picture -> G.Picture applyView (Camera (V2 x y) scale) = G.Scale (realToFrac scale) (realToFrac scale) . G.Translate (realToFrac . negate $ x) (realToFrac . negate $ y) mouseToWorld :: (Float,Float) -> Camera -> V2 Double mouseToWorld (x,y) (Camera offset scale) = (/scale) <$> (V2 (realToFrac x) (realToFrac y))-offset toPicture :: Convex -> G.Picture toPicture (Convex [V2 x y] radius) = G.Translate (realToFrac x) (realToFrac y) $ G.Circle (realToFrac radius) toPicture (Convex [a,b] _) = G.Line [v2ToTuple a, v2ToTuple b] toPicture (Convex verts _) = G.Polygon (v2ToTuple <$> verts) v2ToTuple :: V2 Double -> (Float, Float) v2ToTuple (V2 x y) = (realToFrac x, realToFrac y) drawWorld :: ( Has w IO Physics , Has w IO BodyPicture , Has w IO Camera) => System w G.Picture drawWorld = do pics <- flip cfold [] $ \ps ((Position (V2 x y), Angle theta, BodyPicture pic)) -> (G.Translate (realToFrac x) (realToFrac y) . G.Rotate (negate . radToDeg . realToFrac $ theta) $ pic) : ps view <- get global return . applyView view . G.pictures $ pics defaultSimulate :: ( Has w IO Physics , Has w IO BodyPicture , Has w IO Camera) => w -> String -> IO () defaultSimulate w name = GS.simulateIO (GS.InWindow name (640,480) (100,100)) G.black 60 w render stepper where render w = runSystem drawWorld w stepper _ dT w = runSystem (stepPhysics (1/60)) w >> return w