Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell Graphics Library that works in GHCi on MacOS X

Does there exist a Haskell graphics library or binding to an external library that fulfills the following requirements:

  1. Can be used from ghci, i.e. I don't have to link and restart the program.
  2. Works on MacOS X. (Tricky in conjunction with 1!)
  3. Can do simple vector graphics (lines, polygons, simple fills and strokes).
  4. Can put bitmap images on screen. Example: blit a 17x12 .bmp image.

?

Please include a minimal source code example or a reference to it (just a window on screen, maybe with a green line drawn inside it) so that I can check the points 1. and 2. in particular. Also, if one of these feature requests is more elaborate (for example OpenGL + 4), please include a good reference.


PS: Concerning 1 and 2, I know about the enableGUI trick and I am willing to use it. However, most libraries have the problem that you can't run the main function multiple times and hence don't qualify.


Edit: To avoid wasting your time, here a list of packages that I've tried:

  • wx - ghci chokes on libstdc++
  • sdl - redefines main to be a macro. Compile-time only.
  • GLFW (OpenGL) - Can't run main twice, something about "failing because it can't install mouse event handler".
like image 944
Heinrich Apfelmus Avatar asked May 03 '11 11:05

Heinrich Apfelmus


1 Answers

EDIT: Actually, I'm no longer sure. Several versions later, it seems that GLFW no longer works in GHCi on OS X.

It turns out that GLFW+OpenGL fulfills all four requirements!

  1. You need to invoke ghci with ghci -framework Carbon.
  2. You need the EnableGUI.hs file, which you can get here. Note that you can't load it right into GHCi, you have to comiple it, first.
  3. OpenGL has a 2D projection mode where you can draw lines and polygons.
  4. Bitmaps can be loaded as textures and put on polygons.

Here is a small example that puts a bitmap onto the screen. There are some restrictions on the bitmap: its dimensions must be a power of two (here 256) and it must be a .tga file (here "Bitmap.tga"). But since transparency is supported, this is not much of a problem.

You should be able to call main multiple times without problem. The key point is that you should not call GLFW.terminate.

import Graphics.Rendering.OpenGL as GL import qualified Graphics.UI.GLFW as GLFW import Graphics.Rendering.OpenGL (($=))  import Control.Monad import EnableGUI  main = do     enableGUI     GLFW.initialize     -- open window     GLFW.openWindow (GL.Size 400 400) [GLFW.DisplayAlphaBits 8] GLFW.Window     GLFW.windowTitle $= "Bitmap Test"      -- enable alpha channel     GL.blend         $= GL.Enabled     GL.blendFunc     $= (GL.SrcAlpha, GL.OneMinusSrcAlpha)     -- set the color to clear background     GL.clearColor    $= GL.Color4 0.8 0.8 0.8 0      -- set 2D orthogonal view inside windowSizeCallback because     -- any change to the Window size should result in different     -- OpenGL Viewport.     GLFW.windowSizeCallback $= \ size@(GL.Size w h) ->       do         GL.viewport   $= (GL.Position 0 0, size)         GL.matrixMode $= GL.Projection         GL.loadIdentity         GL.ortho2D 0 (realToFrac w) (realToFrac h) 0      render <- initialize     loop render      GLFW.closeWindow  loop render = do     -- draw the entire screen     render     -- swap buffer     GLFW.swapBuffers     -- check whether ESC is pressed for termination     p <- GLFW.getKey GLFW.ESC     unless (p == GLFW.Press) $ do         -- sleep for 1ms to yield CPU to other applications         GLFW.sleep 0.001         -- only continue when the window is not closed         windowOpenStatus <- GLFW.getParam GLFW.Opened         unless (windowOpenStatus == False) $             loop render  -- rendering initialize = do     -- load texture from file     GL.texture GL.Texture2D $= Enabled     [textureName] <- GL.genObjectNames 1     GL.textureBinding GL.Texture2D $= Just textureName     GL.textureFilter  GL.Texture2D $= ((GL.Nearest, Nothing), GL.Nearest)     GLFW.loadTexture2D "Bitmap.tga" []      return $ do         GL.clear [GL.ColorBuffer]         GL.renderPrimitive GL.Quads $ do             GL.texCoord $ texCoord2 0 0             GL.vertex   $ vertex3 (0) 256 0             GL.texCoord $ texCoord2 0 1             GL.vertex   $ vertex3 (0) (0) 0             GL.texCoord $ texCoord2 1 1             GL.vertex   $ vertex3 256 (0) 0             GL.texCoord $ texCoord2 1 0             GL.vertex   $ vertex3 256 256 0  -- type signatures to avoid ambiguity vertex3 :: GLfloat -> GLfloat -> GLfloat -> GL.Vertex3 GLfloat vertex3 = GL.Vertex3  texCoord2 :: GLfloat -> GLfloat -> GL.TexCoord2 GLfloat texCoord2 = GL.TexCoord2  color3 :: GLfloat -> GLfloat -> GLfloat -> GL.Color3 GLfloat color3 = GL.Color3 

Here an example bitmap (which you need to convert to .tga).

Sample bitmap

like image 110
Heinrich Apfelmus Avatar answered Oct 11 '22 10:10

Heinrich Apfelmus