- Seting up the environment
- Compiling and running the project
- Defining the data structure to draw
First, you have to download and install the Haskell platform from:
For the creation and configuration of a cabal sandbox, I strongly recomend following this tutorial:
Every time you change one or more files from the folder that composes the sandbox, you have to run the following command:
cabal build
or
cabal run
First, we name the module "DataGraph":
module DataGraph where
Then, import the required libraries:
import Graphics.Rendering.OpenGL
import Graphics.UI.GLUT
import Data.Graph
import System.Random
For convenience, we create the synonym type "GraphPoint", to be a triple of GLfloat (the float type for OpenGL bindings):
type GraphPoint = (GLfloat,GLfloat,GLfloat)
Then, we use the built-in function of module Data.Graph "buildG" to generate the graph to be drawn. The function takes an interval (n,m) of ints that will be the graph's vertex set, and a list of directed edges (v1,v2) to build the graph:
graph1 :: Graph
graph1 = buildG (0,10) [(1,2),
(2,3),
(3,1), (3,5), (3,4),
(4,1), (4,2),
(5,4),
(6,2), (6,5),
(7,1),
(8,2), (8,5),
(9,3),
(9,4), (9,5),
(10,9), (10,8)]
The function points3D will take every vertex from the graph previously defined, and for each one, it will return a 3D point to be drawn:
points3D :: Graph -> Int -> Int -> [ GraphPoint ]
points3D graph r1 r2 = [ getPoint r1 r2 v | v <- (vertices graph) ]
The next function in the module is getPoint. It takes two int seeds to generate two random lists, and returns a triple, where the x-coordinate is the point divided by 10 (to obtain a float between 0 and 1) and the other two coordinates are random floats also between 0 and 1, extracted as the v-th component of each of the random lists... I think that was not very clear, I will clarify soon:
getPoint :: Int -> Int -> Data.Graph.Vertex -> GraphPoint
getPoint r1 r2 v = let
randList1 = randomList r1
randList2 = randomList r2
in ((fromIntegral v)/10, randList1!!v, randList2!!v)
The function myPoints is just a synonym of points3D, but without the graph parameter in the middle:
myPoints :: Int -> Int -> [ GraphPoint ]
myPoints r1 r2 = points3D graph1 r1 r2
The function myEdges is a list of pairs of GraphPoints, where each component is the point previously generated for the corresponding vertex. The unit cube is just a reference that will be useful when we rotate the graph:
myEdges :: Int -> Int -> [ (GraphPoint, GraphPoint) ]
myEdges r1 r2 = [ (getPoint r1 r2 v1, getPoint r1 r2 v2) | (v1,v2) <- (edges graph1) ] ++ unitCube
The origin is represented by a constant, not to repeat too many times the same triple:
origin = (0.0, 0.0, 0.0)
These three functions add one to each component of a point:
plusX (x, y, z) = (x+1, y, z)
plusY (x, y, z) = (x, y+1, z)
plusZ (x, y, z) = (x, y, z+1)
- These functions are used to generate the reference axis:
unitCube = [ (origin , plusZ origin ), -- +z
(plusZ origin , plusX $ plusZ origin), -- +x
(plusX $ plusZ origin , plusX origin ), -- -z
(plusX origin , origin ), -- -x
(origin , plusX origin ), -- +x
(plusX origin , plusY $ plusX origin), -- +y
(plusY $ plusX origin , plusY origin ), -- -x
(plusY origin , origin ), -- -y
(origin , plusY origin ), -- +y
(plusY origin , plusZ $ plusY origin), -- +z
(plusZ $ plusY origin , plusZ origin ), -- -y
(plusZ origin , origin ) -- -z
]
Finally, the function randomList takes a seed and returns an infinite list of floats:
randomList :: Int -> [Float]
randomList seed = randoms (mkStdGen seed) :: [Float]