packages feed

patat 0.11.0.0 → 0.12.0.0

raw patch · 10 files changed

+256/−48 lines, 10 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Patat.Presentation.Internal: [evalWrap] :: EvalSettings -> !EvalSettingsWrap
- Patat.Presentation.Settings: EvalWrapCode :: EvalSettingsWrap
- Patat.Presentation.Settings: EvalWrapRaw :: EvalSettingsWrap
- Patat.Presentation.Settings: EvalWrapRawInline :: EvalSettingsWrap
- Patat.Presentation.Settings: [evalWrap] :: EvalSettings -> !EvalSettingsWrap
- Patat.Presentation.Settings: data EvalSettingsWrap
- Patat.Presentation.Settings: instance Data.Aeson.Types.FromJSON.FromJSON Patat.Presentation.Settings.EvalSettingsWrap
- Patat.Presentation.Settings: instance GHC.Show.Show Patat.Presentation.Settings.EvalSettingsWrap
+ Patat.Presentation: [psTabStop] :: PresentationSettings -> !Maybe (FlexibleNum Int)
+ Patat.Presentation.Display.Internal: [dsTabStop] :: DisplaySettings -> !Int
+ Patat.Presentation.Internal: [evalContainer] :: EvalSettings -> !EvalSettingsContainer
+ Patat.Presentation.Internal: [psTabStop] :: PresentationSettings -> !Maybe (FlexibleNum Int)
+ Patat.Presentation.Settings: EvalContainerCode :: EvalSettingsContainer
+ Patat.Presentation.Settings: EvalContainerInline :: EvalSettingsContainer
+ Patat.Presentation.Settings: EvalContainerNone :: EvalSettingsContainer
+ Patat.Presentation.Settings: [evalContainer] :: EvalSettings -> !EvalSettingsContainer
+ Patat.Presentation.Settings: [psTabStop] :: PresentationSettings -> !Maybe (FlexibleNum Int)
+ Patat.Presentation.Settings: data EvalSettingsContainer
+ Patat.Presentation.Settings: instance Data.Aeson.Types.FromJSON.FromJSON Patat.Presentation.Settings.EvalSettingsContainer
+ Patat.Presentation.Settings: instance GHC.Show.Show Patat.Presentation.Settings.EvalSettingsContainer
+ Patat.Transition.Matrix: instance Data.Aeson.Types.FromJSON.FromJSON Patat.Transition.Matrix.Config
+ Patat.Transition.Matrix: transition :: Config -> TransitionGen
- Patat.Presentation: PresentationSettings :: !Maybe (FlexibleNum Int) -> !Maybe (FlexibleNum Int) -> !Maybe MarginSettings -> !Maybe Wrap -> !Maybe Theme -> !Maybe Bool -> !Maybe (FlexibleNum Int) -> !Maybe Int -> !Maybe ExtensionList -> !Maybe ImageSettings -> !Maybe Bool -> !Maybe EvalSettingsMap -> !Maybe Bool -> !Maybe [FilePath] -> !Maybe SpeakerNotesSettings -> !Maybe TransitionSettings -> PresentationSettings
+ Patat.Presentation: PresentationSettings :: !Maybe (FlexibleNum Int) -> !Maybe (FlexibleNum Int) -> !Maybe MarginSettings -> !Maybe Wrap -> !Maybe (FlexibleNum Int) -> !Maybe Theme -> !Maybe Bool -> !Maybe (FlexibleNum Int) -> !Maybe Int -> !Maybe ExtensionList -> !Maybe ImageSettings -> !Maybe Bool -> !Maybe EvalSettingsMap -> !Maybe Bool -> !Maybe [FilePath] -> !Maybe SpeakerNotesSettings -> !Maybe TransitionSettings -> PresentationSettings
- Patat.Presentation.Display.Internal: DisplaySettings :: !Size -> !Wrap -> !Margins -> !Theme -> !SyntaxMap -> DisplaySettings
+ Patat.Presentation.Display.Internal: DisplaySettings :: !Size -> !Wrap -> !Int -> !Margins -> !Theme -> !SyntaxMap -> DisplaySettings
- Patat.Presentation.Internal: EvalSettings :: !Text -> !Bool -> !Bool -> !EvalSettingsWrap -> EvalSettings
+ Patat.Presentation.Internal: EvalSettings :: !Text -> !Bool -> !Bool -> !EvalSettingsContainer -> EvalSettings
- Patat.Presentation.Internal: PresentationSettings :: !Maybe (FlexibleNum Int) -> !Maybe (FlexibleNum Int) -> !Maybe MarginSettings -> !Maybe Wrap -> !Maybe Theme -> !Maybe Bool -> !Maybe (FlexibleNum Int) -> !Maybe Int -> !Maybe ExtensionList -> !Maybe ImageSettings -> !Maybe Bool -> !Maybe EvalSettingsMap -> !Maybe Bool -> !Maybe [FilePath] -> !Maybe SpeakerNotesSettings -> !Maybe TransitionSettings -> PresentationSettings
+ Patat.Presentation.Internal: PresentationSettings :: !Maybe (FlexibleNum Int) -> !Maybe (FlexibleNum Int) -> !Maybe MarginSettings -> !Maybe Wrap -> !Maybe (FlexibleNum Int) -> !Maybe Theme -> !Maybe Bool -> !Maybe (FlexibleNum Int) -> !Maybe Int -> !Maybe ExtensionList -> !Maybe ImageSettings -> !Maybe Bool -> !Maybe EvalSettingsMap -> !Maybe Bool -> !Maybe [FilePath] -> !Maybe SpeakerNotesSettings -> !Maybe TransitionSettings -> PresentationSettings
- Patat.Presentation.Settings: EvalSettings :: !Text -> !Bool -> !Bool -> !EvalSettingsWrap -> EvalSettings
+ Patat.Presentation.Settings: EvalSettings :: !Text -> !Bool -> !Bool -> !EvalSettingsContainer -> EvalSettings
- Patat.Presentation.Settings: PresentationSettings :: !Maybe (FlexibleNum Int) -> !Maybe (FlexibleNum Int) -> !Maybe MarginSettings -> !Maybe Wrap -> !Maybe Theme -> !Maybe Bool -> !Maybe (FlexibleNum Int) -> !Maybe Int -> !Maybe ExtensionList -> !Maybe ImageSettings -> !Maybe Bool -> !Maybe EvalSettingsMap -> !Maybe Bool -> !Maybe [FilePath] -> !Maybe SpeakerNotesSettings -> !Maybe TransitionSettings -> PresentationSettings
+ Patat.Presentation.Settings: PresentationSettings :: !Maybe (FlexibleNum Int) -> !Maybe (FlexibleNum Int) -> !Maybe MarginSettings -> !Maybe Wrap -> !Maybe (FlexibleNum Int) -> !Maybe Theme -> !Maybe Bool -> !Maybe (FlexibleNum Int) -> !Maybe Int -> !Maybe ExtensionList -> !Maybe ImageSettings -> !Maybe Bool -> !Maybe EvalSettingsMap -> !Maybe Bool -> !Maybe [FilePath] -> !Maybe SpeakerNotesSettings -> !Maybe TransitionSettings -> PresentationSettings

Files

CHANGELOG.md view
@@ -1,5 +1,29 @@ # Changelog +## 0.12.0.0 (2024-02-27)++ *  Render tabs in code blocks by expanding them to spaces.  The amount of+    spaces a tab character aligns to is customizable using `tabStop`, e.g.+    `tabStop: 8`.  The default is 4.++ *  Rename eval.wrap to eval.container (#167)++    `wrap` is used at the top-level of settings for wrapping at a certain+    column, and inside `eval` to determine the type in which the result+    is "wrapped". Using the same name for both is confusing, so this adds+    `eval.container` as the new name for `eval.wrap`. `eval.wrap` will continue+    to be supported for the forseeable future, but its use will be discouraged.++    This also changes the values (again keeping the original ones for+    backwards-compat), so the complete changes to a configuration would be:++     -   `wrap: code` becomes `container: code`+     -   `wrap: raw` becomes `container: none`+     -   `wrap: rawInline` becomes `container: inline`++ *  Add a `type: matrix` transition effect, loosely inspired by the 1999 science+    fiction movie.+ ## 0.11.0.0 (2024-02-14)   *  Support wrapping at a specific column (#164)
README.md view
@@ -21,7 +21,7 @@ - [Centering](#centering) and [re-wrapping](#line-wrapping) text to terminal   width with proper indentation. - [Theming](#theming) support including 24-bit RGB.-- Hihgly portable as it only requires an ANSI terminal as opposed to+- Highly portable as it only requires an ANSI terminal as opposed to   something like `ncurses`.  ![screenshot](extra/demo.gif?raw=true)@@ -42,6 +42,7 @@ -   [Input format](#input-format) -   [Configuration](#configuration)     -   [Line wrapping](#line-wrapping)+    -   [Tab stop](#tab-stop)     -   [Margins](#margins)     -   [Auto advancing](#auto-advancing)     -   [Advanced slide splitting](#advanced-slide-splitting)@@ -266,7 +267,8 @@     Hello world.     ``` -3.  Within a slide, using a comment starting with `<!--config:`.  These+3.  In version 0.10 and later slides can be individually configured.+    Within a slide, using a comment starting with `<!--config:`.  These     settings can override configuration for that specific slide only.     There should not be any whitespace between `<!--` and `config:`. @@ -302,8 +304,14 @@  Line wrapping can be enabled by setting `wrap: true` in the configuration.  This will re-wrap all lines to fit the terminal width better.  You can also ask patat-to wrap at a specific column using e.g. `wrap: 60`.+to wrap at a specific column using `wrap: number`, e.g. `wrap: 60`. +### Tab stop++In version 0.12 and later, the amount of spaces a `\t` character in a code block+aligns to can be customized by setting `tabStop: number` in the configuration.+The default is `4`.+ ### Margins  Margins can be enabled by setting a `margins` entry in the configuration:@@ -336,9 +344,13 @@  #### Centering -To vertically center content, use `top: auto`. To horizontally center content,-use both `left: auto` and `right: auto`.  For example:+Version 0.11 and later can center content. + -  To vertically center content, use `top: auto`.+ -  To horizontally center content, use both `left: auto` and `right: auto`.++For example:+ ```markdown --- title: Centered presentation@@ -611,7 +623,7 @@  #### Native Images support -`patat-0.8.0.0` and newer include images support for some terminal emulators.+Version 0.8 and later include images support for some terminal emulators.  ```markdown ---@@ -656,7 +668,7 @@ [code evaluation](#evaluating-code).  In order to do that, for example, we could configure `kitten` code snippets-to evaluate using [Kitty]'s command `icat`.  This uses the `rawInline` code+to evaluate using [Kitty]'s command `icat`.  This uses the `none` container setting to ensure that the resulting output is not wrapped in a code block, and the `fragment` and `replace` settings immediately replace the snippet. @@ -667,7 +679,7 @@           command: sed 's/^/kitten /' | bash           replace: true           fragment: false-          wrap: rawInline+          container: none     ...      See, for example:@@ -712,7 +724,7 @@           command: irb --noecho --noverbose           fragment: true  # Optional           replace: false  # Optional-          wrap: code  # Optional+          container: code  # Optional     ...      Here is an example of a code block that is evaluated:@@ -728,18 +740,20 @@ code of presentations downloaded from the internet before running them if they contain `eval` settings. -Aside from the command, there are two more options:+Aside from the command, there are three more options:   -  `fragment`: Introduce a pause (see [fragments](#fragmented-slides)) in     between showing the original code block and the output.  Defaults to `true`.  -  `replace`: Remove the original code block and replace it with the output     rather than appending the output in a new code block.  Defaults to `false`.- -  `wrap`: By default, the output is wrapped in a code block again with the-    original syntax highlighting.  You can customize this behaviour by setting-    `wrap` to:+ -  `container`: By default, the output is wrapped in a code block again with+    the original syntax highlighting.  You can customize this behaviour by+    setting `container` to:      *  `code`: the default setting.-     *  `raw`: no formatting applied.-     *  `rawInline`: no formatting applied and no trailing newline.+     *  `none`: no formatting applied.+     *  `inline`: no formatting applied and no trailing newline.+ -  `wrap`: this is a deprecated name for `container`, used in version 0.11 and+    earlier.  Setting `fragment: false` and `replace: true` offers a way to "filter" code blocks, which can be used to render ASCII graphics.@@ -766,7 +780,7 @@  ### Speaker Notes -`patat` supports comments which can be used as speaker notes.+Version 0.9 and later support comments which can be used as speaker notes.  ```markdown ---@@ -817,10 +831,10 @@  ### Transitions -`patat` supports transitions in between slides.  A relatively fast terminal-emulator (e.g. [Alacritty], [Kitty], [iTerm2])-is suggested when enabling this, to avoid too much flickering -- some-flickering is unavoidable since we redraw the entire screen on each frame.+Version 0.10 and later support transitions in between slides.  A relatively+fast terminal emulator (e.g. [Alacritty], [Kitty], [iTerm2]) is suggested when+enabling this, to avoid too much flickering -- some flickering is unavoidable+since we redraw the entire screen on each frame.  ```yaml patat:@@ -851,6 +865,7 @@   -  `slideLeft`: slides the new slide in from right to left.  -  `dissolve`: changes characters over time.+ -  `matrix`: loosely inspired by the 1999 science fiction movie.  All transitions currently take these arguments: 
lib/Patat/Eval.hs view
@@ -72,10 +72,10 @@                     evalCommand <> ": exit code " <> T.pack (show i) <> "\n" <>                     erStderr         let fmt = "eval"-            blocks = case evalWrap of-                EvalWrapCode      -> [Pandoc.CodeBlock attr out]-                EvalWrapRaw       -> [Pandoc.RawBlock fmt out]-                EvalWrapRawInline -> [Pandoc.Plain [Pandoc.RawInline fmt out]]+            blocks = case evalContainer of+                EvalContainerCode   -> [Pandoc.CodeBlock attr out]+                EvalContainerNone   -> [Pandoc.RawBlock fmt out]+                EvalContainerInline -> [Pandoc.Plain [Pandoc.RawInline fmt out]]         pure $ case (evalFragment, evalReplace) of             (False, True) -> [Append blocks]             (False, False) -> [Append (orig : blocks)]
lib/Patat/Presentation/Display.hs view
@@ -60,8 +60,9 @@     settings  = activeSettings pres     ds        = DisplaySettings         { dsSize          = canvasSize-        , dsWrap          = fromMaybe NoWrap $ psWrap settings         , dsMargins       = margins settings+        , dsWrap          = fromMaybe NoWrap $ psWrap settings+        , dsTabStop       = maybe 4 A.unFlexibleNum $ psTabStop settings         , dsTheme         = fromMaybe Theme.defaultTheme (psTheme settings)         , dsSyntaxMap     = pSyntaxMap         }
lib/Patat/Presentation/Display/CodeBlock.hs view
@@ -1,5 +1,6 @@ -------------------------------------------------------------------------------- -- | Displaying code blocks, optionally with syntax highlighting.+{-# LANGUAGE BangPatterns      #-} {-# LANGUAGE OverloadedStrings #-} module Patat.Presentation.Display.CodeBlock     ( prettyCodeBlock@@ -7,6 +8,7 @@   --------------------------------------------------------------------------------+import           Data.Char.WCWidth                   (wcwidth) import           Data.Maybe                          (mapMaybe) import qualified Data.Text                           as T import           Patat.Presentation.Display.Internal@@ -50,12 +52,30 @@   --------------------------------------------------------------------------------+-- | Expands tabs in code lines.+expandTabs :: Int -> Skylighting.SourceLine -> Skylighting.SourceLine+expandTabs tabStop = goTokens 0+  where+    goTokens _    []                        = []+    goTokens col0 ((tokType, txt) : tokens) = goString col0 "" (T.unpack txt) $+        \col1 str -> (tokType, T.pack str) : goTokens col1 tokens++    goString :: Int -> String -> String -> (Int -> String -> k) -> k+    goString !col acc str k = case str of+        []       -> k col (reverse acc)+        '\t' : t -> goString (col + spaces) (replicate spaces ' ' ++ acc) t k+        c    : t -> goString (col + wcwidth c) (c : acc) t k+      where+        spaces = tabStop - col `mod` tabStop+++-------------------------------------------------------------------------------- prettyCodeBlock :: DisplaySettings -> [T.Text] -> T.Text -> PP.Doc prettyCodeBlock ds classes rawCodeBlock =     PP.vcat (map blockified sourceLines) <> PP.hardline   where     sourceLines :: [Skylighting.SourceLine]-    sourceLines =+    sourceLines = map (expandTabs (dsTabStop ds)) $         [[]] ++ highlight (dsSyntaxMap ds) classes rawCodeBlock ++ [[]]      prettySourceLine :: Skylighting.SourceLine -> PP.Doc@@ -65,7 +85,7 @@     prettyToken (tokenType, str) = themed         ds         (\theme -> syntaxHighlight theme tokenType)-        (PP.string $ T.unpack str)+        (PP.text str)      sourceLineLength :: Skylighting.SourceLine -> Int     sourceLineLength line = sum [T.length str | (_, str) <- line]
lib/Patat/Presentation/Display/Internal.hs view
@@ -18,6 +18,7 @@ data DisplaySettings = DisplaySettings     { dsSize      :: !Size     , dsWrap      :: !Wrap+    , dsTabStop   :: !Int     , dsMargins   :: !Margins     , dsTheme     :: !Theme.Theme     , dsSyntaxMap :: !Skylighting.SyntaxMap
lib/Patat/Presentation/Settings.hs view
@@ -16,7 +16,7 @@     , ImageSettings (..)      , EvalSettingsMap-    , EvalSettingsWrap (..)+    , EvalSettingsContainer (..)     , EvalSettings (..)      , SpeakerNotesSettings (..)@@ -49,6 +49,7 @@     , psColumns           :: !(Maybe (A.FlexibleNum Int))     , psMargins           :: !(Maybe MarginSettings)     , psWrap              :: !(Maybe Wrap)+    , psTabStop           :: !(Maybe (A.FlexibleNum Int))     , psTheme             :: !(Maybe Theme.Theme)     , psIncrementalLists  :: !(Maybe Bool)     , psAutoAdvanceDelay  :: !(Maybe (A.FlexibleNum Int))@@ -71,6 +72,7 @@         , psColumns           = on mplus psColumns           l r         , psMargins           = on (<>)  psMargins           l r         , psWrap              = on mplus psWrap              l r+        , psTabStop           = on mplus psTabStop           l r         , psTheme             = on (<>)  psTheme             l r         , psIncrementalLists  = on mplus psIncrementalLists  l r         , psAutoAdvanceDelay  = on mplus psAutoAdvanceDelay  l r@@ -90,9 +92,9 @@ instance Monoid PresentationSettings where     mappend = (<>)     mempty  = PresentationSettings-                    Nothing Nothing Nothing Nothing Nothing Nothing Nothing-                    Nothing Nothing Nothing Nothing Nothing Nothing Nothing-                    Nothing Nothing+                Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing+                Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing+                Nothing   --------------------------------------------------------------------------------@@ -221,38 +223,52 @@   ---------------------------------------------------------------------------------data EvalSettingsWrap-    = EvalWrapCode-    | EvalWrapRaw-    | EvalWrapRawInline+data EvalSettingsContainer+    = EvalContainerCode+    | EvalContainerNone+    | EvalContainerInline     deriving (Show)   ---------------------------------------------------------------------------------instance A.FromJSON EvalSettingsWrap where-    parseJSON = A.withText "FromJSON EvalSettingsWrap" $ \txt -> case txt of-        "code"      -> pure EvalWrapCode-        "raw"       -> pure EvalWrapRaw-        "rawInline" -> pure EvalWrapRawInline-        _           -> fail $ "unknown wrap: " <> show txt+instance A.FromJSON EvalSettingsContainer where+    parseJSON = A.withText "FromJSON EvalSettingsContainer" $ \t -> case t of+        "code"      -> pure EvalContainerCode+        "none"      -> pure EvalContainerNone+        "inline"    -> pure EvalContainerInline+        -- Deprecated names+        "raw"       -> pure EvalContainerNone+        "rawInline" -> pure EvalContainerInline+        _           -> fail $ "unknown container: " <> show t   -------------------------------------------------------------------------------- data EvalSettings = EvalSettings-    { evalCommand  :: !T.Text-    , evalReplace  :: !Bool-    , evalFragment :: !Bool-    , evalWrap     :: !EvalSettingsWrap+    { evalCommand   :: !T.Text+    , evalReplace   :: !Bool+    , evalFragment  :: !Bool+    , evalContainer :: !EvalSettingsContainer     } deriving (Show)   -------------------------------------------------------------------------------- instance A.FromJSON EvalSettings where     parseJSON = A.withObject "FromJSON EvalSettings" $ \o -> EvalSettings-        <$> o A..: "command"+        <$> o A..:  "command"         <*> o A..:? "replace"  A..!= False         <*> o A..:? "fragment" A..!= True-        <*> o A..:? "wrap"     A..!= EvalWrapCode+        <*> deprecated "wrap" "container" EvalContainerCode o+      where+        deprecated old new def obj = do+            mo <- obj A..:? old+            mn <- obj A..:? new+            case (mo, mn) of+                (Just _, Just _)   -> fail $+                    show old ++ " (deprecated) and " ++ show new ++ " " +++                    "are both specified, please remove " ++ show old+                (Just o, Nothing)  -> pure o+                (Nothing, Just n)  -> pure n+                (Nothing, Nothing) -> pure def   --------------------------------------------------------------------------------
lib/Patat/Transition.hs view
@@ -29,6 +29,7 @@ import           Patat.Presentation.Settings (TransitionSettings (..)) import qualified Patat.Transition.Dissolve   as Dissolve import           Patat.Transition.Internal+import qualified Patat.Transition.Matrix     as Matrix import qualified Patat.Transition.SlideLeft  as SlideLeft import           System.Random               (uniformR) @@ -54,6 +55,7 @@ transitions :: NonEmpty (Text, Transition) transitions =     ("dissolve",  Transition Dissolve.transition) :|+    ("matrix",    Transition Matrix.transition) :     ("slideLeft", Transition SlideLeft.transition) : []  
+ lib/Patat/Transition/Matrix.hs view
@@ -0,0 +1,128 @@+--------------------------------------------------------------------------------+{-# LANGUAGE TemplateHaskell #-}+module Patat.Transition.Matrix+    ( transition+    ) where+++--------------------------------------------------------------------------------+import           Control.Monad             (forM_, guard, when)+import qualified Data.Aeson.Extended       as A+import qualified Data.Aeson.TH.Extended    as A+import           Data.Bifunctor            (first)+import qualified Data.Vector               as V+import qualified Data.Vector.Mutable       as VM+import           Patat.PrettyPrint.Matrix+import           Patat.Size                (Size (..))+import           Patat.Transition.Internal+import           System.Random.Stateful+++--------------------------------------------------------------------------------+data Config = Config+    { cDuration  :: Maybe (A.FlexibleNum Double)+    , cFrameRate :: Maybe (A.FlexibleNum Int)+    }+++--------------------------------------------------------------------------------+data Particle = Particle+    { pX        :: Double+    , pInitialY :: Double+    , pFinalY   :: Double+    , pSpeed    :: Double+    , pCell     :: Cell+    }+++--------------------------------------------------------------------------------+particleY :: Particle -> Double -> Double+particleY p t = pInitialY p * (1 - t') + pFinalY p * t'+  where+    t' = min 1 (pSpeed p * t)+++--------------------------------------------------------------------------------+-- | Maximum speed of a particle, expressed as a factor of the minimum speed of+-- a particle.+particleMaxSpeed :: Double+particleMaxSpeed = 2+++--------------------------------------------------------------------------------+-- | Number of ghosts a particle leaves behind.  Currently hardcoded but could+-- be moved to config.+particleGhosts :: Int+particleGhosts = 3+++--------------------------------------------------------------------------------+transition :: Config -> TransitionGen+transition config (Size rows cols) initial final rgen =+    first frame <$>+    evenlySpacedFrames+        (A.unFlexibleNum <$> cDuration  config)+        (A.unFlexibleNum <$> cFrameRate config)+  where+    speeds :: V.Vector Double+    speeds = runStateGen_ rgen $ \g ->+        V.replicateM (rows * cols) (uniformRM (1, particleMaxSpeed) g)++    up :: V.Vector Bool+    up = runStateGen_ rgen $ \g ->+        V.replicateM (rows * cols) (uniformM g)++    ghosts :: Double -> [Double]+    ghosts baseSpeed =+        [ baseSpeed * (1 + fromIntegral i / fromIntegral particleGhosts)+        | i <- [0 .. particleGhosts]+        ]++    initialParticles :: [Particle]+    initialParticles = do+        (x, y, cell) <- posCells initial+        let idx = y * cols + x+        speed <- ghosts $ speeds V.! idx+        pure Particle+            { pX        = fromIntegral x+            , pInitialY = fromIntegral y+            , pFinalY   = if up V.! idx then 0 else fromIntegral rows+            , pSpeed    = speed+            , pCell     = cell+            }++    finalParticles :: [Particle]+    finalParticles = do+        (x, y, cell) <- posCells final+        let idx = y * cols + x+        speed <- ghosts $ speeds V.! idx+        pure Particle+            { pX        = fromIntegral x+            , pInitialY = if up V.! idx then -1 else fromIntegral rows+            , pFinalY   = fromIntegral y+            , pSpeed    = speed+            , pCell     = cell+            }++    posCells :: Matrix -> [(Int, Int, Cell)]+    posCells mat = do+        y <- [0 .. rows - 1]+        x <- [0 .. cols - 1]+        let cell = mat V.! (y * cols + x)+        guard . not $ cell == emptyCell+        pure (x, y, cell)++    frame :: Double -> Matrix+    frame t = V.create $ do+        mat <- VM.replicate (rows * cols) emptyCell+        forM_ (initialParticles ++ finalParticles) $ \particle ->+            let y = round $ particleY particle t+                x = round $ pX particle+                idx = y * cols + x in+            when (x >= 0 && x < cols && y >= 0 && y < rows) $+                VM.write mat idx $ pCell particle+        pure mat+++--------------------------------------------------------------------------------+$(A.deriveFromJSON A.dropPrefixOptions ''Config)
patat.cabal view
@@ -1,5 +1,5 @@ Name:          patat-Version:       0.11.0.0+Version:       0.12.0.0 Synopsis:      Terminal-based presentations using Pandoc Description:   Terminal-based presentations using Pandoc. License:       GPL-2@@ -97,6 +97,7 @@     Patat.Transition     Patat.Transition.Internal     Patat.Transition.Dissolve+    Patat.Transition.Matrix     Patat.Transition.SlideLeft    Other-modules: