reflex-gloss-scene (empty) → 0.1
raw patch · 13 files changed
+962/−0 lines, 13 filesdep +MonadRandomdep +basedep +containerssetup-changed
Dependencies added: MonadRandom, base, containers, dependent-sum, gloss, gloss-rendering, lens, linear, mtl, profunctors, random, reflex, reflex-animation, reflex-gloss, reflex-gloss-scene, reflex-transformers, semigroups, transformers, tuple, vector-space
Files
- .gitignore +15/−0
- .travis.yml +27/−0
- LICENSE +30/−0
- README.md +7/−0
- Setup.hs +2/−0
- examples/Animations.hs +68/−0
- examples/Collections.hs +96/−0
- examples/FrpZoo.hs +51/−0
- examples/Widgets.hs +65/−0
- reflex-gloss-scene.cabal +95/−0
- src/Reflex/Gloss/Random.hs +73/−0
- src/Reflex/Gloss/Scene.hs +348/−0
- src/Reflex/Gloss/Vector.hs +85/−0
+ .gitignore view
@@ -0,0 +1,15 @@+dist+docs+wiki+TAGS+tags+wip+.DS_Store+.*.swp+.*.swo+*.o+*.hi+*~+*#+.cabal-sandbox+cabal.sandbox.config
+ .travis.yml view
@@ -0,0 +1,27 @@+sudo: false+cache:+ directories:+ - $HOME/hlint+before_install:+ - travis_retry cabal-$CABALVER update+ - export PATH=/opt/ghc/$GHCVER/bin:/opt/happy/1.19.5/bin:/opt/alex/3.1.4/bin:~/hlint/bin:$PATH+ - travis/install-hlint.sh+install:+ - cabal-$CABALVER sandbox init+ - git clone -b develop https://github.com/ryantrinkle/reflex reflex+ - cabal-$CABALVER sandbox add-source $PWD/reflex+ - cabal-$CABALVER install -v --only-dependencies --enable-tests --enable-benchmarks -j++script:+ - travis/script.sh+ - hlint src++matrix:+ allow_failures:+ - env: CABALVER=head GHCVER=head + include:+ - env: CABALVER=1.22 GHCVER=7.10.2+ addons: {apt: {packages: [freeglut3-dev, cabal-install-1.22, ghc-7.10.2, alex-3.1.4, happy-1.19.5],sources: [hvr-ghc]}}+ - env: CABALVER=head GHCVER=head+ addons: {apt: {packages: [cabal-install-head, ghc-head, alex-3.1.4, happy-1.19.5], sources: [hvr-ghc]}}+ fast_finish: true
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2015, Jeffrey Rosenbluth++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Jeffrey Rosenbluth nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,7 @@+reflex-gloss-scene [](http://travis-ci.org/Saulzar/reflex-gloss-scene)+====================+++A scenegraph library using gloss and gloss-reflex, with support for picking, +continuous time animations and monad based switching. An example of a framework +created using the reflex-transformers package
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ examples/Animations.hs view
@@ -0,0 +1,68 @@+-- {-# LANGUAGE RecursiveDo #-}++module Main where++import Reflex+import Reflex.Gloss.Scene+-- import Reflex.Monad.Time+-- import Reflex.Animation+++-- import Reflex.Monad++import Graphics.Gloss++++-- import Control.Applicative hiding (optional)++import Widgets++ ++-- data Track time = Rotation Float | Position Vector deriving Ord+-- +-- +-- newtype Tracks time = Tracks { unTracks :: [Track time] }++-- +-- +-- +-- +-- bounce :: PictureAnimation+-- bounce = repeat (half (sine 0.5))+ + + +additive :: Reflex t => Scene t ()+additive = do+ + translation (-60, 0) $ do+ (_, bounceClick) <- translation (0, 80) $ toggleButton (pure "Bounce") (pure (80, 30)) never+ (_, wiggleClick) <- translation (0, 30) $ toggleButton (pure "Wiggle") (pure (80, 30)) never+ + return ()+ + translation (60, 0) $ + render $ square <$> pure green+ + + where+ + square c = color c $ rectangleSolid 40 40++ ++widget :: Reflex t => Scene t ()+widget = do+ additive+ + return ()++++main = playSceneGraph display background frequency widget+ where + display = InWindow "Animations example" (600, 600) (0, 0)+ background = white+ frequency = 30
+ examples/Collections.hs view
@@ -0,0 +1,96 @@+{-# LANGUAGE RecursiveDo #-}++module Main where++import Reflex+import Reflex.Gloss.Scene+import Reflex.Gloss.Random+import Reflex.Monad.Time+import Reflex.Animation++import Graphics.Gloss++import Data.Monoid+import Data.Maybe++import qualified Data.Map as Map++import Control.Applicative hiding (optional)++import Widgets++ + + +randomCircle :: RandomGen g => Rand g (Color, Point, Float) +randomCircle = do+ c <- getRandomR (makeColor 0 0 0 1, makeColor 1 1 1 1)+ p <- getRandomR ((-200, -200), (200, 200))+ r <- getRandomR (40, 100)+ return (c, p, r)++ +fadeFrom :: Color -> Clip Time Color+fadeFrom from = keyframes from [(0.5, withAlpha 0 from)]++flashRed :: Color -> Clip Time Color+flashRed c = keyframes c [(0.4, red), (0.4, c)]+++makeCircle :: (Reflex t) => (Color, Point, Float) -> Scene t ((), Event t ()) +makeCircle (c, p, radius) = translation p $ do + e <- chain (Chain active >-> Chain quitting) ()+ return ((), e)+ + where+ + quitting currentCol = do+ (col, done) <- playClamp (fadeFrom currentCol)+ renderCircle col+ return done++ active () = do+ target <- targetCircle (constant radius)+ (rightClick, _) <- clicked RightButton target+ + (enter, leave) <- mouseOver target+ rec + (flashing, _) <- playOn (gate (isNothing <$> flashing) $ flashRed c <$ enter)++ let col = fromMaybe c <$> flashing+ renderCircle col++ return (tag col rightClick)+ + renderCircle currentCol = render (renderBody <$> currentCol) + renderBody c = color c $ circleSolid radius + + + ++ ++widget :: Reflex t => Scene t ()+widget = do+ + click <- translation (200, 200) $ + rotation 40 $+ button (constant "New") (constant (80, 30))+ + degrees <- integrate 0 (constant 4)++ activeRotation degrees $ do+ newItems <- fmap makeCircle <$> foldRand (mkStdGen 0) randomCircle click + circles <- holdMapDyn =<< collection [] (pure <$> newItems) + render $ centreText . show . Map.size <$> current circles+++ return ()++++main = playSceneGraph display background frequency widget+ where + display = InWindow "Collections example" (600, 600) (0, 0)+ background = white+ frequency = 30
+ examples/FrpZoo.hs view
@@ -0,0 +1,51 @@+{-# LANGUAGE RecursiveDo #-}++module Main where++import Reflex+import Reflex.Gloss.Scene++import Graphics.Gloss+import Data.Monoid+import Data.Maybe+import Data.Bifunctor++import Control.Applicative hiding (optional)+import Widgets++foldMerge :: MonadReflex t m => a -> [Event t (a -> a)] -> m (Dynamic t a)+foldMerge initial events = foldDyn ($) initial (mergeWith (.) events) ++buttonPair :: (Reflex t) => Behavior t Int -> Scene t (Event t (), Event t ())+buttonPair b = mdo+ inc <- translation (0, 20) $ button (show <$> b) (pure (80, 30))+ toggle <- translation (0, -20) $ button (pure "Toggle") (pure (80, 30))+ return (inc, toggle) ++resetButtons :: (Reflex t) => Scene t ()+resetButtons = mdo+ (inc, toggle) <- buttonPair (fromMaybe (-1) <$> current d) + d <- foldMerge (Just 0) [fmap (+1) <$ inc, reset <$ toggle] + return ()+ where+ reset Nothing = Just 0+ reset (Just n) = Nothing + +toggleButtons :: (Reflex t) => Scene t ()+toggleButtons = do+ rec (inc, toggle) <- buttonPair ((\(n, b) -> if b then n else (-1)) <$> current d) + d <- foldMerge (0, True) [first (+1) <$ inc, second not <$ toggle]+ return ()+ +widget :: Reflex t => Scene t ()+widget = do+ translation (-100, 0) resetButtons + translation (0, 0) toggleButtons + translation (100, 0) toggleButtons +++main = playSceneGraph display background frequency widget+ where + display = InWindow "Reflex Frp-zoo" (300, 200) (0, 0)+ background = white+ frequency = 60
+ examples/Widgets.hs view
@@ -0,0 +1,65 @@+{-# LANGUAGE RecursiveDo #-}++module Widgets + ( centreText+ + , renderButton+ , button+ + , toggleButton+ ) where++import Reflex+import Reflex.Gloss.Scene+import Graphics.Gloss++++smallText :: String -> Picture+smallText str = scale 0.1 0.1 $ text str++centreText :: String -> Picture+centreText str = translate (-x) (-y) $ smallText str where+ x = fromIntegral (length str) * charWidth * 0.5+ y = charHeight * 0.5+ + (charWidth, charHeight) = (6, 10)+ +renderButton :: Color -> Bool -> Bool -> String -> Vector -> Picture+renderButton c isHovering isPressing str (sx, sy) = mconcat + [ color fill $ rectangleSolid sx sy+ , centreText str+ , if isHovering then rectangleWire sx sy else mempty+ ] where+ + fill = if isHovering && isPressing then dark c else c +++coloredButton :: Reflex t => Behavior t Color -> Behavior t String -> Behavior t Vector -> Scene t (Event t ())+coloredButton col str size = do+ + target <- targetRect size+ (click, pressing) <- clicked LeftButton target+ + render $ renderButton <$> col <*> hovering target+ <*> pressing <*> str <*> size+ return click+ ++button :: Reflex t => Behavior t String -> Behavior t Vector -> Scene t (Event t ())+button = coloredButton (pure $ light azure)+ + +toggleButton :: Reflex t => Behavior t String -> Behavior t Vector -> Event t Bool -> Scene t (Dynamic t Bool, Event t Bool)+toggleButton str size setter = do+ rec + click <- coloredButton (currentColor <$> current down) str size+ let clickDown = tag (not <$> current down) click+ + down <- holdDyn False $ leftmost [clickDown, setter]+ + return (down, clickDown)++ where+ currentColor d = if d then orange else light azure+
+ reflex-gloss-scene.cabal view
@@ -0,0 +1,95 @@+name: reflex-gloss-scene+version: 0.1+synopsis: A simple scene-graph using reflex and gloss+description: A scenegraph library using gloss and gloss-reflex, with support for picking, + continuous time animations and monad based switching. An example of a framework + created using the reflex-transformers package.++homepage: https://github.com/saulzar/reflex-gloss-scene+bug-reports: https://github.com/saulzar/reflex-gloss-scene/issues+license: BSD3+license-file: LICENSE++author: Oliver Batchelor+maintainer: saulzar@gmail.com+copyright: 2015 Oliver Batchelor+category: FRP+build-type: Simple+cabal-version: >=1.10+++extra-source-files: + examples/Widgets.hs+ .gitignore+ .travis.yml+ README.md++source-repository head+ type: git+ location: https://github.com/saulzar/reflex-gloss-scene.git++executable animations+ hs-source-dirs: examples+ default-language: Haskell2010+ main-is: Animations.hs+ build-depends: base, gloss, reflex, reflex-transformers, reflex-gloss-scene, reflex-animation, transformers, mtl, linear, lens, containers+ default-extensions: MultiParamTypeClasses ScopedTypeVariables RankNTypes FlexibleContexts TypeFamilies+++executable collections+ hs-source-dirs: examples+ default-language: Haskell2010+ main-is: Collections.hs+ build-depends: base, gloss, reflex, reflex-transformers, reflex-gloss-scene, reflex-animation, transformers, mtl, linear, lens, containers+ default-extensions: MultiParamTypeClasses ScopedTypeVariables RankNTypes FlexibleContexts TypeFamilies++executable frpzoo+ hs-source-dirs: examples+ default-language: Haskell2010+ main-is: FrpZoo.hs+ build-depends: base, gloss, reflex, reflex-transformers, reflex-animation, reflex-gloss-scene, transformers, mtl, linear, lens, containers+ default-extensions: MultiParamTypeClasses ScopedTypeVariables RankNTypes FlexibleContexts TypeFamilies+++library+ exposed-modules: Reflex.Gloss.Scene+ Reflex.Gloss.Random++ other-modules: Reflex.Gloss.Vector+ + build-depends: base >=4.6 && <4.9,+ gloss >= 1.9.2.1 && < 1.10,+ gloss-rendering >= 1.9.2.1,+ reflex-gloss >= 0.1.0.1 && < 0.2.0,+ reflex >=0.2 && <0.4,+ reflex-transformers >= 0.1 && < 1.0, + reflex-animation >= 0.1 && < 1.0, + dependent-sum >= 0.2.1 && < 0.3,+ transformers >= 0.4.2 && < 0.5, + + mtl,+ vector-space,+ lens,+ random,+ MonadRandom,+ tuple,+ semigroups,+ profunctors,+ containers++ default-extensions:+ FlexibleInstances+ TupleSections+ FlexibleContexts+ StandaloneDeriving+ FunctionalDependencies+ TypeFamilies+ GeneralizedNewtypeDeriving+ MultiParamTypeClasses+ RecursiveDo+ ScopedTypeVariables+ RankNTypes+ GADTs + + hs-source-dirs: src+ default-language: Haskell2010
+ src/Reflex/Gloss/Random.hs view
@@ -0,0 +1,73 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}++module Reflex.Gloss.Random + ( module Control.Monad.Random+ , module System.Random+ , foldGen+ , foldRandoms+ , foldRandomRs+ , foldRand+ )+ + where++import Control.Monad.Fix +import Control.Monad.State++import Control.Applicative+import Graphics.Gloss ++import System.Random+import Control.Monad.Random++import Data.Tuple.All+import Data.Bifunctor++import Reflex+++-- | Reflex.Gloss.Random+-- A module for using random number state with reflex. Not really related to gloss, but the examples use it!+ + ++-- These should really be in base!+instance (Random a, Random b) => Random (a, b) where + randomR ((a, b), (a', b')) = runState (liftA2 (,) (state $ randomR (a, a')) (state $ randomR (b, b'))) + random = runState (liftA2 (,) (state random) (state random)) + ++instance (Random a, Random b, Random c) => Random (a, b, c) where + randomR ((a, b, c), (a', b', c')) = runState (liftA3 (,,) (state $ randomR (a, a')) (state $ randomR (b, b')) (state $ randomR (c, c'))) + random = runState (liftA3 (,,) (state random) (state random) (state random)) ++ +instance (Random a, Random b, Random c, Random d) => Random (a, b, c, d) where + randomR ((a, b, c, d), (a', b', c', d')) = runState ((,,,) <$> state (randomR (a, a')) <*> state (randomR (b, b')) <*> state (randomR (c, c')) <*> state (randomR (d, d')))+ random = runState ((,,,) <$> state random <*> state random <*> state random <*> state random) ++-- and these should be in gloss!++instance Random Color where+ random = first (uncurryN makeColor) . randomR ((0, 0, 0, 0), (1, 1, 1, 1))+ randomR (a, a') = first (uncurryN makeColor) . randomR (rgbaOfColor a, rgbaOfColor a')+ +++foldGen :: (Reflex t, MonadHold t m, MonadFix m) => s -> (s -> (a, s)) -> Event t () -> m (Event t a)+foldGen initialState f input = do+ rec+ curState <- hold initialState newState+ let (outputs, newState) = splitE $ f <$> tag curState input + return outputs++ +foldRandoms :: (Reflex t, MonadHold t m, MonadFix m, Random a) => Int -> Event t () -> m (Event t a)+foldRandoms seed = foldGen (mkStdGen seed) random+++foldRandomRs :: (Reflex t, MonadHold t m, MonadFix m, Random a) => Int -> (a, a) -> Event t () -> m (Event t a)+foldRandomRs seed range = foldGen (mkStdGen seed) (randomR range)++foldRand :: (Reflex t, MonadHold t m, MonadFix m, RandomGen g) => g -> Rand g a -> Event t () -> m (Event t a)+foldRand g f = foldGen g (runRand f)
+ src/Reflex/Gloss/Scene.hs view
@@ -0,0 +1,348 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE NoMonomorphismRestriction #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE UndecidableInstances #-}+++-- | Reflex.Gloss.Scene+-- A simple scene graph implementation rendered using gloss. Contains functionality for picking and transformations.+-- Implements interfaces for MonadSwitch (see reflex-transformers) and MonadTime (see reflex-animations) for switching, and animation support respectively.+++module Reflex.Gloss.Scene+ ( playSceneGraph+ , rotation+ , activeRotation+ + , translation+ , activeTranslation+ + , render+ , Scene+ + , Time+ + , targetRect+ , targetCircle+ , makeTarget+ , hovering+ , Target+ + , mouseUp+ , mouseDown+ , clicked+ + , mouseOver+ + , MouseButton(..)+ , SpecialKey (..)+ , Input (..)+ + , module Reflex.Monad+ + )+ where++import Control.Monad.Fix +import Control.Monad.Reader++import Control.Lens hiding ((<.>), transform)+import Control.Applicative++import Data.Monoid+import Data.List+import Data.List.NonEmpty (nonEmpty)+++import Reflex+import Reflex.Gloss++import Reflex.Monad.Time++import Graphics.Gloss.Interface.IO.Game hiding (Event, KeyDown, KeyUp)+import qualified Graphics.Gloss.Interface.IO.Game as G+++import Reflex.Monad+import Reflex.Monad.ReaderWriter+import Reflex.Monad.ReflexM++import Reflex.Gloss.Vector++-- | Input data type more covenient for splitting than the Gloss Input type +data Input = MouseDown MouseButton + | MouseUp MouseButton+ | CharDown Char+ | CharUp Char+ | SpecialUp SpecialKey+ | SpecialDown SpecialKey +-- | MouseOver +-- | MouseOut+ deriving (Show, Eq, Ord)+ +-- | Time is just a float! +type Time = Float+ +data SceneNode t = SceneNode + { _sceneTime :: Behavior t Time + , _sceneTick :: Event t Time+ , _sceneMousePosition :: Behavior t Vector+ , _sceneTransform :: Behavior t Matrix33+ , _sceneEvents :: Event t Input+ , _sceneWindowSize :: Behavior t Vector+ }+ +$(makeLenses ''SceneNode)+$(makePrisms ''Input)+++$(makePrisms ''G.Event)+$(makePrisms ''G.Key)++-- | Scene Monad, passes down the scene graph a SceneNode, passes back up the scene graph renderings (Behavior t Picture)+-- Implements MonadSwitch and MonadTime abstractions.+newtype Scene t a = Scene { runScene :: ReaderWriterT (SceneNode t) [Behavior t Picture] (ReflexM t) a } + deriving (Monad, Functor, Applicative, MonadFix)+++deriving instance Reflex t => MonadSample t (Scene t)+deriving instance Reflex t => MonadHold t (Scene t)+deriving instance Reflex t => MonadWriter [Behavior t Picture] (Scene t)+deriving instance Reflex t => MonadReader (SceneNode t) (Scene t)++ + + +instance Reflex t => MonadSwitch t (Scene t) where+ switchM u = Scene $ switchM (runScene <$> u)+ switchMapM um = Scene $ switchMapM (runScene <$> um)+++ + +toVector :: Integral a => (a, a) -> Vector+toVector (a, b) = (fromIntegral a, fromIntegral b)+++toInput :: (Key, KeyState, a, b) -> Input+toInput (key, d, _, _) = case (key, d) of + (MouseButton b, Down) -> MouseDown b+ (MouseButton b, Up) -> MouseUp b+ + (Char c, Down) -> CharDown c+ (Char c, Up) -> CharUp c+ + (SpecialKey k, Down) -> SpecialDown k+ (SpecialKey k, Up) -> SpecialUp k++ + + +previewF :: FunctorMaybe f => Getting (First b) s b -> f s -> f b+previewF getter = fmapMaybe (preview getter) + + +runSceneGraph :: forall t m. (Reflex t, MonadHold t m, MonadFix m) => Vector -> Scene t () -> Event t Float -> Event t InputEvent -> m (Behavior t Picture)+runSceneGraph initialSize scene tick inputs = do+ time <- foldDyn (+) 0 tick + mousePosition <- hold (1/0, 1/0) $ previewF _EventMotion inputs+ windowSize <- hold initialSize (toVector <$> previewF _EventResize inputs)+ + let rootNode = SceneNode + { _sceneTime = current time+ , _sceneTick = tick+ , _sceneMousePosition = mousePosition+ , _sceneEvents = toInput <$> previewF _EventKey inputs + , _sceneTransform = constant identityMat + , _sceneWindowSize = windowSize+ }+ + runSceneGraph' rootNode scene++ + +runSceneGraph' :: (Reflex t, MonadHold t m, MonadFix m) => SceneNode t -> Scene t a -> m (Behavior t Picture)+runSceneGraph' node = fmap mconcat . runReflexM . flip execReaderWriterT node . runScene+++-- | Run a scenegraph in IO+playSceneGraph :: Display -> Color -> Int -> (forall t. Reflex t => Scene t ()) -> IO ()+playSceneGraph display col frequency scene = playReflex display col frequency (runSceneGraph (displaySize display) scene) where+ displaySize (InWindow _ s _) = toVector s+ displaySize (FullScreen s) = toVector s+ + +-- | Render a picture in the current widget+render :: Reflex t => Behavior t Picture -> Scene t ()+render r = tell [r]++ + +-- | Local mouse position relative to the widget+localMouse :: Reflex t => Scene t (Behavior t Point)+localMouse = asks sceneLocalMouse+++-- | Get the global mouse position in the window+globalMouse :: Reflex t => Scene t (Behavior t Point)+globalMouse = asks _sceneMousePosition++-- | Rotate a child widget using a constant+rotation :: Reflex t => Float -> Scene t a -> Scene t a +rotation degrees child = localRender (fmap $ G.Rotate degrees) $ + localTransform (rotationMat degrees) child++-- | Rotate a child widget actively using a Behavior+activeRotation :: Reflex t => Behavior t Float -> Scene t a -> Scene t a +activeRotation degrees child = localRender (G.Rotate <$> degrees <*>) $ + localTransformDyn (rotationMat <$> degrees) child+ +-- | Translate a child widget using a constant+translation :: Reflex t => Vector -> Scene t a -> Scene t a +translation (x, y) child = localRender (fmap $ G.Translate x y) $ + localTransform (translationMat (-x, -y)) child++-- | Translate a child widget actively using a Behavior+activeTranslation :: Reflex t => Behavior t Vector -> Scene t a -> Scene t a +activeTranslation v child = localRender (uncurry G.Translate <$> v <*>) $ + localTransformDyn (translationMat . negate <$> v) child ++ +-- | Type for picking targets+data Target t = Target + { targetNode :: SceneNode t + , hovering :: Behavior t Bool+ } + + + +-- | Make a target from an intersection function +makeTarget :: Reflex t => Behavior t (Vector -> Bool) -> Scene t (Target t) +makeTarget intersects = do+ node <- ask+ mouse <- localMouse+ return Target + { targetNode = node+ , hovering = intersects <*> mouse+ } + ++intersectRect :: Vector -> Vector -> Bool+intersectRect (sizeX, sizeY) (x, y) = x >= -sx && x <= sx && y >= -sy && y <= sy where+ sx = sizeX * 0.5+ sy = sizeY * 0.5++intersectCircle :: Float -> Vector -> Bool+intersectCircle radius (x, y) = x * x + y * y < radius * radius + + +-- | Make a circular picking target +targetCircle :: Reflex t => Behavior t Float -> Scene t (Target t) +targetCircle size = makeTarget (intersectCircle <$> size)+++-- | Make a rectangular picking target +targetRect :: Reflex t => Behavior t Vector -> Scene t (Target t) +targetRect size = makeTarget (intersectRect <$> size)+++-- Match a particular kind of mouse or key event+nodeEvent :: Reflex t => Input -> SceneNode t -> Event t ()+nodeEvent input node = match input $ node ^. sceneEvents +++-- | Mouse down event on a picking target+mouseDown :: Reflex t => MouseButton -> Target t -> Event t ()+mouseDown button target = gate (hovering target) $ nodeEvent (MouseDown button) (targetNode target)+ +-- | Mouse up event on a picking target+mouseUp :: Reflex t => MouseButton -> Target t -> Event t ()+mouseUp button target = gate (hovering target) $ nodeEvent (MouseUp button) (targetNode target)+++-- | Mouse over event on a picking target, returns events for (entering, leaving)+mouseOver :: (Reflex t, MonadTime t time m) => Target t -> m (Event t (), Event t ())+mouseOver target = do+ ov <- observeChanges (hovering target)+ return (match True ov, match False ov)++-- | Mouse click event for picking target, returns click event and behavior indicating current clicking+clicked :: (Reflex t, MonadHold t m) => MouseButton -> Target t -> m (Event t (), Behavior t Bool)+clicked button target = do+ pressing <- hold False $ leftmost + [ False <$ nodeEvent (MouseUp button) (targetNode target)+ , True <$ mouseDown button target + ]+ + return (void $ gate pressing (mouseUp button target), pressing)++getTick :: Reflex t => Scene t (Event t Float)+getTick = asks _sceneTick++++instance Reflex t => MonadTime t Time (Scene t) where++ --observe :: MonadTime t m => Behavior t a -> m (Event t a)+ observe b = tag b <$> getTick ++ --integrate :: (VectorSpace v, Scalar v ~ Time, MonadReflex t m) => v -> Behavior t v -> Scene t (Behavior t v)+ integrate x dx = do+ frame <- attachWith (^*) dx <$> getTick+ current <$> foldDyn (^+^) x frame+ ++ --after :: MonadReflex t m => Time -> Scene t (Event t ())+ after t = do+ time <- foldDyn (+) 0 =<< getTick+ onceE $ matchBy (>= t) (updated time)+ + + getTime = asks _sceneTime+ + + -- | Delay an Event by a time period+ --delay :: MonadReflex t m => Event t (a, Time) -> Scene t (Event t [a])+ delay e = do+ tick <- asks _sceneTick+ time <- getTime++ rec+ b <- hold [] waiting+ + let next = leftmost [new, tag b tick]+ (waiting, firing) = split $ + attachWith (\t -> partition ((> t) . snd)) time next++ new = pushFor e $ \(a, p) -> do+ now <- sample time+ existing <- sample b+ return ((a, now + p) : existing)+ + return (fmapMaybe (nonEmpty . fmap fst) firing) +++ +-- Internal utility functions for Scene Node transformations+localRender :: Reflex t => (Behavior t Picture -> Behavior t Picture) -> Scene t a -> Scene t a +localRender f = censor (pure . f . mconcat) +++transformNode :: (Reflex t) => Matrix33 -> SceneNode t -> SceneNode t+transformNode transform node = node & sceneTransform %~ fmap (transform `mm3`)++ +transformNodeDyn :: (Reflex t) => Behavior t Matrix33 -> SceneNode t -> SceneNode t+transformNodeDyn transform node = node & sceneTransform %~ liftA2 (flip mm3) transform+ +localTransform :: Reflex t => Matrix33 -> Scene t a -> Scene t a +localTransform transform = local (transformNode transform)+ + +localTransformDyn :: Reflex t => Behavior t Matrix33 -> Scene t a -> Scene t a +localTransformDyn transform = local (transformNodeDyn transform)+ +sceneLocalMouse :: Reflex t => SceneNode t -> Behavior t Point+sceneLocalMouse node = liftA2 transformPoint (_sceneTransform node) (_sceneMousePosition node)
+ src/Reflex/Gloss/Vector.hs view
@@ -0,0 +1,85 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}++module Reflex.Gloss.Vector + ( mv3+ , mm3+ , rotationMat+ , identityMat+ , translationMat+ , transformPoint+ + , Matrix33, Vector3+ , module Data.VectorSpace+ + ) where+ + +import Graphics.Gloss.Rendering++import Data.VectorSpace+++type Vector3 = (Float, Float, Float)+type Matrix33 = (Vector3, Vector3, Vector3)+++mv3 :: Matrix33 -> Vector3 -> Vector3+mv3 (a, b, c) v = (a <.> v, b <.> v, c <.> v)+++vm3 :: Vector3 -> Matrix33 -> Vector3+vm3 (x, y, z) (a, b, c) = x *^ a ^+^ y *^ b ^+^ z *^ c++++mm3 :: Matrix33 -> Matrix33 -> Matrix33+mm3 (a, b, c) m = (a `vm3` m, b `vm3` m, c `vm3` m) +++rotationMat :: Float -> Matrix33+rotationMat deg = + ( (ca, -sa, 0)+ , (sa, ca, 0)+ , (0, 0, 1))+ + where+ a = deg * pi / 180.0 + ca = cos a+ sa = sin a+ + +identityMat :: Matrix33+identityMat = + ( (1, 0, 0)+ , (0, 1, 0)+ , (0, 0, 1)) ++ +translationMat :: Vector -> Matrix33+translationMat (dx, dy) = + ( (1, 0, dx)+ , (0, 1, dy)+ , (0, 0, 1)) + + ++transformPoint :: Matrix33 -> Point -> Point+transformPoint transform (x, y) = (x', y') ^* (1 / w) where+ (x', y', w) = transform `mv3` (x, y, 1) + + +instance AdditiveGroup Color where+ zeroV = makeColor 0 0 0 0+ c ^+^ c' = makeRawColor (r + r') (g + g') (b + b') (a + a')+ where (r, g, b, a) = rgbaOfColor c+ (r', g', b', a') = rgbaOfColor c'+ negateV c = makeRawColor (-r) (-g) (-b) (-a)+ where (r, g, b, a) = rgbaOfColor c+++instance VectorSpace Color where+ type Scalar Color = Float+ + s *^ c = makeRawColor (r * s) (g * s) (b * s) (a * s) + where (r, g, b, a) = rgbaOfColor c+