module Graphics.Liveplot.Utils( dump , dbg , moveCam , cnf , cnfEndo , rpad , normalize ) where import Prelude import Control.Concurrent.STM import Data.Monoid (All(..), Any(..)) import Data.Foldable (Foldable, foldMap,foldl',fold) import Data.Set (Set) import qualified Data.Set as S import qualified Pipes.Prelude as P import Linear hiding (normalize) import MVC import Graphics.GLUtil.Camera2D import Graphics.GLUtil.Camera3D hiding (roll) import Graphics.UI.GLFW dump :: View a dump = asSink (\_ -> return()) dbg :: (Show a) => View a dbg = asSink (\e -> (putStrLn $ show e) >> return()) -- | Evaluate a boolean formula in conjunctive normal form (CNF) by -- applying the predicate to each atom according to the logic of its -- nesting in the formula. cnf :: (Foldable s, Foldable t) => s (t Bool) -> Bool cnf = getAll . foldMap (All . getAny . foldMap Any) -- | Perform a left fold over a set of guarded update functions, -- evaluating the guards left-to-right. For each guard that passes, -- its associated update function is composed into a final composite -- update function. cnfEndo :: (k -> s -> Bool) -> (k -> s -> s) -> [([[k]], a -> a)] -> s -> a -> a cnfEndo p del = go where go [] _ = id go ((k,f):fs) s | cnf (fmap (fmap (`p` s)) k) = go fs (delAll k s) . f | otherwise = go fs s delAll k s = foldl' (flip del) s (fold k) -- | Translate and rotate a 'Camera' based on 'UI' input. moveCam :: (Conjugate a, Epsilon a, RealFloat a) => Set Key -> Camera a -> Camera a moveCam keys = cnfEndo S.member S.delete [ ([shift, [Key'Left]], roll na) , ([shift, [Key'Right]], roll pa) , ([[Key'Left]], track (V2 np 0)) , ([[Key'Right]], track (V2 pp 0)) , ([[Key'Up]], track (V2 0 pp)) , ([[Key'Down]], track (V2 0 np)) --- XXX: tilting instead of zooming, how to zoom in 2d with cam? -- maybe just switch to 3d cam , ([[Key'PageUp]], tilt (pa)) , ([[Key'PageDown]], tilt (na)) ] keys where shift = [Key'LeftShift, Key'RightShift] -- XXX: pass timeScale as well? (Normalize speeds to 60Hz update) timeScale = 1 -- realToFrac $ timeStep ui * 60 pp = 0.08 * timeScale -- 1D speed np = negate pp pa = 2 * timeScale -- angular step na = negate pa rpad :: Int -> a -> [a] -> [a] rpad n x xs = xs ++ (take (n-(length xs)) $ repeat x) scaleRange :: Fractional a => (a, a) -> (a, a) -> a -> a scaleRange (fromLow, fromHigh) (toLow, toHigh) x = (x - fromLow) * (toHigh - toLow) / (fromHigh - fromLow) + toLow normRange :: Fractional a => (a, a) -> a -> a normRange from = scaleRange from (-1, 1) graphRange :: Fractional a => (a, a) -> a -> a graphRange from = scaleRange from (0, 1) -- normalize input from range to graph range (0..1) normalize :: (Fractional b, Monad m) => (b, b) -> Pipe b b m r normalize from = P.map (graphRange from)