I'm working on a Haskell program for playing spatial games: I have a graph of a bunch of "individuals" playing the Prisoner's Dilemma, but only with their immediate neighbors, and copying the strategies of the people who do best.
I've reached a point where I need to draw an image of the world, and this is where I've hit problems. Two of the possible geometries are easy: if people have four or eight neighbors each, then I represent each one as a filled square (with color corresponding to strategy) and tile the plane with these. However, I also have a situation where people have six neighbors (hexagons) or three neighbors (triangles).
My question, then, is: what's a good Haskell library for creating images and drawing shapes on them? I'd prefer that it create PNGs, but I'm not incredibly picky. I was originally using Graphics.GD, but it only exports bindings to functions for drawing points, lines, arcs, ellipses, and non-rotated rectangles, which is not sufficient for my purposes (unless I want to draw hexagons pixel by pixel*). I looked into using foreign import
, but it's proving a bit of a hassle (partly because the polygon-drawing function requires an array of gdPoint
structs), and given that my requirements may grow, it would be nice to use an in-Haskell solution and not have to muck about with the FFI (though if push comes to shove, I'm willing to do that). Any suggestions?
* That is also an option, actually; any tips on how to do that would also be appreciated, though I think a library would be easier.
EDIT: Thank you all so much for your suggestions. Unfortunately, I wasn't able to get all of gtk2hs's required libraries to build, which ruled out a lot of solutions. For a variety of reasons, after I tried all your answers, failed to install a number of libraries and found that others could not do what I wanted, I ended up deciding to just export more of an FFI for libgd and used that instead.
Diagrams looks way cool, but if you want to avoid committing and stay super lightweight, you could generate svg directly. Stealing from Conrad Barski at http://www.lisperati.com/haskell/
type Point = (Float,Float)
type Color = (Int,Int,Int)
type Polygon = [Point]
writePoint :: Point -> String
writePoint (x,y) = (show x)++","++(show y)++" "
writePolygon :: (Color,Polygon) -> String
writePolygon ((r,g,b),p) = "<polygon points=\""++(concatMap writePoint p)++"\" style=\"fill:#cccccc;stroke:rgb("++(show r)++","++(show g)++","++(show b)++");stroke-width:2\"/>"
writePolygons :: [(Color,Polygon)] -> String
writePolygons p = "<svg xmlns=\"http://www.w3.org/2000/svg\">"++(concatMap writePolygon p)++"</svg>"
colorize :: Color -> [Polygon] -> [(Color,Polygon)]
colorize = zip.repeat
rainbow@[red,green,blue,yellow,purple,teal] = map colorize [(255,0,0),(0,255,0),(0,0,255),(255,255,0),(255,0,255),(0,255,255)]
t0 = writeFile "tut0.svg" $ writePolygons (blue [[(100,100),(200,100),(200,200),(100,200)],[(200,200),(300,200),(300,300),(200,300)]])
hexagon c r = translateTo c basicHexagon where
basicHexagon = top ++ (negate r, 0):bottom
top = [(r,0),(r * cos 1,(r * sin 1)),(negate (r * cos 1), r * (sin 1))]
bottom = map (\(x,y)->(x,negate y)) (reverse top)
translateTo (x,y) poly = map f poly where f (a,b)= ((a+x),(b+y))
t1 = writeFile "t1.svg" $ writePolygons (blue [hexagon (100,100) 50] )
hexField r n m = let
mkHex n = hexagon (1.5*n*(r*2),(r*2)) r
row n = map mkHex [1..n]
aRow = row n
in concat [map (offset (r*x)) aRow |x<-[1..m]]
offset r polys = map (oh r) polys where
oh r pt@(x,y) = (x+(1.5*r),y+(r*sin 1))
t2 = writeFile "t2.svg" $ writePolygons (blue $ hexField 50 4 5 )
run t2 and load the file into Firefox or some other program that supports svg.
t2.svg ,exported png http://img30.imageshack.us/img30/2245/93715707.png
I've used HOpenGL before, but the problem is that (as far as I can tell) it can't render to a file, but only the screen; the same (again, as far as I can tell) seems to be true of SDL and Wx. I will look into Cairo, though.
For some reason I can not reply to this post so I have to quote it. You're incorrect about GL and SDL, you can make an off-screen surface/buffer or render-to-texture. Those libraries don't need such a function (and doesn't make much sense either) because you can do it yourself quite easily by accessing pixels in the buffer and writing it out yourself, even with the screen buffers you can access pixel data.
Just the other day I showed somebody how to do this with the Haskell SDL bindings:
http://hpaste.org/fastcgi/hpaste.fcgi/view?id=25047
Use a library that can write out to .PNG files, they will most likely take a raw pointer to pixel buffer which you can get from SDL/GL or copy it to a format which the library needs.
I Just found a Haskell binding for the library DevIL which can output .PNG files. Check out the function called writeImageFromPtr
Cairo is a good bet if you want to generate PNGs. Wumpus also looks promising, though I have never used it. If you just need to see it on the screen, graphics-drawingcombinators is an easy interface to OpenGL that will do what you need in a few lines (see example.hs
in the distribution).
Check out Diagrams:
http://hackage.haskell.org/cgi-bin/hackage-scripts/package/diagrams
The examples are quite nice.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With