reactive-glut (empty) → 0.0.1
raw patch · 13 files changed
+626/−0 lines, 13 filesdep +GLUTdep +OpenGLdep +basesetup-changed
Dependencies added: GLUT, OpenGL, base, old-time, reactive, vector-space
Files
- Makefile +1/−0
- README +21/−0
- Setup.lhs +3/−0
- reactive-glut.cabal +41/−0
- src/.ghci +2/−0
- src/CheckTimes.hs +17/−0
- src/FRP/Reactive/GLUT/Adapter.hs +109/−0
- src/FRP/Reactive/GLUT/SimpleGL.hs +122/−0
- src/FRP/Reactive/GLUT/SimpleGfx.hs +110/−0
- src/FRP/Reactive/GLUT/UI.hs +44/−0
- src/FTest.hs +88/−0
- src/Test.hs +53/−0
- wikipage.tw +15/−0
+ Makefile view
@@ -0,0 +1,1 @@+include ../conal-cabal-make.inc
+ README view
@@ -0,0 +1,21 @@+project-foo [1] is a template for a package, to make it easy to copy &+modify. It uses cabal-make [2] for convenience. It's even equipped with+a darcs repo. Copy the directory ("cp -rp"), "darcs mv" the .cabal file,+edit README and wikipage.tw. Then "darcs update" the changes and add new+content with "make check-add" and "make add-new". When ready, do a "darcs+put" or "make repo".++Please share any comments & suggestions on the discussion (talk) page at+[1].++You can configure, build, and install all in the usual way with Cabal+commands.++ runhaskell Setup.lhs configure+ runhaskell Setup.lhs build+ runhaskell Setup.lhs install++References:++[1] http://haskell.org/haskellwiki/project-foo+[1] http://haskell.org/haskellwiki/cabal-make
+ Setup.lhs view
@@ -0,0 +1,3 @@+#!/usr/bin/env runhaskell+> import Distribution.Simple+> main = defaultMain
+ reactive-glut.cabal view
@@ -0,0 +1,41 @@+Name: reactive-glut+Version: 0.0.1+Cabal-Version: >= 1.2+Synopsis: Connects Reactive and GLUT+Category: FRP, graphics+Description:+ This package contains an adapter that connects OpenGL/GLUT to the+ library FRP \"Reactive\".+ .+ Project wiki page: <http://haskell.org/haskellwiki/reactive-glut>+ .+ The module documentation pages have links to colorized source code and+ to wiki pages where you can read and contribute user comments. Enjoy!+ .+ © 2008 by Conal Elliott; BSD3 license.+Author: Conal Elliott +Maintainer: conal@conal.net+Homepage: http://haskell.org/haskellwiki/reactive-glut+Package-Url: http://code.haskell.org/reactive-glut+Copyright: (c) 2008 by Conal Elliott+License: BSD3+Stability: experimental+build-type: Simple++Library+ hs-Source-Dirs: src+ Extensions:+ Build-Depends: base, old-time, OpenGL, GLUT, vector-space, reactive >= 0.8.3+ Exposed-Modules: + FRP.Reactive.GLUT.Adapter+ Other-Modules:+ FRP.Reactive.GLUT.SimpleGL+ FRP.Reactive.GLUT.UI+ Test+-- Relies on fieldtrip+-- FTest++ ghc-options: -Wall+++-- ghc-prof-options: -prof -auto-all
+ src/.ghci view
@@ -0,0 +1,2 @@+-- While actively tweaking Reactive, make its sources visible to ghci+-- :set -i../../reactive/src
+ src/CheckTimes.hs view
@@ -0,0 +1,17 @@+-- Hack to check some timings. Requires some editor magic to format times.++import Data.List (findIndices)++ts :: [Float]++ts = [6.484375,6.5,6.515625,6.53125,6.5625,6.578125,6.59375,6.609375,6.625]++diffs xs = uncurry subtract `fmap` (xs `zip` tail xs)++ds = diffs ts++es = (round . (* 100000)) `fmap` ds++esi = findIndices (> 20) es+esid = diffs esi+
+ src/FRP/Reactive/GLUT/Adapter.hs view
@@ -0,0 +1,109 @@+{-# OPTIONS -Wall #-}+----------------------------------------------------------------------+-- |+-- Module : FRP.Reactive.GLUT.Adapter+-- Copyright : (c) Conal Elliott 2008+-- License : BSD3+-- +-- Maintainer : conal@conal.net+-- Stability : experimental+-- +-- Connect Reactive + GLUT+----------------------------------------------------------------------++module FRP.Reactive.GLUT.Adapter+ ( adapt+ , Action, Sink+ , UI(..)+ ) where++-- import Control.Concurrent (yield)++import Control.Applicative ((<$>))+import Control.Monad ((>=>))++import qualified Graphics.UI.GLUT as G++import FRP.Reactive (Behavior,stepper)++import FRP.Reactive.Internal.Misc (Sink,Action)+import FRP.Reactive.Internal.Clock (makeClock,cGetTime)+import FRP.Reactive.Internal.TVal (makeEvent)+import FRP.Reactive.Internal.Timing (mkUpdater)++import FRP.Reactive.GLUT.UI+import FRP.Reactive.GLUT.SimpleGL++-- | Adapter to connect @FRP.Reactive@ with @GLUT@.+adapt :: String -> Sink (UI -> Behavior Action)+adapt title f =+ do initGfx title+ clock <- makeClock+ let mkE = makeEvent clock+ (mousePosE, mousePosSink ) <- mkE+ (leftDown , leftDownSink ) <- mkE+ (rightDown, rightDownSink) <- mkE+ (keyPress , keyPressSink ) <- mkE+ (tick , tickSink ) <- mkE+ -- TODO: let the initial mouse position be its actual position+ let windowPoint' p = do -- putStrLn $ "window point " ++ show p+ windowPoint p+ mousePosSink' p = do -- putStrLn $ "mouse " ++ show p+ mousePosSink p+ -- yield+ mousePos = (0.0,0.0) `stepper` mousePosE+ mouseCB = Just (windowPoint' >=> mousePosSink')+ -- Callbacks+ G.passiveMotionCallback G.$= mouseCB+ G.motionCallback G.$= mouseCB+ G.displayCallback G.$= return ()+ G.keyboardMouseCallback G.$= Just (\k ks _ _ ->+ case (k,ks) of+ (G.MouseButton G.LeftButton ,G.Down) -> leftDownSink ()+ (G.MouseButton G.RightButton,G.Down) -> rightDownSink ()+ (G.Char c ,G.Down) -> keyPressSink (Char c)+ (G.SpecialKey s,G.Down) -> keyPressSink (SpecialKey s)+ _ -> return ()+ )+ updater <- mkUpdater+ (cGetTime clock)+ (glwrap <$> f (UI mousePos leftDown rightDown keyPress tick))+ schedule (updater >> tickSink ())+ -- putStrLn "mainLoop"+ G.mainLoop++-- TODO: figure out how to exit cleanly. Right now mainLoop never returns+-- killThread programTid+-- exitWith ExitSuccess+-- Better yet, don't kill the process. Instead leave the GL state such+-- that ghci can continue with more graphical yumminess.+++-- Schedule an action for regular execution. This version uses idle+-- callback. Could instead use a chain of timer call-backs, which could+-- then limit the update rate. (On my Windows machine, I'm getting exactly+-- 64fps most of the time for extremely simple graphics. I guess there's+-- something wired into G.idleCallback. -Conal)++-- Schedule using idle callback.+scheduleIdle :: Sink Action+scheduleIdle act = G.idleCallback G.$= Just act++-- Schedule using the timer callback. No matter how small an interval I+-- choose, I still get max 64 frames/sec.+scheduleTimer :: Sink Action+scheduleTimer act = G.addTimerCallback ms (act >> scheduleTimer act)+ where+ ms = 10++-- Schedule an action for repeated execution+schedule :: Sink Action+schedule | useIdle = scheduleIdle+ | otherwise = scheduleTimer++-- Whether to use the 'scheduleIdle' or 'scheduleTimer'.+useIdle :: Bool+useIdle = False++-- Both schedulers seem to work great.+
+ src/FRP/Reactive/GLUT/SimpleGL.hs view
@@ -0,0 +1,122 @@+{-# OPTIONS -Wall #-}+----------------------------------------------------------------------+-- |+-- Module : FRP.Reactive.GLUT.SimpleGL+-- Copyright : (c) Conal Elliott 2008+-- License : BSD3+-- +-- Maintainer : conal@conal.net+-- Stability : experimental+-- +-- Simplified GL/GLUT interface+-- +-- With much help from Andy Gill and David Sankel.+----------------------------------------------------------------------++module FRP.Reactive.GLUT.SimpleGL (initGfx, glwrap, windowPoint) where+++import Graphics.UI.GLUT+++-- Not sure about this, seems to work? (andy)+resizeScene :: Size -> IO ()+resizeScene (Size w 0) = resizeScene (Size w 1) -- prevent divide by zero+resizeScene s@(Size width height) = do+ -- putStrLn "resizeScene"+ viewport $= (Position 0 0, s)+ matrixMode $= Projection+ loadIdentity+ perspective 75 (w2/h2) 0.001 100+ matrixMode $= Modelview 0+ flush+ where+ w2 = half width+ h2 = half height+ half z = realToFrac z / 2++-- Initialize GL graphics+initGfx :: String -> IO ()+initGfx title = do+ do getArgsAndInitialize++ initialDisplayMode $= [ DoubleBuffered, RGBAMode, WithDepthBuffer, WithAlphaComponent ] + initialWindowSize $= Size 800 600+ createWindow title++ get windowSize >>= resizeScene++ -- ReverseBlend, Modulate, Blend, LessEqual, Decal, or Replace?+ textureFunction $= Modulate+ texture Texture2D $= Enabled+ textureFilter Texture2D $= ((Nearest, Nothing), Nearest)++ blend $= Enabled+ blendFunc $= (SrcAlpha, OneMinusSrcAlpha)++ lineSmooth $= Enabled+ hint LineSmooth $= Nicest+ shadeModel $= Smooth -- enables smooth color shading++ clearColor $= Color4 0 0 0 0+ clearDepth $= 1 -- enables clearing of the depth buffer+ depthFunc $= Just Less -- type of depth test++ polygonMode $= (Fill,Fill) -- or Fill,Line++ -- Enable the following if non-uniform scaling will occur+ normalize $= Enabled+ -- Enable the following if only uniform scaling will occur+ -- rescaleNormal $= Enabled++ -- lightModel undefined+ -- Set the light up at an infinite distance behind the camera+ lighting $= Enabled+ position (Light 0) $= Vertex4 1 1 0 1 + light (Light 0) $= Enabled ++ position (Light 0) $= Vertex4 0.0 0.0 1.0 0.0+ ambient (Light 0) $= Color4 0.0 0.0 0.0 1.0+ diffuse (Light 0) $= Color4 1.0 1.0 1.0 1.0+ specular (Light 0) $= Color4 1.0 1.0 1.0 1.0++ -- Material properties+ materialSpecular Front $= Color4 1.0 1.0 1.0 1.0+ materialEmission Front $= Color4 0.0 0.0 0.0 1.0+ materialShininess Front $= 70.0+ -- "If colorMaterial is defined, the normal calls to change the+ -- material colors don't work and glColor must be used in its place."+ -- (camio).+ -- + -- colorMaterial $= Just (FrontAndBack,AmbientAndDiffuse)+ -- colorMaterial $= Just (FrontAndBack,Specular)++ reshapeCallback $= Just resizeScene++ -- loadIdentity+++-- | Convert a window position to a logical X,Y. Logical zero is at the+-- origin, and [size??].+-- +-- TODO: this def is completely broken. What do I even want? Probably to+-- go backward through the viewing transform to the XY plane.+windowPoint :: Position -> IO (Double, Double)+windowPoint (Position x y) = do Size w h <- get windowSize+ return (f w x, f h y)+ where+ -- Scale and translate coordinate. Put zero at the center, and give one+ -- unit to 100 pixels.+ f _ z = realToFrac z+ -- f z maxZ = realToFrac (z - maxZ) / (100 * realToFrac maxZ)+++-- | Wrap an OpenGL rendering action, to clear the frame-buffer before+-- swap buffers afterward.+glwrap :: IO () -> IO ()+glwrap act = do clear [ColorBuffer, DepthBuffer]+ -- putStrLn "glwrap"+ -- loadIdentity+ -- translate (Vector3 0 0 (-3 :: Float))+ act+ swapBuffers
+ src/FRP/Reactive/GLUT/SimpleGfx.hs view
@@ -0,0 +1,110 @@+{-# OPTIONS_GHC -Wall #-}++module FRP.Reactive.GLUT.SimpleGfx+ ( initGfx, glwrap, windowPoint+ -- remove later+ , drawBox+ ) where++import Graphics.UI.GLUT+-- import Graphics.Rendering.OpenGL as GL++windowWidth, windowHeight :: GLint+windowWidth = 800 -- physical pixels+windowHeight = 600 -- physical pixels++canvasMinX, canvasMaxX, canvasMinY, canvasMaxY :: Double+canvasMaxX = 400 -- logical units+canvasMinX = -400 -- logical units+canvasMaxY = 300 -- logical units+canvasMinY = -300 -- logical units++canvasFarZ, canvasNearZ :: GLclampd+canvasFarZ = 4000+canvasNearZ = -4000++-- Initialize GL graphics+initGfx :: String -> IO ()+initGfx title = do+ -- Glut Part+ getArgsAndInitialize+ initialWindowSize $= (Size windowWidth windowHeight)+ initialDisplayMode $= [DoubleBuffered, RGBAMode]+ createWindow title+ -- OpenGL Part+ shadeModel $= Smooth+ lineSmooth $= Enabled++ lighting $= Enabled+ -- Needs to be disabled for fieldtrip+ -- colorMaterial $= Just (Front,AmbientAndDiffuse)+ light (Light 0) $= Enabled++ -- Enable the following if non-uniform scaling will occur+ normalize $= Enabled+ -- Enable the following if only uniform scaling will occur+ -- rescaleNormal $= Enabled++ -- Set the light up at an infinite distance behind the camera+ position (Light 0) $= (Vertex4 0.0 0.0 1.0 0.0)+ ambient (Light 0) $= (Color4 0.0 0.0 0.0 1.0)+ diffuse (Light 0) $= (Color4 1.0 1.0 1.0 1.0)+ specular (Light 0) $= (Color4 1.0 1.0 1.0 1.0)++ -- Some simple defaults here.+ materialSpecular (Front) $= (Color4 1.0 1.0 1.0 1.0)+ materialEmission (Front) $= (Color4 0.0 0.0 0.0 1.0)+ materialShininess (Front) $= 70.0++ blend $= Enabled+ blendFunc $= (SrcAlpha, OneMinusSrcAlpha)+ lineWidth $= 1.5+ clearColor $= Color4 0 0 0 0+ pointSmooth $= Enabled+ -- pointSize $= 5.0+ clearDepth $= canvasFarZ+ depthRange $= (canvasNearZ, canvasFarZ)+ depthFunc $= Just Lequal+ matrixMode $= Projection+ loadIdentity+ ortho canvasMinX canvasMaxX canvasMinY canvasMaxY canvasNearZ canvasFarZ+ matrixMode $= Modelview 0+ loadIdentity++-- | Convert a window position to a logical X,Y. Logical zero is at the+-- origin, and [size??].+-- +-- TODO: fix to work with variable window size.+windowPoint :: Position -> IO (Double, Double)+windowPoint (Position x y) = return $+ let pixToLog p minP maxP l u = + let p0to1 = (realToFrac $ p-minP)/(realToFrac $ maxP-minP)+ in (p0to1 * (u-l)) + l+ in (pixToLog x 0 (windowWidth-1) canvasMinX canvasMaxX,+ -(pixToLog y 0 (windowHeight-1) canvasMinY canvasMaxY))++-- | Wrap an OpenGL rendering action, to clear the frame-buffer before+-- swap buffers afterward.+glwrap :: IO () -> IO ()+glwrap act = do clearScreen (0.0,0.0,0.0)+ -- putStrLn "glwrap"+ act+ swapBuffers++clearScreen :: (GLdouble, GLdouble, GLdouble) -> IO()+clearScreen (r,g,b) = do+ clearColor $= Color4 (realToFrac r) (realToFrac g) (realToFrac b) 0+ clear [ColorBuffer, DepthBuffer]+++vertexPF :: VertexComponent a => (a,a) -> IO ()+vertexPF = vertex . (uncurry Vertex2)++drawBox :: (MatrixComponent c, Fractional c) => (c, c) -> IO ()+drawBox (centerX,centerY) = preservingMatrix $ do+ translate $ Vector3 centerX centerY 0.0+ renderPrimitive Polygon (mapM_ vertexPF [+ (-10.0::GLdouble ,-10.0),+ (-10.0,10.0),+ (10.0,10.0),+ (10.0,-10.0) ] )
+ src/FRP/Reactive/GLUT/UI.hs view
@@ -0,0 +1,44 @@+{-# LANGUAGE FlexibleContexts #-}+{-# OPTIONS -Wall #-}+----------------------------------------------------------------------+-- |+-- Module : FRP.Reactive.GLUT.UI+-- Copyright : (c) Conal Elliott 2008+-- License : BSD3+-- +-- Maintainer : conal@conal.net+-- Stability : experimental+-- +-- Simple UI type.+-- +-- Based on code from David Sankel.+----------------------------------------------------------------------++module FRP.Reactive.GLUT.UI+ ( UI(..)+ , Key(..), SpecialKey(..)+ , uiIntegral+ ) where++import Control.Applicative (liftA2)+import Graphics.UI.GLUT(SpecialKey(..))++import Data.VectorSpace+import FRP.Reactive++-- | Simple UI type.+data UI = UI {+ mousePosition :: Behavior (Double,Double),+ leftMousePressed :: Event (),+ rightMousePressed :: Event (),+ keyPressed :: Event Key,+ framePass :: Event ()+}++-- | Key pressed+data Key = Char Char | SpecialKey SpecialKey++-- | Integral tracking frame sampling+uiIntegral :: VectorSpace v TimeT =>+ (UI -> Behavior v) -> (UI -> Behavior v)+uiIntegral = liftA2 integral framePass
+ src/FTest.hs view
@@ -0,0 +1,88 @@+{-# OPTIONS -Wall #-}+----------------------------------------------------------------------+-- |+-- Module : FTest+-- Copyright : (c) Conal Elliott 2008+-- License : BSD3+-- +-- Maintainer : conal@conal.net+-- Stability : experimental+-- +-- Test Reactive + FieldTrip+----------------------------------------------------------------------++module FTest where++import Data.Monoid+import Control.Applicative++import Graphics.FieldTrip++import FRP.Reactive (Behavior,time)+import FRP.Reactive.GLUT.Adapter+++main :: IO ()+main = -- anim2 $ pure.pure $ txt+ -- anim2 $ pure $ rotTxt <$> time+ -- anim2 track+ anim3 spin++txt :: Geometry2+txt = utext "Reactive + FieldTrip"++rotTxt :: Double -> Geometry2+rotTxt t = rotate2 t *% txt++track :: Anim Geometry2+track = (fmap.fmap) (f . uncurry Vector2) mousePosition+ where+ f = (uscale2 (0.5::Float) *%) . utext . show++spin :: Anim Geometry3+spin = spinningG $+ -- usphere+ torusPair+ -- flatG txt++torusPair :: Geometry3+torusPair = f red (1/2) `mappend` pivot3X (f green (-1/2))+ where+ tor = torus 1 (2/5)+ f :: Col -> R -> Geometry3+ f col dx = plasmat col (move3X dx tor)++----++plasmat :: Col -> Filter3+plasmat col = materialG (plastic col)++spinningG :: Geometry3 -> Anim Geometry3+spinningG g env = liftA2 (*%) (spinning env) (pure g)++spinning :: Anim (Transform3 Double)+spinning = const (xf . (*2) <$> time)+ where+ xf t = translate3 (Vector3 (0::Double) 0 (3*sin (-t/5)))+ `mappend` rotate3 t (Vector3 0.1 0.2 0.3)+ `mappend` scale3 0.2 0.2 0.2++{--------------------------------------------------------------------+ Move to reactive-fieldtrip+--------------------------------------------------------------------}++view :: Filter3+view = move3Z (-3 :: R)++type Anim a = UI -> Behavior a++animate :: Sink a -> Sink (Anim a)+animate f anim = adapt "Reactive + FieldTrip" ((fmap.fmap) f anim)++anim2 :: Sink (Anim Geometry2)+anim2 = anim3 . (fmap.fmap) flatG++anim3 :: Sink (Anim Geometry3)+anim3 = animate (renderWith3 gc . view)+ where+ gc = defaultGC { gcErr = 0.005 }
+ src/Test.hs view
@@ -0,0 +1,53 @@+{-# OPTIONS -Wall #-}+----------------------------------------------------------------------+-- |+-- Module : Test+-- Copyright : (c) Conal Elliott 2008+-- License : BSD3+-- +-- Maintainer : conal@conal.net+-- Stability : experimental+-- +-- Testing out reactive+glut+----------------------------------------------------------------------++module Test where++import Control.Applicative (pure,(<$>))+import qualified Graphics.UI.GLUT as G++import FRP.Reactive (Behavior,time)+import FRP.Reactive.GLUT.Adapter++main :: IO ()+main = adapt "Reactive on GLUT" $ (fmap.fmap) view t2++type G = UI -> Behavior Action++t1, t2 :: G++t1 = const (pure (drawBox (0::Double,0)))++t2 = const (f <$> time)+ where+ f t = drawBox (t',t') where t' = t * 1+++---- Utilities++view :: IO () -> IO ()+view act = G.preservingMatrix $+ do G.translate (G.Vector3 0 0 (-30 :: Double))+ act+++vertexPF :: G.VertexComponent a => (a,a) -> IO ()+vertexPF = G.vertex . uncurry G.Vertex2++drawBox :: (G.MatrixComponent c, Fractional c) => (c, c) -> IO ()+drawBox (centerX,centerY) = G.preservingMatrix $+ do G.translate $ G.Vector3 centerX centerY 0+ G.renderPrimitive G.Polygon (mapM_ vertexPF ps)+ where+ ps :: [(Float,Float)]+ ps = [(-1,-1),(-1,1),(1,1),(1,-1)]
+ wikipage.tw view
@@ -0,0 +1,15 @@+[[Category:Packages]]++== Abstract ==++'''reactive-glut''' connects [[Reactive]] with [[OpenGL#Additional_software|GLUT]] and with [[FieldTrip]]. It also serves as a demonstration for how to hook up imperative (i.e., legacy) libraries [[Reactive]].++'''Note:''' The <code>conal.net</code> locations below are temporary, while waiting for a project on code.haskell.org.++Besides this wiki page, here are more ways to find out about reactive-glut:+* Read [http://conal.net/repos/reactive-glut/doc/html/ the library documentation].+* Get the code repository: '''<tt>darcs get http://conal.net/repos/reactive-glut</tt>'''.+* Install from [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/reactive-glut Hackage].+* See the [[reactive-glut/Versions| version history]].++Please leave comments at the [[Talk:reactive-glut|Talk page]].