patat 0.10.0.0 → 0.10.1.0
raw patch · 10 files changed
+182/−44 lines, 10 filesdep ~ansi-terminaldep ~ansi-wl-pprintdep ~mtlPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: ansi-terminal, ansi-wl-pprint, mtl, random
API changes (from Hackage documentation)
- Patat.Transition.SlideLeft: slideLeft :: Config -> TransitionGen
+ Patat.Transition: instance Data.Aeson.Types.FromJSON.FromJSON Patat.Transition.RandomTransitionSettings
+ Patat.Transition.Dissolve: instance Data.Aeson.Types.FromJSON.FromJSON Patat.Transition.Dissolve.Config
+ Patat.Transition.Dissolve: transition :: Config -> TransitionGen
+ Patat.Transition.Internal: evenlySpacedFrames :: Maybe Double -> Maybe Int -> NonEmpty (Double, Duration)
+ Patat.Transition.SlideLeft: transition :: Config -> TransitionGen
Files
- CHANGELOG.md +8/−0
- README.md +23/−3
- lib/Data/Aeson/Extended.hs +8/−2
- lib/Patat/Main.hs +1/−2
- lib/Patat/Presentation/Read.hs +3/−7
- lib/Patat/Transition.hs +56/−12
- lib/Patat/Transition/Dissolve.hs +49/−0
- lib/Patat/Transition/Internal.hs +20/−0
- lib/Patat/Transition/SlideLeft.hs +11/−16
- patat.cabal +3/−2
CHANGELOG.md view
@@ -1,5 +1,13 @@ # Changelog +## 0.10.1.0 (2023-10-15)++ * Add dissolve transition effect (#150)++ * Add random transitions (#151)++ Set transition `type` to `random` to randomly sample transition effects+ ## 0.10.0.0 (2023-10-12) * Add transition effects (#149)
README.md view
@@ -803,11 +803,31 @@ Supported transitions `type`s: - `slideLeft`: slides the new slide in from right to left.+ - `dissolve`: changes characters over time. - Arguments:+All transitions currently take these arguments: - - `frameRate`: number of frames per second. Defaults to 24.- - `duration`: duration of the animation in seconds. Defaults to 1.+ - `frameRate`: number of frames per second. Defaults to 24.+ - `duration`: duration of the animation in seconds. Defaults to 1.++#### Random transitions++You can set `type` to `random` to randomly pick a transition effect.++```yaml+patat:+ transition:+ type: random+ items:+ - type: dissolve+ duration: 3+ - type: slideLeft+ frameRate: 10+```++You can optionally set `items` to a non-empty list of transition effects to+randomly sample from. If `items` is not set, `patat` will simply sample from+all transition effects using their respective default settings. Trivia ------
lib/Data/Aeson/Extended.hs view
@@ -3,12 +3,14 @@ ( module Data.Aeson , FlexibleNum (..)++ , resultToEither ) where import Data.Aeson-import qualified Data.Text as T+import qualified Data.Text as T import Prelude-import Text.Read (readMaybe)+import Text.Read (readMaybe) -- | This can be parsed from a JSON string in addition to a JSON number. newtype FlexibleNum a = FlexibleNum {unFlexibleNum :: a}@@ -19,3 +21,7 @@ Nothing -> fail $ "Could not parse " ++ T.unpack str ++ " as a number" Just x -> return (FlexibleNum x) parseJSON val = FlexibleNum <$> parseJSON val++resultToEither :: Result a -> Either String a+resultToEither (Success x) = Right x+resultToEither (Error err) = Left err
lib/Patat/Main.hs view
@@ -13,8 +13,7 @@ import Control.Concurrent.Chan.Extended (Chan) import qualified Control.Concurrent.Chan.Extended as Chan import Control.Exception (bracket)-import Control.Monad (forever, join, unless, void,- when)+import Control.Monad (forever, unless, void, when) import qualified Data.Aeson.Extended as A import Data.Foldable (for_) import Data.Functor (($>))
lib/Patat/Presentation/Read.hs view
@@ -16,7 +16,7 @@ import Control.Monad.Except (ExceptT (..), runExceptT, throwError) import Control.Monad.Trans (liftIO)-import qualified Data.Aeson as A+import qualified Data.Aeson.Extended as A import qualified Data.Aeson.KeyMap as AKM import Data.Bifunctor (first) import Data.Maybe (fromMaybe)@@ -166,13 +166,9 @@ Nothing -> Right mempty Just (Left err) -> Left err Just (Right (A.Object obj)) | Just val <- AKM.lookup "patat" obj ->- resultToEither $! A.fromJSON val+ first (\err -> "Error parsing patat settings from metadata: " ++ err) $!+ A.resultToEither $! A.fromJSON val Just (Right _) -> Right mempty- where- resultToEither :: A.Result a -> Either String a- resultToEither (A.Success x) = Right x- resultToEither (A.Error e) = Left $!- "Error parsing patat settings from metadata: " ++ e --------------------------------------------------------------------------------
lib/Patat/Transition.hs view
@@ -1,5 +1,9 @@ --------------------------------------------------------------------------------+{-# LANGUAGE DeriveFoldable #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE DeriveTraversable #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-} module Patat.Transition ( Duration (..) , threadDelayDuration@@ -13,30 +17,70 @@ ---------------------------------------------------------------------------------import qualified Data.Aeson as A+import qualified Data.Aeson.Extended as A+import qualified Data.Aeson.TH.Extended as A+import Data.Bifunctor (first) import qualified Data.HashMap.Strict as HMS+import Data.List.NonEmpty (NonEmpty (..))+import qualified Data.List.NonEmpty as NonEmpty import Data.Text (Text) import qualified Data.Text as T+import Data.Traversable (for) import Patat.Presentation.Settings (TransitionSettings (..))+import qualified Patat.Transition.Dissolve as Dissolve import Patat.Transition.Internal import qualified Patat.Transition.SlideLeft as SlideLeft+import System.Random (uniformR) ---------------------------------------------------------------------------------transitions :: HMS.HashMap Text Transition-transitions = HMS.fromList- [ ("slideLeft", Transition SlideLeft.slideLeft)- ]+data RandomTransitionSettings = RandomTransitionSettings+ { rtsItems :: Maybe (NonEmpty TransitionSettings)+ } --------------------------------------------------------------------------------+$(A.deriveFromJSON A.dropPrefixOptions ''RandomTransitionSettings)+++--------------------------------------------------------------------------------+random :: NonEmpty TransitionGen -> TransitionGen+random items size matrix0 matrix1 rg0 =+ let (idx, rg1) = uniformR (0, length items - 1) rg0 in+ (items NonEmpty.!! idx) size matrix0 matrix1 rg1+++--------------------------------------------------------------------------------+transitions :: NonEmpty (Text, Transition)+transitions =+ ("dissolve", Transition Dissolve.transition) :|+ ("slideLeft", Transition SlideLeft.transition) : []+++--------------------------------------------------------------------------------+transitionTable :: HMS.HashMap Text Transition+transitionTable = foldMap (uncurry HMS.singleton) transitions+++-------------------------------------------------------------------------------- parseTransitionSettings :: TransitionSettings -> Either String TransitionGen-parseTransitionSettings ts = case HMS.lookup ty transitions of- Nothing -> Left $ "unknown transition type: " ++ show ty- Just (Transition f) -> case A.fromJSON (A.Object $ tsParams ts) of- A.Success conf -> Right $ f conf- A.Error err -> Left $- "could not parse " ++ T.unpack ty ++ " transition: " ++ err+parseTransitionSettings ts+ -- Random is treated specially here.+ | ty == "random" = fmap random $ do+ settings <- A.resultToEither . A.fromJSON . A.Object $ tsParams ts+ case rtsItems settings of+ -- Items specified: parse those+ Just items -> traverse parseTransitionSettings items+ -- No items specified: parse default transition settings.+ Nothing -> for transitions $ \(typ, _) -> parseTransitionSettings+ TransitionSettings {tsType = typ, tsParams = mempty}+ -- Found the transition type.+ | Just (Transition f) <- HMS.lookup ty transitionTable =+ fmap (f $) . first (\err ->+ "could not parse " ++ T.unpack ty ++ " transition: " ++ err) .+ A.resultToEither . A.fromJSON . A.Object $ tsParams ts+ -- Not found, error.+ | otherwise = Left $ "unknown transition type: " ++ show ty where- ty = tsType ts+ ty = tsType ts
+ lib/Patat/Transition/Dissolve.hs view
@@ -0,0 +1,49 @@+--------------------------------------------------------------------------------+{-# LANGUAGE TemplateHaskell #-}+module Patat.Transition.Dissolve+ ( transition+ ) where+++--------------------------------------------------------------------------------+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 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)+ }+++--------------------------------------------------------------------------------+transition :: Config -> TransitionGen+transition config (Size rows cols) initial final rgen =+ first frame <$>+ evenlySpacedFrames+ (A.unFlexibleNum <$> cDuration config)+ (A.unFlexibleNum <$> cFrameRate config)+ where+ -- Generate a random number between 0 and 1 for each position.+ noise :: V.Vector Double+ noise = runStateGen_ rgen $ \g ->+ V.replicateM (rows * cols) (uniformRM (0, 1) g)++ -- Select the initial or final value depending on the noise.+ frame :: Double -> Matrix+ frame t = V.zipWith3+ (\treshold l r -> if t < treshold then l else r)+ noise+ initial+ final+++--------------------------------------------------------------------------------+$(A.deriveFromJSON A.dropPrefixOptions ''Config)
lib/Patat/Transition/Internal.hs view
@@ -10,6 +10,8 @@ , TransitionInstance (..) , newTransition , stepTransition++ , evenlySpacedFrames ) where @@ -17,6 +19,7 @@ import Control.Concurrent (threadDelay) import qualified Data.Aeson as A import Data.List.NonEmpty (NonEmpty ((:|)))+import Data.Maybe (fromMaybe) import Data.Unique (Unique, newUnique) import qualified Patat.PrettyPrint as PP import Patat.PrettyPrint.Matrix@@ -79,3 +82,20 @@ stepTransition _ trans = case tiFrames trans of _ :| [] -> Nothing _ :| f : fs -> Just trans {tiFrames = f :| fs}+++--------------------------------------------------------------------------------+-- | Given an optional duration and frame rate, generate a sequence of evenly+-- spaced frames, represented by a number ranging from [0 .. 1].+evenlySpacedFrames+ :: Maybe Double -> Maybe Int -> NonEmpty (Double, Duration)+evenlySpacedFrames mbDuration mbFrameRate =+ frame 0 :| map frame [1 .. frames - 1]+ where+ duration = fromMaybe 1 mbDuration+ frameRate = fromMaybe 24 mbFrameRate++ frames = round $ duration * fromIntegral frameRate :: Int+ delay = duration / fromIntegral (frames + 1)++ frame idx = (fromIntegral (idx + 1) / fromIntegral frames, Duration delay)
lib/Patat/Transition/SlideLeft.hs view
@@ -1,16 +1,15 @@ -------------------------------------------------------------------------------- {-# LANGUAGE TemplateHaskell #-} module Patat.Transition.SlideLeft- ( slideLeft+ ( transition ) where -------------------------------------------------------------------------------- import qualified Data.Aeson.Extended as A import qualified Data.Aeson.TH.Extended as A+import Data.Bifunctor (first) import Data.Foldable (for_)-import Data.List.NonEmpty (NonEmpty ((:|)))-import Data.Maybe (fromMaybe) import qualified Data.Vector as V import qualified Data.Vector.Mutable as VM import Patat.PrettyPrint.Matrix@@ -26,19 +25,15 @@ ---------------------------------------------------------------------------------slideLeft :: Config -> TransitionGen-slideLeft config (Size rows cols) initial final _rgen =- fmap (\f -> (f, Duration delay)) $- frame 0 :| map frame [1 .. frames - 1]+transition :: Config -> TransitionGen+transition config (Size rows cols) initial final _rgen =+ first frame <$>+ evenlySpacedFrames+ (A.unFlexibleNum <$> cDuration config)+ (A.unFlexibleNum <$> cFrameRate config) where- duration = fromMaybe 1 $ A.unFlexibleNum <$> cDuration config- frameRate = fromMaybe 24 $ A.unFlexibleNum <$> cFrameRate config-- frames = round $ duration * fromIntegral frameRate :: Int- delay = duration / fromIntegral (frames + 1)-- frame :: Int -> Matrix- frame idx = V.create $ do+ frame :: Double -> Matrix+ frame t = V.create $ do ini <- V.unsafeThaw initial fin <- V.unsafeThaw final mat <- VM.replicate (rows * cols) emptyCell@@ -52,7 +47,7 @@ pure mat where offset = max 0 . min cols . (round :: Double -> Int) $- fromIntegral (idx + 1) / fromIntegral frames * fromIntegral cols+ t * fromIntegral cols --------------------------------------------------------------------------------
patat.cabal view
@@ -1,5 +1,5 @@ Name: patat-Version: 0.10.0.0+Version: 0.10.1.0 Synopsis: Terminal-based presentations using Pandoc Description: Terminal-based presentations using Pandoc. License: GPL-2@@ -47,7 +47,7 @@ pandoc >= 3.1 && < 3.2, pandoc-types >= 1.23 && < 1.24, process >= 1.6 && < 1.7,- random >= 1.1 && < 1.3,+ random >= 1.2 && < 1.3, skylighting >= 0.10 && < 0.15, terminal-size >= 0.3 && < 0.4, text >= 1.2 && < 2.1,@@ -95,6 +95,7 @@ Patat.Theme Patat.Transition Patat.Transition.Internal+ Patat.Transition.Dissolve Patat.Transition.SlideLeft Other-modules: