Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Image drawing library for Haskell?

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.

like image 793
Antal Spector-Zabusky Avatar asked Apr 26 '10 03:04

Antal Spector-Zabusky


4 Answers

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

like image 52
ja. Avatar answered Nov 18 '22 06:11

ja.


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

like image 6
snk_kid Avatar answered Nov 18 '22 05:11

snk_kid


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

like image 5
luqui Avatar answered Nov 18 '22 05:11

luqui


Check out Diagrams:

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

The examples are quite nice.

like image 3
fryguybob Avatar answered Nov 18 '22 06:11

fryguybob