Updated comments in YGL files

This commit is contained in:
Yann Esposito 2012-06-15 11:25:40 +02:00
parent c0900ea16c
commit 055afacbff
2 changed files with 170 additions and 96 deletions

View file

@ -8,31 +8,37 @@ Typically separate the display function.
-} -}
module YGL ( module YGL (
-- Datas -- Here is declared our interface with external files
Point -- that will include our YGL module
, Time
, Scalar -- Declarations related to data types
, Color Point -- the 1 dimension point type
, Point3D , Time -- the type for the time
, Scalar -- the type for scalar values
, Color -- the type for color (3 scalars)
, Point3D (..) -- A 3D point type (3 Points)
, makePoint3D -- helper (x,y,z) -> Point3D , makePoint3D -- helper (x,y,z) -> Point3D
, (-*<) -- scalar product on Point3D , (-*<) -- scalar product on Point3D a -*< (x,y,z) = (ax,ay,az)
, Function3D , Function3D -- Point -> Point -> Maybe (Point,Color)
-- Your world state must be an instance -- Your world state must be an instance
-- of the DisplayableWorld type class -- of the DisplayableWorld type class
, DisplayableWorld (..) , DisplayableWorld (..)
-- Datas related to DisplayableWorld -- Datas related to DisplayableWorld
, Camera (..) , Camera (..)
, YObject (..) , YObject (..) -- 3D Objects to display
, Box3D (..) , Box3D (..) -- Some bounded 3D box
, makeBox , makeBox -- helper to make a box
, hexColor , hexColor -- Color from hexadecimal string
, makeColor , makeColor -- make color from RGB values
-- Datas related to user Input -- Interface related to user input
, InputMap , InputMap
, UserInput (Press,Ctrl,Alt,CtrlAlt) , UserInput (Press,Ctrl,Alt,CtrlAlt)
, inputMapFromList , inputMapFromList
-- The main loop function to call -- The main loop function to call
, yMainLoop) where , yMainLoop
) where
-- A bunch of imports -- A bunch of imports
import Numeric (readHex) -- to read hexadecimal values import Numeric (readHex) -- to read hexadecimal values
@ -55,17 +61,17 @@ import Data.Maybe (isNothing)
- Just take the time to follow me. - Just take the time to follow me.
--} --}
-- | A 1D point -- | A 1D point
type Point = GLfloat type Point = GLfloat
-- | A Scalar value -- | A Scalar value
type Scalar = GLfloat type Scalar = GLfloat
-- | The time type (currently its Int -- | The time type (currently its Int)
type Time = Int type Time = Int
-- | A 3D Point mainly '(x,y,z)' -- | A 3D Point mainly '(x,y,z)'
data Point3D = P (Point,Point,Point) deriving (Eq,Show,Read) data Point3D = P (Point,Point,Point) deriving (Eq,Show,Read)
type Color = Color3 Scalar type Color = Color3 Scalar
-- Get x (resp. y, z) coordinate of a 3D point
xpoint :: Point3D -> Point xpoint :: Point3D -> Point
xpoint (P (x,_,_)) = x xpoint (P (x,_,_)) = x
ypoint :: Point3D -> Point ypoint :: Point3D -> Point
@ -73,10 +79,11 @@ ypoint (P (_,y,_)) = y
zpoint :: Point3D -> Point zpoint :: Point3D -> Point
zpoint (P (_,_,z)) = z zpoint (P (_,_,z)) = z
-- Create a Point3D element from a triplet
makePoint3D :: (Point,Point,Point) -> Point3D makePoint3D :: (Point,Point,Point) -> Point3D
makePoint3D p = P p makePoint3D = P
-- Make Point3D an instance of Num
instance Num Point3D where instance Num Point3D where
(+) (P (ax,ay,az)) (P (bx,by,bz)) = P (ax+bx,ay+by,az+bz) (+) (P (ax,ay,az)) (P (bx,by,bz)) = P (ax+bx,ay+by,az+bz)
(-) (P (ax,ay,az)) (P (bx,by,bz)) = P (ax-bx,ay-by,az-bz) (-) (P (ax,ay,az)) (P (bx,by,bz)) = P (ax-bx,ay-by,az-bz)
@ -87,11 +94,12 @@ instance Num Point3D where
signum (P (x,y,z)) = P (signum x, signum y, signum z) signum (P (x,y,z)) = P (signum x, signum y, signum z)
fromInteger i = P (fromInteger i, 0, 0) fromInteger i = P (fromInteger i, 0, 0)
-- The scalar product
infixr 5 -*< infixr 5 -*<
(-*<) :: Scalar -> Point3D -> Point3D (-*<) :: Scalar -> Point3D -> Point3D
(-*<) s p = P (s*xpoint p, s*ypoint p, s*zpoint p) (-*<) s p = P (s*xpoint p, s*ypoint p, s*zpoint p)
-- Used internally to convert point3D to different types
toGLVector3 :: Point3D -> Vector3 GLfloat toGLVector3 :: Point3D -> Vector3 GLfloat
toGLVector3 (P(x,y,z)) = Vector3 x y z toGLVector3 (P(x,y,z)) = Vector3 x y z
@ -110,31 +118,46 @@ data Box3D = Box3D {
, maxPoint :: Point3D , maxPoint :: Point3D
, resolution :: Scalar } , resolution :: Scalar }
-- | An helper to make a Box3D
makeBox :: (Point,Point,Point) -> (Point,Point,Point) -> Scalar -> Box3D makeBox :: (Point,Point,Point) -> (Point,Point,Point) -> Scalar -> Box3D
makeBox mini maxi res = Box3D { makeBox mini maxi res = Box3D {
minPoint = makePoint3D mini minPoint = makePoint3D mini
, maxPoint = makePoint3D maxi , maxPoint = makePoint3D maxi
, resolution = res } , resolution = res }
-- | A Triangle3D is simply 3 points and a color
type Triangle3D = (Point3D,Point3D,Point3D,Color) type Triangle3D = (Point3D,Point3D,Point3D,Color)
-- For a general purpose library we should add many other different atoms
-- corresponding to Quads for example. -- | The type Atom is the atom for our display here we'll only use triangles.
-- | For a general purpose library we should add many other different atoms
-- | corresponding to Quads for example.
data Atom = ColoredTriangle Triangle3D data Atom = ColoredTriangle Triangle3D
-- | A Function3D is simply a function for each x,y associate a z and a color
-- | If undefined at point (x,y), it returns Nothing.
type Function3D = Point -> Point -> Maybe (Point,Color) type Function3D = Point -> Point -> Maybe (Point,Color)
-- | Our objects that will be displayed
-- | Wether a function3D delimited by a Box
-- | or a list of Atoms
data YObject = XYFunc Function3D Box3D data YObject = XYFunc Function3D Box3D
| Atoms [Atom] | Atoms [Atom]
-- | The function atoms retrieve the list of atoms from an YObject
atoms :: YObject -> [Atom] atoms :: YObject -> [Atom]
atoms (XYFunc f b) = getObject3DFromShapeFunction f b atoms (XYFunc f b) = getObject3DFromShapeFunction f b
atoms (Atoms atomList) = atomList atoms (Atoms atomList) = atomList
-- | We decalre the input map type we need here -- | We decalre the input map type we need here
-- | It is our API -- | It is our API
-- | I don't use Mouse but it can be easily added
type InputMap worldType = Map.Map UserInput (worldType -> worldType) type InputMap worldType = Map.Map UserInput (worldType -> worldType)
data UserInput = Press Char | Ctrl Char | Alt Char | CtrlAlt Char data UserInput = Press Char | Ctrl Char | Alt Char | CtrlAlt Char
deriving (Eq,Ord,Show,Read) deriving (Eq,Ord,Show,Read)
-- | A displayable world -- | A displayable world is a type for which
-- | ther exists a function that provide sufficient informations
-- | to provide a camera, lights, objects and a window title.
class DisplayableWorld world where class DisplayableWorld world where
camera :: world -> Camera camera :: world -> Camera
camera _ = defaultCamera camera _ = defaultCamera
@ -152,6 +175,7 @@ data Camera = Camera {
, camDir :: Point3D , camDir :: Point3D
, camZoom :: Scalar } , camZoom :: Scalar }
-- | A default initial camera
defaultCamera :: Camera defaultCamera :: Camera
defaultCamera = Camera { defaultCamera = Camera {
camPos = makePoint3D (0,0,0) camPos = makePoint3D (0,0,0)
@ -159,8 +183,8 @@ defaultCamera = Camera {
, camZoom = 1 } , camZoom = 1 }
-- Given a shape function and a delimited Box3D -- | Given a shape function and a delimited Box3D
-- return a list of Triangles to be displayed -- | return a list of Atoms (here only colored triangles) to be displayed
getObject3DFromShapeFunction :: Function3D -> Box3D -> [Atom] getObject3DFromShapeFunction :: Function3D -> Box3D -> [Atom]
getObject3DFromShapeFunction shape box = do getObject3DFromShapeFunction shape box = do
x <- [xmin,xmin+res..xmax] x <- [xmin,xmin+res..xmax]
@ -199,6 +223,7 @@ getObject3DFromShapeFunction shape box = do
ymax = ypoint $ maxPoint box ymax = ypoint $ maxPoint box
res = resolution box res = resolution box
-- | Get the user input map from a list
inputMapFromList :: (DisplayableWorld world) => inputMapFromList :: (DisplayableWorld world) =>
[(UserInput,world -> world)] -> InputMap world [(UserInput,world -> world)] -> InputMap world
inputMapFromList = Map.fromList inputMapFromList = Map.fromList
@ -208,13 +233,17 @@ inputMapFromList = Map.fromList
- As you can see the code is _not_ pure - As you can see the code is _not_ pure
- and not even functionnal friendly! - and not even functionnal friendly!
- But when called, - But when called,
- it will look like a standard function. - it will look like a pure functional function.
--} --}
yMainLoop :: (DisplayableWorld worldType) => yMainLoop :: (DisplayableWorld worldType) =>
InputMap worldType -- the mapping user input / world -- the mapping user input / world
InputMap worldType
-- function that modify the world
-> (Time -> worldType -> worldType) -> (Time -> worldType -> worldType)
-> worldType -- the world state -- the world state of type worldType
-> IO () -- into IO () for obvious reason -> worldType
-- into IO () for obvious reason
-> IO ()
yMainLoop inputActionMap yMainLoop inputActionMap
worldTranformer worldTranformer
world = do world = do
@ -234,14 +263,18 @@ yMainLoop inputActionMap
Just (keyboardMouse inputActionMap worldRef) Just (keyboardMouse inputActionMap worldRef)
-- We generate one frame using the callback -- We generate one frame using the callback
displayCallback $= display worldRef displayCallback $= display worldRef
-- let OpenGL resize normal vectors to unity
normalize $= Enabled normalize $= Enabled
-- Lights shadeModel $= Smooth
-- Lights (in a better version should be put elsewhere)
lighting $= Enabled lighting $= Enabled
ambient (Light 0) $= Color4 0 0 0 1 ambient (Light 0) $= Color4 0 0 0 1
diffuse (Light 0) $= Color4 0.5 0.5 0.5 1 diffuse (Light 0) $= Color4 0.5 0.5 0.5 1
specular (Light 0) $= Color4 1 1 1 1 specular (Light 0) $= Color4 1 1 1 1
position (Light 0) $= Vertex4 1 1 0 1 position (Light 0) $= Vertex4 1 1 0 1
light (Light 0) $= Enabled light (Light 0) $= Enabled
pointSmooth $= Enabled
colorMaterial $= Just (Front,AmbientAndDiffuse) colorMaterial $= Just (Front,AmbientAndDiffuse)
materialDiffuse Front $= Color4 0.5 0.5 0.5 1 materialDiffuse Front $= Color4 0.5 0.5 0.5 1
materialAmbient Front $= Color4 0.5 0.5 0.5 1 materialAmbient Front $= Color4 0.5 0.5 0.5 1
@ -259,15 +292,15 @@ idle worldTranformer world = do
world $= worldTranformer t w world $= worldTranformer t w
postRedisplay Nothing postRedisplay Nothing
-- Get User Input -- | Get User Input
-- both cleaner, terser and more expendable than the preceeding code -- | both cleaner, terser and more expendable than the preceeding code
keyboardMouse :: InputMap a -> IORef a keyboardMouse :: InputMap a -> IORef a
-> Key -> KeyState -> Modifiers -> Position -> IO() -> Key -> KeyState -> Modifiers -> Position -> IO()
keyboardMouse input world key state _ _ = keyboardMouse input world key state _ _ =
when (state == Down) $ when (state == Down) $
let let
charFromKey (Char c) = c charFromKey (Char c) = c
-- To replace -- To complete if you want to finish it
charFromKey _ = '#' charFromKey _ = '#'
transformator = Map.lookup (Press (charFromKey key)) input transformator = Map.lookup (Press (charFromKey key)) input
@ -280,7 +313,7 @@ keyboardMouse input world key state _ _ =
world $= transform w world $= transform w
-- The function that will display datas -- | The function that will display datas
display :: (HasGetter g, DisplayableWorld world) => display :: (HasGetter g, DisplayableWorld world) =>
g world -> IO () g world -> IO ()
display worldRef = do display worldRef = do
@ -295,6 +328,7 @@ display worldRef = do
-- and refere to competent authorities -- and refere to competent authorities
let cam = camera w let cam = camera w
-- set the background color (dark solarized theme) -- set the background color (dark solarized theme)
-- Could also be externalized to world state
clearColor $= Color4 0 0.1686 0.2117 1 clearColor $= Color4 0 0.1686 0.2117 1
clear [ColorBuffer,DepthBuffer] clear [ColorBuffer,DepthBuffer]
-- Transformation to change the view -- Transformation to change the view
@ -316,24 +350,21 @@ display worldRef = do
scalarFromHex :: String -> Scalar scalarFromHex :: String -> Scalar
scalarFromHex = (/256) . fst . head . readHex scalarFromHex = (/256) . fst . head . readHex
hexColor :: [Char] -> Color -- | Color from CSS style color string
hexColor ('#':rd:ru:gd:gu:bd:bu:[]) = Color3 (scalarFromHex (rd:ru:[])) hexColor :: String -> Color
(scalarFromHex (gd:gu:[])) hexColor ('#':rd:ru:gd:gu:bd:bu:[]) = Color3 (scalarFromHex [rd,ru])
(scalarFromHex (bd:bu:[])) (scalarFromHex [gd,gu])
hexColor ('#':r:g:b:[]) = hexColor ('#':r:r:g:g:b:b:[]) (scalarFromHex [bd,bu])
hexColor ('#':r:g:b:[]) = hexColor ['#',r,r,g,g,b,b]
hexColor _ = error "Bad color!!!!" hexColor _ = error "Bad color!!!!"
-- | Helper to make a color from RGB scalar values
makeColor :: Scalar -> Scalar -> Scalar -> Color makeColor :: Scalar -> Scalar -> Scalar -> Color
makeColor x y z = Color3 x y z makeColor = Color3
---
-- drawObject :: (YObject obj) => obj -> IO() -- | Where the drawing occurs
drawObject :: YObject -> IO() drawObject :: YObject -> IO()
drawObject shape = do drawObject shape = renderPrimitive Triangles $
-- We will print only Triangles
renderPrimitive Triangles $ do
-- solarized base3 color
-- color $ hexColor "#fdf603"
mapM_ drawAtom (atoms shape) mapM_ drawAtom (atoms shape)
-- simply draw an Atom -- simply draw an Atom
@ -345,6 +376,8 @@ drawAtom atom@(ColoredTriangle (p0,p1,p2,c)) = do
vertex $ toGLVertex3 p1 vertex $ toGLVertex3 p1
vertex $ toGLVertex3 p2 vertex $ toGLVertex3 p2
-- get the normal vector of an Atom -- | get the normal vector of an Atom
-- I don't normalize it; it is done by OpenGL
-- in main with 'normalize $= Enabled'
getNormal :: Atom -> Point3D getNormal :: Atom -> Point3D
getNormal (ColoredTriangle (p0,p1,p2,_)) = (p1 - p0) * (p2 - p0) getNormal (ColoredTriangle (p0,p1,p2,_)) = (p1 - p0) * (p2 - p0)

View file

@ -1,6 +1,3 @@
-- The languages include needed because I wanted to use
-- (Point,Point,Point) instead of
-- data Point3D = Point3D (Point,Point,Point) deriving ...
{- {-
The module YGL will contains most boilerplate The module YGL will contains most boilerplate
And display details. And display details.
@ -11,40 +8,56 @@ Typically separate the display function.
-} -}
module YGL ( module YGL (
-- Datas -- Here is declared our interface with external files
Point -- that will include our YGL module
, Time
, Scalar -- Declarations related to data types
, Color Point -- the 1 dimension point type
, Point3D (..) , Time -- the type for the time
, Scalar -- the type for scalar values
, Color -- the type for color (3 scalars)
, Point3D (..) -- A 3D point type (3 Points)
, makePoint3D -- helper (x,y,z) -> Point3D , makePoint3D -- helper (x,y,z) -> Point3D
, (-*<) -- scalar product on Point3D , (-*<) -- scalar product on Point3D a -*< (x,y,z) = (ax,ay,az)
, Function3D , Function3D -- Point -> Point -> Maybe (Point,Color)
, xpoint, ypoint, zpoint , xpoint, ypoint, zpoint
, Atom (..) , Atom (..) -- The Atom object (colored triangles for now)
-- Your world state must be an instance -- Your world state must be an instance
-- of the DisplayableWorld type class -- of the DisplayableWorld type class
, DisplayableWorld (..) , DisplayableWorld (..)
-- Datas related to DisplayableWorld -- Datas related to DisplayableWorld
, Camera (..) , Camera (..)
, YObject (..) , YObject (..) -- 3D Objects to display
, Box3D (..) , Box3D (..) -- Some bounded 3D box
, makeBox
, getObject3DFromShapeFunction , getObject3DFromShapeFunction
, hexColor , makeBox -- helper to make a box
, makeColor , hexColor -- Color from hexadecimal string
-- Datas related to user Input , makeColor -- make color from RGB values
-- Interface related to user input
, InputMap , InputMap
, UserInput (Press,Ctrl,Alt,CtrlAlt) , UserInput (Press,Ctrl,Alt,CtrlAlt)
, inputMapFromList , inputMapFromList
-- The main loop function to call
, yMainLoop) where
import Numeric (readHex) -- The main loop function to call
, yMainLoop
) where
-- A bunch of imports
import Numeric (readHex) -- to read hexadecimal values
-- Import of OpenGL and GLUT
-- but, I use my own Color type, therefore I hide the definition
-- of Color inside GLUT and OpenGL packages
import Graphics.Rendering.OpenGL hiding (Color) import Graphics.Rendering.OpenGL hiding (Color)
import Graphics.UI.GLUT hiding (Color) import Graphics.UI.GLUT hiding (Color)
import Data.IORef import Data.IORef
-- I use Map to deal with user interaction
import qualified Data.Map as Map import qualified Data.Map as Map
-- Some standard stuff
import Control.Monad (when) import Control.Monad (when)
import Data.Maybe (isNothing) import Data.Maybe (isNothing)
@ -56,12 +69,13 @@ import Data.Maybe (isNothing)
type Point = GLfloat type Point = GLfloat
-- | A Scalar value -- | A Scalar value
type Scalar = GLfloat type Scalar = GLfloat
-- | The time type (currently its Int -- | The time type (currently its Int)
type Time = Int type Time = Int
-- | A 3D Point mainly '(x,y,z)' -- | A 3D Point mainly '(x,y,z)'
data Point3D = P (Point,Point,Point) deriving (Eq,Show,Read) data Point3D = P (Point,Point,Point) deriving (Eq,Show,Read)
type Color = Color3 Scalar type Color = Color3 Scalar
-- Get x (resp. y, z) coordinate of a 3D point
xpoint :: Point3D -> Point xpoint :: Point3D -> Point
xpoint (P (x,_,_)) = x xpoint (P (x,_,_)) = x
ypoint :: Point3D -> Point ypoint :: Point3D -> Point
@ -69,9 +83,11 @@ ypoint (P (_,y,_)) = y
zpoint :: Point3D -> Point zpoint :: Point3D -> Point
zpoint (P (_,_,z)) = z zpoint (P (_,_,z)) = z
-- Create a Point3D element from a triplet
makePoint3D :: (Point,Point,Point) -> Point3D makePoint3D :: (Point,Point,Point) -> Point3D
makePoint3D = P makePoint3D = P
-- Make Point3D an instance of Num
instance Num Point3D where instance Num Point3D where
(+) (P (ax,ay,az)) (P (bx,by,bz)) = P (ax+bx,ay+by,az+bz) (+) (P (ax,ay,az)) (P (bx,by,bz)) = P (ax+bx,ay+by,az+bz)
(-) (P (ax,ay,az)) (P (bx,by,bz)) = P (ax-bx,ay-by,az-bz) (-) (P (ax,ay,az)) (P (bx,by,bz)) = P (ax-bx,ay-by,az-bz)
@ -82,11 +98,12 @@ instance Num Point3D where
signum (P (x,y,z)) = P (signum x, signum y, signum z) signum (P (x,y,z)) = P (signum x, signum y, signum z)
fromInteger i = P (fromInteger i, 0, 0) fromInteger i = P (fromInteger i, 0, 0)
-- The scalar product
infixr 5 -*< infixr 5 -*<
(-*<) :: Scalar -> Point3D -> Point3D (-*<) :: Scalar -> Point3D -> Point3D
(-*<) s p = P (s*xpoint p, s*ypoint p, s*zpoint p) (-*<) s p = P (s*xpoint p, s*ypoint p, s*zpoint p)
-- Used internally to convert point3D to different types
toGLVector3 :: Point3D -> Vector3 GLfloat toGLVector3 :: Point3D -> Vector3 GLfloat
toGLVector3 (P(x,y,z)) = Vector3 x y z toGLVector3 (P(x,y,z)) = Vector3 x y z
@ -105,31 +122,46 @@ data Box3D = Box3D {
, maxPoint :: Point3D , maxPoint :: Point3D
, resolution :: Scalar } , resolution :: Scalar }
-- | An helper to make a Box3D
makeBox :: (Point,Point,Point) -> (Point,Point,Point) -> Scalar -> Box3D makeBox :: (Point,Point,Point) -> (Point,Point,Point) -> Scalar -> Box3D
makeBox mini maxi res = Box3D { makeBox mini maxi res = Box3D {
minPoint = makePoint3D mini minPoint = makePoint3D mini
, maxPoint = makePoint3D maxi , maxPoint = makePoint3D maxi
, resolution = res } , resolution = res }
-- | A Triangle3D is simply 3 points and a color
type Triangle3D = (Point3D,Point3D,Point3D,Color) type Triangle3D = (Point3D,Point3D,Point3D,Color)
-- For a general purpose library we should add many other different atoms
-- corresponding to Quads for example. -- | The type Atom is the atom for our display here we'll only use triangles.
-- | For a general purpose library we should add many other different atoms
-- | corresponding to Quads for example.
data Atom = ColoredTriangle Triangle3D data Atom = ColoredTriangle Triangle3D
-- | A Function3D is simply a function for each x,y associate a z and a color
-- | If undefined at point (x,y), it returns Nothing.
type Function3D = Point -> Point -> Maybe (Point,Color) type Function3D = Point -> Point -> Maybe (Point,Color)
-- | Our objects that will be displayed
-- | Wether a function3D delimited by a Box
-- | or a list of Atoms
data YObject = XYFunc Function3D Box3D data YObject = XYFunc Function3D Box3D
| Atoms [Atom] | Atoms [Atom]
-- | The function atoms retrieve the list of atoms from an YObject
atoms :: YObject -> [Atom] atoms :: YObject -> [Atom]
atoms (XYFunc f b) = getObject3DFromShapeFunction f b atoms (XYFunc f b) = getObject3DFromShapeFunction f b
atoms (Atoms atomList) = atomList atoms (Atoms atomList) = atomList
-- | We decalre the input map type we need here -- | We decalre the input map type we need here
-- | It is our API -- | It is our API
-- | I don't use Mouse but it can be easily added
type InputMap worldType = Map.Map UserInput (worldType -> worldType) type InputMap worldType = Map.Map UserInput (worldType -> worldType)
data UserInput = Press Char | Ctrl Char | Alt Char | CtrlAlt Char data UserInput = Press Char | Ctrl Char | Alt Char | CtrlAlt Char
deriving (Eq,Ord,Show,Read) deriving (Eq,Ord,Show,Read)
-- | A displayable world -- | A displayable world is a type for which
-- | ther exists a function that provide sufficient informations
-- | to provide a camera, lights, objects and a window title.
class DisplayableWorld world where class DisplayableWorld world where
camera :: world -> Camera camera :: world -> Camera
camera _ = defaultCamera camera _ = defaultCamera
@ -147,6 +179,7 @@ data Camera = Camera {
, camDir :: Point3D , camDir :: Point3D
, camZoom :: Scalar } , camZoom :: Scalar }
-- | A default initial camera
defaultCamera :: Camera defaultCamera :: Camera
defaultCamera = Camera { defaultCamera = Camera {
camPos = makePoint3D (0,0,0) camPos = makePoint3D (0,0,0)
@ -154,8 +187,8 @@ defaultCamera = Camera {
, camZoom = 1 } , camZoom = 1 }
-- Given a shape function and a delimited Box3D -- | Given a shape function and a delimited Box3D
-- return a list of Triangles to be displayed -- | return a list of Atoms (here only colored triangles) to be displayed
getObject3DFromShapeFunction :: Function3D -> Box3D -> [Atom] getObject3DFromShapeFunction :: Function3D -> Box3D -> [Atom]
getObject3DFromShapeFunction shape box = do getObject3DFromShapeFunction shape box = do
x <- [xmin,xmin+res..xmax] x <- [xmin,xmin+res..xmax]
@ -194,6 +227,7 @@ getObject3DFromShapeFunction shape box = do
ymax = ypoint $ maxPoint box ymax = ypoint $ maxPoint box
res = resolution box res = resolution box
-- | Get the user input map from a list
inputMapFromList :: (DisplayableWorld world) => inputMapFromList :: (DisplayableWorld world) =>
[(UserInput,world -> world)] -> InputMap world [(UserInput,world -> world)] -> InputMap world
inputMapFromList = Map.fromList inputMapFromList = Map.fromList
@ -203,13 +237,17 @@ inputMapFromList = Map.fromList
- As you can see the code is _not_ pure - As you can see the code is _not_ pure
- and not even functionnal friendly! - and not even functionnal friendly!
- But when called, - But when called,
- it will look like a standard function. - it will look like a pure functional function.
--} --}
yMainLoop :: (DisplayableWorld worldType) => yMainLoop :: (DisplayableWorld worldType) =>
InputMap worldType -- the mapping user input / world -- the mapping user input / world
InputMap worldType
-- function that modify the world
-> (Time -> worldType -> worldType) -> (Time -> worldType -> worldType)
-> worldType -- the world state -- the world state of type worldType
-> IO () -- into IO () for obvious reason -> worldType
-- into IO () for obvious reason
-> IO ()
yMainLoop inputActionMap yMainLoop inputActionMap
worldTranformer worldTranformer
world = do world = do
@ -229,14 +267,13 @@ yMainLoop inputActionMap
Just (keyboardMouse inputActionMap worldRef) Just (keyboardMouse inputActionMap worldRef)
-- We generate one frame using the callback -- We generate one frame using the callback
displayCallback $= display worldRef displayCallback $= display worldRef
normalize $= Enabled -- let OpenGL resize normal vectors to unity -- let OpenGL resize normal vectors to unity
normalize $= Enabled
shadeModel $= Smooth shadeModel $= Smooth
-- Lights -- Lights (in a better version should be put elsewhere)
lighting $= Enabled lighting $= Enabled
ambient (Light 0) $= Color4 0.5 0.5 0.5 1 ambient (Light 0) $= Color4 0.5 0.5 0.5 1
diffuse (Light 0) $= Color4 1 1 1 1 diffuse (Light 0) $= Color4 1 1 1 1
-- specular (Light 0) $= Color4 1 1 1 1
-- position (Light 0) $= Vertex4 (-5) 5 10 0
light (Light 0) $= Enabled light (Light 0) $= Enabled
pointSmooth $= Enabled pointSmooth $= Enabled
@ -245,8 +282,8 @@ yMainLoop inputActionMap
materialDiffuse Front $= Color4 0.0 0.0 0.0 1 materialDiffuse Front $= Color4 0.0 0.0 0.0 1
materialSpecular Front $= Color4 1 1 1 1 materialSpecular Front $= Color4 1 1 1 1
materialEmission Front $= Color4 0.0 0.0 0.0 1 materialEmission Front $= Color4 0.0 0.0 0.0 1
-- We enter the main loop
materialShininess Front $= 96 materialShininess Front $= 96
-- We enter the main loop
mainLoop mainLoop
-- When no user input entered do nothing -- When no user input entered do nothing
@ -257,15 +294,15 @@ idle worldTranformer world = do
world $= worldTranformer t w world $= worldTranformer t w
postRedisplay Nothing postRedisplay Nothing
-- Get User Input -- | Get User Input
-- both cleaner, terser and more expendable than the preceeding code -- | both cleaner, terser and more expendable than the preceeding code
keyboardMouse :: InputMap a -> IORef a keyboardMouse :: InputMap a -> IORef a
-> Key -> KeyState -> Modifiers -> Position -> IO() -> Key -> KeyState -> Modifiers -> Position -> IO()
keyboardMouse input world key state _ _ = keyboardMouse input world key state _ _ =
when (state == Down) $ when (state == Down) $
let let
charFromKey (Char c) = c charFromKey (Char c) = c
-- To replace -- To complete if you want to finish it
charFromKey _ = '#' charFromKey _ = '#'
transformator = Map.lookup (Press (charFromKey key)) input transformator = Map.lookup (Press (charFromKey key)) input
@ -278,7 +315,7 @@ keyboardMouse input world key state _ _ =
world $= transform w world $= transform w
-- The function that will display datas -- | The function that will display datas
display :: (HasGetter g, DisplayableWorld world) => display :: (HasGetter g, DisplayableWorld world) =>
g world -> IO () g world -> IO ()
display worldRef = do display worldRef = do
@ -293,6 +330,7 @@ display worldRef = do
-- and refere to competent authorities -- and refere to competent authorities
let cam = camera w let cam = camera w
-- set the background color (dark solarized theme) -- set the background color (dark solarized theme)
-- Could also be externalized to world state
clearColor $= Color4 0 0.1686 0.2117 1 clearColor $= Color4 0 0.1686 0.2117 1
clear [ColorBuffer,DepthBuffer] clear [ColorBuffer,DepthBuffer]
-- Transformation to change the view -- Transformation to change the view
@ -314,6 +352,7 @@ display worldRef = do
scalarFromHex :: String -> Scalar scalarFromHex :: String -> Scalar
scalarFromHex = (/256) . fst . head . readHex scalarFromHex = (/256) . fst . head . readHex
-- | Color from CSS style color string
hexColor :: String -> Color hexColor :: String -> Color
hexColor ('#':rd:ru:gd:gu:bd:bu:[]) = Color3 (scalarFromHex [rd,ru]) hexColor ('#':rd:ru:gd:gu:bd:bu:[]) = Color3 (scalarFromHex [rd,ru])
(scalarFromHex [gd,gu]) (scalarFromHex [gd,gu])
@ -321,11 +360,11 @@ hexColor ('#':rd:ru:gd:gu:bd:bu:[]) = Color3 (scalarFromHex [rd,ru])
hexColor ('#':r:g:b:[]) = hexColor ['#',r,r,g,g,b,b] hexColor ('#':r:g:b:[]) = hexColor ['#',r,r,g,g,b,b]
hexColor _ = error "Bad color!!!!" hexColor _ = error "Bad color!!!!"
-- | Helper to make a color from RGB scalar values
makeColor :: Scalar -> Scalar -> Scalar -> Color makeColor :: Scalar -> Scalar -> Scalar -> Color
makeColor = Color3 makeColor = Color3
---
-- drawObject :: (YObject obj) => obj -> IO() -- | Where the drawing occurs
drawObject :: YObject -> IO() drawObject :: YObject -> IO()
drawObject shape = renderPrimitive Triangles $ drawObject shape = renderPrimitive Triangles $
mapM_ drawAtom (atoms shape) mapM_ drawAtom (atoms shape)
@ -339,6 +378,8 @@ drawAtom atom@(ColoredTriangle (p0,p1,p2,c)) = do
vertex $ toGLVertex3 p1 vertex $ toGLVertex3 p1
vertex $ toGLVertex3 p2 vertex $ toGLVertex3 p2
-- get the normal vector of an Atom -- | get the normal vector of an Atom
-- I don't normalize it; it is done by OpenGL
-- in main with 'normalize $= Enabled'
getNormal :: Atom -> Point3D getNormal :: Atom -> Point3D
getNormal (ColoredTriangle (p0,p1,p2,_)) = (p1 - p0) * (p2 - p0) getNormal (ColoredTriangle (p0,p1,p2,_)) = (p1 - p0) * (p2 - p0)