Derive ascii animation from Haskell?

I am trying to get a very fast and dirty animated display of some data created using Haskell. The simplest attempt to try is ASCII art - in other words, something like:

type Frame = [[Char]] -- each frame is given as an array of characters type Animation = [Frame] display :: Animation -> IO () display = ?? 

How can i do this?

Part I cannot understand how to ensure a minimum pause between frames; the rest is just to use putStrLn along with clearScreen from the ansi-terminal package found through this answer .

+6
source share
2 answers

Well, here is a rough sketch of what I will do:

 import Graphics.UI.SDL.Time (getTicks) import Control.Concurrent (threadDelay) type Frame = [[Char]] type Animation = [Frame] displayFrame :: Frame -> IO () displayFrame = mapM_ putStrLn timeAction :: IO () -> IO Integer timeAction act = do t <- getTicks act t' <- getTicks return (fromIntegral $ t' - t) addDelay :: Integer -> IO () -> IO () addDelay hz act = do dt <- timeAction act let delay = calcDelay dt hz threadDelay $ fromInteger delay calcDelay dt hz = max (frame_usec - dt_usec) 0 where frame_usec = 1000000 `div` hz dt_usec = dt * 1000 runFrames :: Integer -> Animation -> IO () runFrames hz frs = mapM_ (addDelay hz . displayFrame) frs 

Obviously, I use the SDL here exclusively for getTicks , because this is what I used before. Feel free to replace it with any other function to get the current time.

The first argument to runFrames - as the name implies - the frame rate in hertz, i.e. frames per second. The runFrames function first converts each frame into an action that draws it, and then gives each addDelay function, which checks the time before and after the action starts, then sleeps until the frame time has passed.

My own code will be slightly different from this because I usually had a more complex loop that would do other things, like polling SDL for events, processing background images, passing data to the next iteration, and & c. But the basic idea is the same.

Obviously, the nice thing about this approach is that, although you are still fairly simple, you get a consistent frame rate when possible, with an explicit means of indicating the target speed.

+5
source

It depends on CA McCann anwer, which works well, but is not long-term in the long run, in particular when the frame rate is not an integral part of the tick speed.

 import GHC.Word (Word32) -- import CAMcCann'sAnswer (Frame, Animation, displayFrame, getTicks, threadDelay) atTick :: IO () -> Word32 -> IO () act `atTick` t = do t' <- getTicks let delay = max (1000 * (t-t')) 0 threadDelay $ fromIntegral delay act runFrames :: Integer -> Animation -> IO () runFrames fRate frs = do t0 <- getTicks mapM_ (\(t,f) -> displayFrame f `atTick` t) $ timecode fRate32 t0 frs where timecode ν t0 = zip [ t0 + (1000 * i) `div` ν | i <- [0..] ] fRate32 = fromIntegral fRate :: Word32 
+2
source

Source: https://habr.com/ru/post/897502/


All Articles