How to create this simple gif animation with Diagrams - haskell

I am trying to understand how to use Diagrams library in Haskell.
Here is my attempt at making a simple gif animation which switches between two circles of radii 1 and 2. I tried to mimic what I saw on
Here is the code, I tried.
{-# LANGUAGE NoMonomorphismRestriction #-}
import Diagrams.Backend.SVG.CmdLine
import Diagrams.Prelude
delays = take 2 (repeat 3)
gif :: [(Diagram B, Int)]
gif = zip [circle 1, circle 2] delays
main = mainWith $ gif
But this fails to compile! The errors I get are
[1 of 1] Compiling Main ( maze.hs, maze.o )
maze.hs:10:8:
No instance for (Diagrams.Backend.CmdLine.Parseable
(Diagrams.Backend.CmdLine.MainOpts
[(QDiagram B V2 Double Any, Int)]))
arising from a use of ‘mainWith’
In the expression: mainWith
In the expression: mainWith $ gif
In an equation for ‘main’: main = mainWith $ gif
Where am I going wrong in the code above?

It took me a moment to figure it out, because the error message is not obvious at all, but it's a backend problem. The documentation states that the cairo backend can create animated gif. However, you are using the svg backend (which is unable to understand what [(Diagram B, Int)] is.
To solve this, make sure you have the diagrams-cairo package installed, and change the line
import Diagrams.Backend.SVG.CmdLine
to
import Diagrams.Backend.Cairo.CmdLine

I'll explain the other steps necessary, since this is one of the first search results for doing animations in diagrams. Diagrams rescales the viewport to the image size for each circle, so that circle 1 and circle 2 are equivalent. To solve this you can lay the circles on invisible squares of the same size as circle 2.
Also the delays are given in 1/100 seconds. The correct code would then be:
{-# LANGUAGE NoMonomorphismRestriction #-}
import Diagrams.Backend.Cairo.CmdLine
import Diagrams.Prelude
gif :: [(Diagram B, Int)]
gif = map (\x -> (x # lc white <> square 4 # lw none,300)) [circle 1,circle 2]
main = mainWith gif

Related

Is there a way to have the origin be in the top left corner when using Gloss

I'm making an Asteroids clone in Haskell using Gloss.
I want the spaceship to come back from the left if it goes out on the right side of the screen.
This is made unnecessarily tricky by the fact that the origin (0, 0) is in the middle of the screen.
Is there a way to move the origin to the top (or bottom) left?
My Main module (where I'm interacting with Gloss) looks like this:
module Main where
import Graphics.Gloss (Display (InWindow), black, play)
-- my own imported code
import Assets (loadAssets)
import GameState (defaultHeight, defaultWidth, initGameState)
import HandleInput (handleInput)
import ProgressGameState (progressGameState)
import Render (render)
windowDisplay :: Display
windowDisplay = InWindow "Window" (defaultWidth, defaultHeight) (10, 10)
main :: IO ()
main = do
assets <- loadAssets
play
windowDisplay
black
60
initGameState
(render assets)
handleInput
progressGameState
I don't think there's an easy way to move the origin. But the conversion between whatever your format is a simple translation. You can use a function (or even a smart constructor) to do the calculation between the two.
For example, if (0,0) is at the center of a picture with the dimensions (x,y), but you would want it to be at the top left, all you have to do is substract (x/2,y/2) from all your points to make the math right again.

Haskell get Matrix from bmp Image

Hi Everyone Im beginner in Haskell I want to read and extract matrix of pixels (numbers) from a grayscale bmp image in haskell
anyone has idea how to do that I searched online without finding any clues thank you
Unfortunately, there are not as many tutorials on this topic as there should be, but it is at least not as hard as it might seem at first. As #leftaroundabout pointed out, the first thing you need is to decide on the library you want to use for dealing matrices. I personally would recommend massiv for array manipulation and massiv-io for reading and writing images. You didn't specify what exactly you want to do with the image, so all I am gonna provide in this example is reading a bmp image into an array, printing some stuff about the matrix and the writing it in jpg format:
module Main where
import Data.Massiv.Array as A
import Data.Massiv.Array.IO
main :: IO ()
main = do
img <- readImage "frog.bmp" :: IO (Image S SRGB Word8)
putStrLn $ "Size: " ++ show (size img)
putStrLn $ "Pixel at <row:10, col:20>: " ++ show (img ! (10 :. 20))
writeImageAuto "frog.jpg" img
Then if you save that snippet into a file named convert-bmp.hs, place frog.bmp image next to it then using stack:
$ stack --resolver lts-16.25 runghc --package massiv --package massiv-io convert-bmp.hs
Size: Sz (200 :. 320)
Pixel at <row:10, col:20>: <SRGB:( 61, 64, 35)>

How to combine shapes via set operations?

I would like to subtract one shape from another, and then combine the resulting shape with another shape. In my example a square is to be clipped in half and that clipped version is to be extended by a half circle to the right.
So I subtract one square from the other via difference and make a union with the whole circle assuming that overlapping areas will just merge.
I'm thinking in terms of set operations where ({1,2,3,4} / {3,4}) U {2,3} equals {1,2,3} but in my implementation it equals {1,3}:
import Diagrams.Backend.SVG.CmdLine
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
import Diagrams.Prelude
import qualified Diagrams.TwoD.Path.Boolean as B
main = mainWith (combination # fc red # bgFrame 0.1 white)
where
combination :: QDiagram B V2 Double Any
combination = strokePath plusCircle
shorterSquare = B.difference Winding (square 2) (square 2 # translateX 1)
plusCircle = B.union Winding (circle 1 <> shorterSquare)
But I get this:
This is not what I want, I want the half circle merged with the rectangle, and the result to be filled just red with no lines inside.
This particular usage of B.difference reverses the direction of the shorterSquare path, so you need to re-reverse it:
shorterSquare = B.difference Winding (square 2) (square 2 # translateX 1)
# reversePath
As this is quite subtle, it is worth it to spend a moment describing how I diagnosed it. Firstly, such fill rule wackiness felt quite like the sort of issue caused by path (or face, etc.) orientations. Secondly, redefining shorterSquare as...
shorterSquare = square 2 # scaleX 0.5 # translateX 0.5
... gives the expected result. That means the issue has to do with B.difference and the definition of shorterSquare, rather than with B.union. Confirmation can be obtained through pathVertices:
GHCi> -- Counterclockwise.
GHCi> pathVertices $ square 2 # scaleX 0.5 # translateX 0.5
[[P (V2 1.0 (-1.0)),P (V2 0.9999999999999999 1.0),P (V2 (-1.1102230246251565e-16) 1.0),P (V2 (-2.220446049250313e-16) (-1.0))]]
GHCi> -- Clockwise.
GHCi> pathVertices $ B.difference Winding (square 2) (square 2 # translateX 1)
[[P (V2 (-1.0) 1.0),P (V2 0.0 1.0),P (V2 0.0 (-1.0)),P (V2 (-1.0) (-1.0))]]
I'm not an expert on Diagrams, but it looks like you are combining stroke paths rather than the shapes they represent. Fill Rules has some interesting things to say about how the Winding fill rule behaves for stroke paths that overlap themselves, which seems relevant to explaining why you get the result you do.
Instead, I'd suggest using the techniques in Composing diagrams, such as atop, to compose the completed shapes.

Don't enter gnuplot terminal

Using the gnuplot module, after I plot a graph I get put into the gnuplot console. Is there a way to stop this happening? I never use it, and it's annoying to have to type exit after closing each graph, especially when I'm testing a sequence of plots.
For example:
module Main where
import Graphics.Gnuplot.Simple
main = do
let xs = linearScale 100 (-10, 10) :: [Float]
plotFunc [] xs sin
plotFunc [] xs cos
I'm using Windows 7 x64 and the Haskell Platform 2013.2.0.0 if that makes a difference.
Try the following:
bind Close "exit gnuplot"
It should do the trick!
See here for bind details if you aren't familiar: bind reference

Dynamically updating a plot in Haskell

I have a program which performs a long-going calculation where the result is shown as a plot.
I am currently using Chart-0.14 for this. I want to show the partial results, and update during calculations.
Graphics.Rendering.Chart.Gtk.updateCanvas :: Renderable a -> DrawingArea -> IO Bool seems to do that, but I do not find any way to get a DrawingArea from the plot. The function renderableToWindow :: Renderable a -> Int -> Int -> IO () does not return anything (and furthermore it does not return before the window is closed).
I would like to do something like the following:
main = do
drawingArea = forkRenderableToWindow (toRenderable $ plotLayout $
plot [0,0.1..10] sin "sin(x)") 640 480
updateCanvas (toRenderable $ plotLayout $ plot [0,0.1..10] sin "sin(x)") drawingArea
How should I do this? Would I need to reimplement the functions in Graphics.Rendering.Chart.Gtk with a version that returns the DrawingArea and in some way (how would I do this? forkIO?) returns immediately without closing the window?
You are looking for createRenderableWindow and then you need to use the GTK operations to work on the given Window - I don't think the Chart package exports any higher level operations on Windows.
EDIT2: So ignore the below - it doesn't work even with GUI initilization. My comment was a guess based on types.
EDIT:
Here is some example code. Understand, I'm just piecing things together based on the types. There might be better ways to do things if you ask someone who actually knows the library.
Below we use:
createRenderableWindow - this was the crux of my answer
castToDrawingArea - This is needed to get a DrawingArea from the Window type provided by GTK. These casts are taking place of C++ OO inheritance, I think.
widgetShowAll - because we haven't actually displayed the window, we best do that. I stole this function after looking at the source for renderableToWindow.
updateCanvas - I just saw this in the haddock documentation and figured it is why you wanted a DrawingArea in the first place.
Now for the code:
import Graphics.Rendering.Chart.Gtk
import Graphics.Rendering.Chart.Renderable
import Graphics.UI.Gtk.Misc.DrawingArea
import qualified Graphics.UI.Gtk as G
main = do
win <- createRenderableWindow emptyRenderable 400 400
let draw = castToDrawingArea win
G.widgetShowAll win
updateCanvas emptyRenderable draw

Resources