Biblioteca de gráficos Haskell que funciona en GHCi en macOS X


¿Existe una biblioteca de gráficos Haskell o un enlace a una biblioteca externa que cumpla con los siguientes requisitos:

  1. Se puede usar desde ghci, es decir, no tengo que vincular y reiniciar el programa.
  2. Funciona en macOS X. (Difícil en conjunción con 1!)
  3. Puede hacer gráficos vectoriales simples (líneas, polígonos, rellenos simples y trazos).
  4. Puede poner imágenes de mapa de bits en la pantalla. Ejemplo: blit a 17x12 .imagen bmp.

?

Por favor incluya un ejemplo de código fuente mínimo o una referencia a él (solo una ventana en la pantalla, tal vez con una línea verde dibujada dentro de ella) para que pueda verificar los puntos 1. y 2. especialmente. Además, si una de estas solicitudes de características es más elaborada (por ejemplo, OpenGL + 4), incluya una buena referencia.


PS: Con respecto a 1 y 2, sé acerca del truco enableGUI y estoy dispuesto a usarlo. Sin embargo, la mayoría de las bibliotecas tienen el problema de que no puede ejecutar la función main varias veces y, por lo tanto, no calificar.


Edit: Para evitar perder el tiempo, aquí una lista de paquetes que he probado:

  • wx - ghci se ahoga en libstdc++
  • sdl - redefine main para ser una macro. Solo en tiempo de compilación.
  • GLFW (OpenGL) - No se puede ejecutar main dos veces, algo sobre "falla porque no puede instalar el controlador de eventos del ratón".
Author: Heinrich Apfelmus, 2011-05-03

5 answers

EDITAR: En realidad, ya no estoy seguro. Varias versiones más tarde, parece que GLFW ya no funciona en GHCi en OS X.

¡Resulta que GLFW + OpenGL cumple con los cuatro requisitos!

  1. Necesita invocar ghci con ghci -framework Carbon.
  2. Necesita el archivo EnableGUI.hs, que puede obtener aquí. Tenga en cuenta que no se puede cargar directamente en GHCi, tienes que comiple, primero.
  3. OpenGL tiene un modo de proyección 2D donde se pueden dibujar líneas y polígonos.
  4. Mapas de bits se puede cargar como texturas y poner en polígonos.

Aquí hay un pequeño ejemplo que pone un mapa de bits en la pantalla. Hay algunas restricciones en el mapa de bits: sus dimensiones deben ser una potencia de dos (aquí 256) y debe ser un archivo .tga (aquí "Bitmap.tga"). Pero dado que se apoya la transparencia, esto no es un gran problema.

Debería poder llamar a main varias veces sin problema. El punto clave es que no debes llamar a 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

Aquí un ejemplo de mapa de bits (que necesita convertir a .tga).

Mapa de bits de muestra

 15
Author: Heinrich Apfelmus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-20 12:27:00

La biblioteca Gtk2Hs cumple todos los requisitos si utiliza la versión X11 del framework gtk2.

En cuanto a los requisitos:

  1. El uso de X11 evita muchos problemas.
  2. Instale gtk2a través de MacPorts y use la opción +x11 (predeterminada). (Dicho esto, he tenido numerosos problemas instalando gtk2 en el pasado, pero esta vez parecía funcionar.)
  3. Me sorprendería si GTK+ no puede hacer eso.
  4. Ídem.

Aquí un mínimo ejemplo

import Graphics.UI.Gtk

hello :: (ButtonClass o) => o -> IO ()
hello b = set b [buttonLabel := "Hello World"]

main :: IO ()
main = do
    initGUI
    window <- windowNew
    button <- buttonNew
    set window [windowDefaultWidth := 200, windowDefaultHeight := 200,
              containerChild := button, containerBorderWidth := 10]
    onClicked button (hello button)
    onDestroy window mainQuit
    widgetShowAll window
    mainGUI
 3
Author: Heinrich Apfelmus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-07 17:24:31

A principios de 2014, no pude usar la respuesta @heinrich-apfelmus en Mac OS X. Sin embargo, este ejemplo de GLFW-b (link) funcionó.

Por lo tanto, asegúrese de tener:

$ cabal install glfw-b

Y, si probaste la respuesta de Apfelmus, es posible que necesites

$ ghc-pkg list
$ ghc-pkg unregister GLFW-x.x.x.x

Como ambos proporcionan Graphics.UI.GLFW, y obtendrá un "nombre de módulo Ambiguo" Gráficos.UI.GLFW '" de ghc. Luego solo probé el programa de ejemplo anterior y funcionó (Mac OS X, 10.9, Mavericks)

 2
Author: carlosayam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-09 11:11:04

¿Has visto el GLFW como se hace referencia http://plucky.cs.yale.edu/soe/software1.htm

 1
Author: Grady Player,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-03 20:36:02

Más información sobre Haskell + GUI + OpenGL está disponible en esta discusión: http://www.haskell.org/pipermail/haskell-cafe/2011-May/091991.html

 1
Author: amindfv,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-18 18:31:22