views:

465

answers:

5

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.

+1  A: 

what's a good Haskell library for creating images and drawing shapes on them?

You have quite a few options, to my mind the following have all been used for games:

Those are the most common, and you might just choose based on features/familiarity.

Don Stewart
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.
Antal S-Z
+3  A: 

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).

luqui
I'll definitely look at Cairo (and installing gtk2hs...) and Wumpus, since I do need to save the image to a file. However, `graphics-drawingcombinators` looks really cool if I ever need to do HOpenGL-type stuff in the future (although, just FYI, the link you have points to to `wumpus-core` again).
Antal S-Z
Fixed the link target.
luqui
+3  A: 

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

snk_kid
Oh, wow—I hadn't thought about trying to solve the problem this way! This looks really promising (especially since gtk failed to install); I'll install this and see if I can make it work.
Antal S-Z
I forgot to mention one more thing, if you're going to access pixel data which is rendered into the screen buffers you probably should blit (copy) the entire buffer into a buffer in system memory, because typically the front/back screen buffers are in VRAM and you need to lock the buffer/surface you don't want to block the GPU with a long running operation.Also note that SDL has an option to create a screen surface which is created in system memory.
snk_kid
If you go this way, you should use *OpenGL's* access to the screen. SDL will just think it's empty. See http://hackage.haskell.org/packages/archive/OpenGL/latest/doc/html/Graphics-Rendering-OpenGL-GL-ReadCopyPixels.html .
luqui
+1  A: 

Check out Diagrams:

http://code.haskell.org/diagrams/

http://hackage.haskell.org/cgi-bin/hackage-scripts/package/diagrams

The examples are quite nice.

fryguybob
+1  A: 

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\"&gt;"++(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

ja