free-game 1.0.3 → 1.0.4
raw patch · 11 files changed
+68/−109 lines, 11 filesdep +boundingboxesdep +lensdep ~freedep ~linear
Dependencies added: boundingboxes, lens
Dependency ranges changed: free, linear
Files
- FreeGame.hs +4/−1
- FreeGame/Backend/GLFW.hs +9/−1
- FreeGame/Class.hs +2/−0
- FreeGame/Data/Font.hs +1/−1
- FreeGame/Instances.hs +3/−5
- FreeGame/Internal/GLFW.hs +18/−10
- FreeGame/Internal/Raindrop.hs +0/−32
- FreeGame/Text.hs +9/−8
- FreeGame/Types.hs +3/−41
- FreeGame/UI.hs +12/−4
- free-game.cabal +7/−6
FreeGame.hs view
@@ -16,6 +16,8 @@ runGameDefault, WindowMode(..), BoundingBox(..), + inBoundingBox, + _Corners, delay, tick, foreverFrame, @@ -104,7 +106,8 @@ import Control.Bool import Data.Color import Data.Color.Names -import Linear hiding (rotate) +import Linear +import Data.BoundingBox.Dim2 (BoundingBox(..), inBoundingBox, _Corners) import Control.Monad.Trans.Iter -- | 'Game' is a kind of procedure but you can also use it like a value.
FreeGame/Backend/GLFW.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE Rank2Types, BangPatterns #-} +{-# LANGUAGE Rank2Types, BangPatterns, ViewPatterns #-} {-# LANGUAGE ScopedTypeVariables #-} {-# OPTIONS_GHC -fno-warn-orphans #-} ----------------------------------------------------------------------------- @@ -19,6 +19,7 @@ import Control.Monad.Reader import Data.IORef import Data.Reflection +import Data.BoundingBox.Dim2 import FreeGame.Class import FreeGame.Internal.Finalizer import FreeGame.UI @@ -32,6 +33,7 @@ import Unsafe.Coerce import Codec.Picture.RGBA8 import Control.Concurrent +import Control.Lens (view) keyCallback :: IORef (Map.Map Key ButtonState) -> GLFW.Window -> GLFW.Key -> Int -> GLFW.KeyState -> GLFW.ModifierKeys -> IO () keyCallback keyBuffer _ key _ GLFW.KeyState'Pressed _ = modifyIORef' keyBuffer $ Map.adjust buttonDown (toEnum $ fromEnum key) @@ -147,6 +149,12 @@ (_, f) <- runFinalizerT $ iterM runUI m modifyIORef' given (f:) cont +runUI (GetBoundingBox cont) = liftIO (readIORef (G.refRegion given)) >>= cont +runUI (SetBoundingBox bbox@(view (size C)-> V2 w h) cont) = do + liftIO $ GLFW.setWindowSize (G.theWindow given) (floor w) (floor h) + liftIO $ writeIORef (G.refRegion given) bbox + cont + mapReaderWith :: (s -> r) -> (m a -> n b) -> ReaderT r m a -> ReaderT s n b mapReaderWith f g m = unsafeCoerce $ \s -> g (unsafeCoerce m (f s))
FreeGame/Class.hs view
@@ -28,6 +28,8 @@ infixr 5 `scale` infixr 5 `color` infixr 5 `colored` +infixr 5 `thickness` +infixr 5 `blendMode` class Functor p => Affine p where -- | (radians)
FreeGame/Data/Font.hs view
@@ -28,7 +28,7 @@ import qualified Data.Map as M import qualified Data.Vector.Storable as V import Linear -import FreeGame.Types +import Data.BoundingBox.Dim2 import FreeGame.Class import FreeGame.Data.Bitmap import FreeGame.Internal.Finalizer
FreeGame/Instances.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE Rank2Types #-} +{-# LANGUAGE Rank2Types, ImpredicativeTypes #-} {-# LANGUAGE CPP #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module FreeGame.Instances () where @@ -79,11 +79,9 @@ hideCursor = (l) hideCursor; \ clearColor c = (l) (clearColor c); \ getFPS = (l) getFPS; \ + getBoundingBox = (l) getBoundingBox; \ + setBoundingBox b = (l) (setBoundingBox b); \ } - -hoistF :: (Functor f, Functor g) => (forall x. f x -> g x) -> Church.F f a -> Church.F g a -hoistF t = Church.iterM (wrap . t) -{-# INLINE hoistF #-} MK_AFFINE(_COMMA_ Functor m, F m, hoistF) MK_AFFINE(_COMMA_ Functor m, Free.Free m, Free.hoistFree)
FreeGame/Internal/GLFW.hs view
@@ -7,6 +7,7 @@ import Data.Color import Data.IORef import FreeGame.Types +import Data.BoundingBox.Dim2 import Graphics.Rendering.OpenGL.GL.StateVar import Graphics.Rendering.OpenGL.Raw.ARB.Compatibility import Linear @@ -23,7 +24,7 @@ , refFPS :: IORef Int , theFPS :: IORef Int , currentFPS :: IORef Int - , theRegion :: BoundingBox Double + , refRegion :: IORef (BoundingBox Double) , theWindow :: GLFW.Window } @@ -136,9 +137,10 @@ beginFrame :: System -> IO () beginFrame sys = do + BoundingBox wl wt wr wb <- fmap realToFrac <$> readIORef (refRegion sys) + GL.viewport $= (GL.Position 0 0, GL.Size (floor $ wr - wl) (floor $ wb - wt)) GL.matrixMode $= GL.Projection GL.loadIdentity - let BoundingBox wl wt wr wb = fmap realToFrac (theRegion sys) GL.ortho wl wr wb wt 0 (-100) GL.matrixMode $= GL.Modelview 0 GL.clear [GL.ColorBuffer] @@ -157,18 +159,18 @@ GLFW.windowShouldClose (theWindow sys) withGLFW :: WindowMode -> BoundingBox Double -> (System -> IO a) -> IO a -withGLFW full bbox@(BoundingBox x0 y0 x1 y1) m = do +withGLFW mode bbox@(BoundingBox x0 y0 x1 y1) m = do let title = "free-game" ww = floor $ x1 - x0 wh = floor $ y1 - y0 () <- unlessM GLFW.init (fail "Failed to initialize") - mon <- case full of + mon <- case mode of FullScreen -> GLFW.getPrimaryMonitor - Windowed -> return Nothing + _ -> return Nothing - GLFW.windowHint (GLFW.WindowHint'Resizable False) - Just win <- GLFW.createWindow ww wh title mon Nothing + GLFW.windowHint $ GLFW.WindowHint'Resizable $ mode == Resizable + win <- GLFW.createWindow ww wh title mon Nothing >>= maybe (fail "Failed to create a window") return GLFW.makeContextCurrent (Just win) GL.lineSmooth $= GL.Enabled GL.blend $= GL.Enabled @@ -179,12 +181,18 @@ GLFW.swapInterval 1 GL.clearColor $= GL.Color4 1 1 1 1 + rbox <- newIORef bbox + + GLFW.setFramebufferSizeCallback win $ Just $ \_ w h -> do + BoundingBox x y _ _ <- readIORef rbox + writeIORef rbox $ BoundingBox x y (x + fromIntegral w) (y + fromIntegral h) + sys <- System <$> newIORef 0 <*> newIORef 0 <*> newIORef 60 <*> newIORef 60 - <*> pure bbox + <*> pure rbox <*> pure win res <- m sys @@ -195,8 +203,8 @@ screenshotFlipped :: System -> IO (Image PixelRGBA8) screenshotFlipped sys = do - let BoundingBox x0 y0 x1 y1 = theRegion sys - w = floor $ x1 - x0 + BoundingBox x0 y0 x1 y1 <- readIORef (refRegion sys) + let w = floor $ x1 - x0 h = floor $ y1 - y0 mv <- MV.unsafeNew (w * h * 4)
− FreeGame/Internal/Raindrop.hs
@@ -1,32 +0,0 @@--------------------------------------------------------------------------------- |--- Module : FreeGame.Internal.Raindrop--- Copyright : (C) 2013 Fumiaki Kinoshita--- License : BSD-style (see the file LICENSE)------ Maintainer : Fumiaki Kinoshita <fumiexcel@gmail.com>--- Stability : experimental--- Portability : non-portable------ A portable implementation of lens(<http://hackage.haskell.org/package/lens>)------------------------------------------------------------------------------module FreeGame.Internal.Raindrop (view, over) where--import Data.Functor.Identity-import Control.Monad.Reader-import Control.Applicative-import Unsafe.Coerce--(#.) :: (b -> c) -> (a -> b) -> (a -> c)-(#.) _ = unsafeCoerce-{-# INLINE (#.) #-}---- | @'view' :: 'MonadReader' s m => Getting a s a -> m a@-view :: MonadReader s m => ((a -> Const a b) -> (s -> Const a t)) -> m a-view f = asks (getConst #. f Const)-{-# INLINE view #-}---- | @'over' :: ASetter s t a b -> (a -> b) -> s -> t@-over :: ((a -> Identity b) -> (s -> Identity t)) -> (a -> b) -> s -> t-over l f = runIdentity #. l (Identity #. f)-{-# INLINE over #-}
FreeGame/Text.hs view
@@ -1,9 +1,9 @@ {-# LANGUAGE TypeSynonymInstances, FlexibleInstances, DeriveFunctor #-} module FreeGame.Text (TextF(..), TextT, runTextT, runTextT_, text) where +import Control.Lens import Data.String -import FreeGame.Types -import FreeGame.Internal.Raindrop +import Data.BoundingBox.Dim2 import FreeGame.Data.Font import FreeGame.Class import FreeGame.Instances () @@ -20,14 +20,15 @@ -- | Render a 'TextT'. runTextT :: (FromFinalizer m, Monad m, Picture2D m) => Maybe (BoundingBox Double) -> Font -> Double -> TextT m a -> m a -runTextT bbox font size = flip evalStateT (V2 x0 y0) . go where +runTextT bbox font siz = flip evalStateT (V2 x0 y0) . go where go m = lift (runFreeT m) >>= \r -> case r of Pure a -> return a Free (TypeChar '\n' cont) -> do - modify $ over _x (const x0) . over _y (+advV) + _x .= x0 + _y += advV go cont Free (TypeChar ch cont) -> do - RenderedChar bmp offset adv <- fromFinalizer $ charToBitmap font size ch + RenderedChar bmp offset adv <- fromFinalizer $ charToBitmap font siz ch pen <- get translate (pen + offset) $ bitmap bmp let pen' = over _x (+adv) pen @@ -35,8 +36,8 @@ then pen' else V2 x0 (view _y pen + advV) go cont - advV = size * (metricsAscent font - metricsDescent font) * 1.1 - (V2 x0 y0, cond) = maybe (zero, const True) (\b -> (view _TopLeft b, flip inBoundingBox b)) bbox + advV = siz * (metricsAscent font - metricsDescent font) * 1.1 + (V2 x0 y0, cond) = maybe (zero, const True) (\b -> (b ^. position TL, flip inBoundingBox b)) bbox runTextT_ :: (FromFinalizer m, Monad m, Picture2D m) => Maybe (BoundingBox Double) -> Font -> Double -> TextT m () -> m () runTextT_ = runTextT @@ -44,4 +45,4 @@ -- | Render a 'String'. text :: (FromFinalizer m, Monad m, Picture2D m) => Font -> Double -> String -> m () -text font size str = runTextT Nothing font size (fromString str)+text font siz str = runTextT Nothing font siz (fromString str)
FreeGame/Types.hs view
@@ -11,56 +11,18 @@ -- ---------------------------------------------------------------------------- module FreeGame.Types ( - WindowMode(..), - Vec2, - BoundingBox(..), - inBoundingBox, - _Corners, - _TopLeft, - _TopRight, - _BottomLeft, - _BottomRight + WindowMode(..) + , Vec2 , Key(..) , BlendMode(..) ) where import Linear.V2 -import Control.Applicative -import Data.Foldable -import Data.Traversable import Data.Typeable -data WindowMode = Windowed | FullScreen - --- | 2D bounding box -data BoundingBox a = BoundingBox a a a a deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Read, Typeable) +data WindowMode = Windowed | Resizable | FullScreen deriving (Show, Eq, Ord, Read) type Vec2 = V2 Double - --- | Determine whether the given point is in the 'BoundingBox'. -inBoundingBox :: Ord a => V2 a -> BoundingBox a -> Bool -inBoundingBox (V2 x y) (BoundingBox x0 y0 x1 y1) = x0 <= x && x <= x1 && y0 <= y && y <= y1 - --- | @'_Corners' :: Traversal' ('BoundingBox' a) ('V2' a)@ -_Corners :: Applicative f => (V2 a -> f (V2 a)) -> (BoundingBox a -> f (BoundingBox a)) -_Corners f (BoundingBox x0 y0 x1 y1) = go <$> f (V2 x0 y0) <*> f (V2 x1 y0) <*> f (V2 x1 y1) <*> f (V2 x0 y1) where - go (V2 x0' _) (V2 _ y1') (V2 x2' _) (V2 _ y3') = BoundingBox x0' y1' x2' y3' - --- | @'_TopLeft' :: Lens' ('BoundingBox' a) ('V2' a)@ -_TopLeft :: Functor f => (V2 a -> f (V2 a)) -> (BoundingBox a -> f (BoundingBox a)) -_TopLeft f (BoundingBox x0 y0 x1 y1) = fmap (\(V2 x0' y0') -> BoundingBox x0' y0' x1 y1) (f (V2 x0 y0)) - --- | @'_TopRight' :: Lens' ('BoundingBox' a) ('V2' a)@ -_TopRight :: Functor f => (V2 a -> f (V2 a)) -> (BoundingBox a -> f (BoundingBox a)) -_TopRight f (BoundingBox x0 y0 x1 y1) = fmap (\(V2 x1' y0') -> BoundingBox x0 y0' x1' y1) (f (V2 x1 y0)) - --- | @'_BottomLeft' :: Lens' ('BoundingBox' a) ('V2' a)@ -_BottomLeft :: Functor f => (V2 a -> f (V2 a)) -> (BoundingBox a -> f (BoundingBox a)) -_BottomLeft f (BoundingBox x0 y0 x1 y1) = fmap (\(V2 x0' y1') -> BoundingBox x0' y0 x1 y1') (f (V2 x0 y1)) - --- | @'_BottomRight' :: Lens' ('BoundingBox' a) ('V2' a)@ -_BottomRight :: Functor f => (V2 a -> f (V2 a)) -> (BoundingBox a -> f (BoundingBox a)) -_BottomRight f (BoundingBox x0 y0 x1 y1) = fmap (\(V2 x1' y1') -> BoundingBox x0 y0 x1' y1') (f (V2 x1 y1)) data Key = KeyUnknown
FreeGame/UI.hs view
@@ -25,6 +25,7 @@ import qualified Data.Map as Map import FreeGame.Data.Bitmap (Bitmap) import Data.Color +import Data.BoundingBox.Dim2 import Control.Monad.Free.Church import Control.Monad.Trans.Iter @@ -44,6 +45,8 @@ | ClearColor Color a | GetFPS (Int -> a) | ForkFrame (Frame ()) a + | GetBoundingBox (BoundingBox Double -> a) + | SetBoundingBox (BoundingBox Double) a deriving Functor type Game = IterT Frame @@ -66,17 +69,19 @@ reUI (ClearColor col cont) = cont <$ clearColor col reUI (GetFPS cont) = cont <$> getFPS reUI (ForkFrame m cont) = cont <$ forkFrame m +reUI (GetBoundingBox cont) = cont <$> getBoundingBox +reUI (SetBoundingBox bb cont) = cont <$ setBoundingBox bb -{-# RULES "reUI/Frame" reUI = id #-} +{-# RULES "reUI/sameness" reUI = id #-} class (Picture2D m, Local m, Keyboard m, Mouse m, FromFinalizer m) => FreeGame m where -- | Draw an action that consist of 'Picture2D''s methods. - draw :: (forall f. (Applicative f, Monad f, Picture2D f, Local f) => f a) => m a + draw :: (forall f. (Applicative f, Monad f, Picture2D f, Local f) => f a) -> m a -- | Load a 'Bitmap' to avoid the cost of the first invocation of 'bitmap'. preloadBitmap :: Bitmap -> m () -- | Run a 'Frame', and release all the matter happened. bracket :: Frame a -> m a - -- | Run a 'Frame' action concurrently. Please do not use this function to draw pictures. + -- | Run a 'Frame' action concurrently. Do not use this function to draw pictures. forkFrame :: Frame () -> m () -- | Generate a 'Bitmap' from the front buffer. takeScreenshot :: m Bitmap @@ -86,6 +91,8 @@ hideCursor :: m () clearColor :: Color -> m () getFPS :: m Int + getBoundingBox :: m (BoundingBox Double) + setBoundingBox :: BoundingBox Double -> m () instance FreeGame UI where draw = Draw @@ -103,7 +110,8 @@ hideCursor = HideCursor () clearColor c = ClearColor c () getFPS = GetFPS id - + getBoundingBox = GetBoundingBox id + setBoundingBox s = SetBoundingBox s () overDraw :: (forall m. (Applicative m, Monad m, Picture2D m, Local m) => m a -> m a) -> UI a -> UI a overDraw f (Draw m) = Draw (f m)
free-game.cabal view
@@ -1,5 +1,5 @@ name: free-game -version: 1.0.3 +version: 1.0.4 synopsis: Create games for free description: free-game defines a monad that integrates features to create 2D games. @@ -36,7 +36,6 @@ FreeGame.Instances FreeGame.Backend.GLFW FreeGame.Internal.Finalizer - FreeGame.Internal.Raindrop FreeGame.Internal.GLFW FreeGame.Types FreeGame.Util @@ -51,19 +50,21 @@ control-bool, directory >= 1.0, filepath >= 1.3, - free >= 4.5 && < 5, + free >= 4.6.1 && < 5, freetype2 >= 0.1, GLFW-b >= 1.3 && <2, hashable >= 1.2, JuicyPixels, JuicyPixels-util == 0.1.*, - linear >= 1.0 && < 1.7, + linear >= 1.0 && < 2, mtl >= 2.1, OpenGL == 2.9.*, OpenGLRaw == 1.4.*, random == 1.*, - reflection==1.*, + reflection == 1.*, template-haskell, transformers >= 0.3, vector >= 0.9 && <0.12, - void >= 0.5+ void >= 0.5, + boundingboxes >= 0.1.1 && < 0.2, + lens >= 3.8