varying 0.1.5.0 → 0.2.0.0
raw patch · 10 files changed
+310/−525 lines, 10 files
Files
- README.md +44/−52
- changelog.md +1/−0
- src/Control/Varying.hs +11/−15
- src/Control/Varying/Core.hs +26/−28
- src/Control/Varying/Event.hs +38/−231
- src/Control/Varying/Spline.hs +106/−49
- src/Control/Varying/Time.hs +9/−9
- src/Control/Varying/Tween.hs +31/−91
- src/Example.hs +40/−46
- varying.cabal +4/−4
README.md view
@@ -2,84 +2,76 @@ [](http://hackage.haskell.org/package/varying) [](https://travis-ci.org/schell/varying) -This library provides automaton based varying values useful for both functional+This library provides automaton based value streams useful for both functional reactive programming (FRP) and locally stateful programming (LSP). It is influenced by the [netwire](http://hackage.haskell.org/package/netwire) and [auto](http://hackage.haskell.org/package/auto) packages. Unlike netwire the concepts of inhibition and time are explicit (through `Control.Varying.Event` -and `Control.Varying.Time`) and the library aims at being minimal and well +and `Control.Varying.Time`). The library aims at being minimal and well documented with a small API. -Depending on your types and values varying can provide discrete or continuous-time semantics.- ## Getting started ```haskell module Main where import Control.Varying-import Control.Varying.Time as Time -- time is not auto-exported+import Control.Applicative import Text.Printf -- | A simple 2d point type.-data Point = Point { x :: Float- , y :: Float+data Point = Point { px :: Float+ , py :: Float } deriving (Show, Eq) --- | Our Point value that varies over time continuously in x and y.-backAndForth :: Var IO a Point-backAndForth =- -- Here we use Applicative to construct a varying Point that takes time- -- as an input.- (Point <$> tweenx <*> tweeny)- -- Here we feed the varying Point a time signal using the 'plug left'- -- function. We could similarly use the 'plug right' (~>) function- -- and put the time signal before the Point. This is needed because the- -- tweens take time as an input.- <~ time---- An exponential tween back and forth from 0 to 100 over 2 seconds.-tweenx :: Monad m => Var m Float Float-tweenx =- -- Tweens only happen for a certain duration and so their sample- -- values have the type (Ord t, Fractional t => Event t). After construction- -- a tween's full type will be- -- (Ord t, Fractional t, Monad m) => Var m t (Event t).- tween easeOutExpo 0 100 1- -- We can chain another tween back to the starting position using- -- `andThenE`, which will sample the first tween until it ends and then- -- switch to sampling the next tween.- `andThenE`- -- Tween back to the starting position.- tween easeOutExpo 100 0 1- -- At this point our resulting sample values will still have the- -- type (Event Float). The tween as a whole will be an event- -- stream. The tween also only runs back and forth once. We'd- -- like the tween to loop forever so that our point cycles back- -- and forth between 0 and 100 indefinitely.- -- We can accomplish this with recursion and the `andThen`- -- combinator, which samples an event stream until it- -- inhibits and then switches to a normal value stream (a- -- varying value). Put succinctly, it disolves our events into- -- values.- `andThen` tweenx+-- An exponential tween back and forth from 0 to 100 over 2 seconds that+-- loops forever. This spline takes float values of delta time as input,+-- outputs the current x value at every step and would result in () if it+-- terminated.+tweenx :: (Applicative m, Monad m) => Spline Float Float m ()+tweenx = do+ -- Tween from 0 to 100 over 1 second+ x <- tween easeOutExpo 0 100 1+ -- Chain another tween back to the starting position+ _ <- tween easeOutExpo x 0 1+ -- Loop forever+ tweenx --- A quadratic tween back and forth from 0 to 100 over 2 seconds.-tweeny :: Monad m => Var m Float Float-tweeny =- tween easeOutQuad 0 100 1 `andThenE` tween easeOutQuad 100 0 1 `andThen` tweeny+-- A quadratic tween back and forth from 0 to 100 over 2 seconds that never+-- ends.+tweeny :: (Applicative m, Monad m) => Spline Float Float m ()+tweeny = do+ y <- tween easeOutQuad 0 100 1+ _ <- tween easeOutQuad y 0 1+ tweeny --- Our time signal.+-- Our time signal that provides delta time samples. time :: Var IO a Float time = deltaUTC +-- | Our Point value that varies over time continuously in x and y.+backAndForth :: Var IO a Point+backAndForth =+ -- Turn our splines back into continuous value streams. We must provide+ -- a starting value since splines are not guaranteed to be defined at+ -- their edges.+ let x = execSpline 0 tweenx+ y = execSpline 0 tweeny+ in+ -- Construct a varying Point that takes time as an input.+ (Point <$> x <*> y)+ -- Stream in a time signal using the 'plug left' combinator.+ -- We could similarly use the 'plug right' (~>) function+ -- and put the time signal before the construction above. This is needed+ -- because the tween streams take time as an input.+ <~ time+ main :: IO () main = do- putStrLn "Varying Values"+ putStrLn "Varying Example" loop backAndForth where loop :: Var IO () Point -> IO () loop v = do (point, vNext) <- runVar v ()- printf "\nPoint %03.1f %03.1f" (x point) (y point)+ printf "\nPoint %03.1f %03.1f" (px point) (py point) loop vNext ```
changelog.md view
@@ -2,3 +2,4 @@ ========== 0.1.5.0 - added Control.Varying.Spline+0.2.0.0 - reordered spline type variables for MonadTrans
src/Control/Varying.hs view
@@ -4,38 +4,34 @@ -- License: MIT -- Maintainer: Schell Scivally <schell.scivally@synapsegroup.com> ----- The simplest, squishiest FRP library around.--- -- [@Core@]--- Get started writing varying values (also called streams) using the pure--- constructor 'var', the monadic constructor 'varM' or the raw constructor--- 'Var'+-- Get started writing value streams using the pure constructor 'var', the+-- monadic constructor 'varM' or the raw constructor 'Var' -- -- [@Event@] -- Write event streams using the many event emitters and combinators. --+-- [@Spline@]+-- Use do-notation to sequence event streams to form complex behavior.+-- -- [@Tween@] -- Tween numerical values over time using interpolation functions and the -- "quick 'n dirty" time generators in 'Control.Varying.Time'. -- -- [@Time@]--- The 'Control.Varying.Time' module is not reexported because some of the--- functions collide with those in 'Event' - namely 'before' and 'after'.--- I think this is okay because in my experience most modules will either--- deal with events based on user input or events based on time, an in--- rare cases both - but in that case the majority of streams will be of one--- type making the choice of which module to import qualified an easy one.--- The time generator 'Control.Varying.Time.deltaUTC' in 'Control.Varying.Time'--- is practical and based on 'Data.Time.Clock.getCurrentTime'. It's meant--- to be simple, not optimal.+-- Create time streams and temporal event streams. -- module Control.Varying ( -- * Reexports module Control.Varying.Core, module Control.Varying.Event,- module Control.Varying.Tween+ module Control.Varying.Spline,+ module Control.Varying.Time,+ module Control.Varying.Tween, ) where import Control.Varying.Core import Control.Varying.Event import Control.Varying.Tween+import Control.Varying.Time+import Control.Varying.Spline
src/Control/Varying/Core.hs view
@@ -4,28 +4,28 @@ -- License: MIT -- Maintainer: Schell Scivally <schell.scivally@synapsegroup.com> ----- Values that change over a given domain.+-- Value streams represent values that change over a given domain. ----- Varying values take some input (the domain ~ time, place, etc) and produce--- a sample and a new varying value. This pattern is known as an automaton.--- `varying` uses this pattern as its base type with the additon of a monadic--- computation to create locally stateful signals that change over some--- domain.+-- A stream takes some input (the domain e.g. time, place, etc) and when+-- sampled using 'runVar' - produces a value and a new value stream. This+-- pattern is known as an automaton. `varying` uses this pattern as its base+-- type with the additon of a monadic computation to create locally stateful+-- signals that change over some domain. module Control.Varying.Core ( Var(..),- -- * Creating varying values+ -- * Creating value streams -- $creation var, varM, mkState,- -- * Composing varying values+ -- * Composing value streams -- $composition (<~), (~>), -- * Adjusting and accumulating delay, accumulate,- -- * Sampling varying values (running, entry points)+ -- * Sampling value streams (running and other entry points) -- $running evalVar, execVar,@@ -33,7 +33,7 @@ loopVar_, whileVar, whileVar_,- -- * Testing varying values+ -- * Testing value streams testVar, testVar_, testWhile_,@@ -51,7 +51,7 @@ import Debug.Trace -------------------------------------------------------------------------------- -- $creation--- You can create a pure varying value by lifting a function @(a -> b)@+-- You can create a pure value stream by lifting a function @(a -> b)@ -- with 'var': -- -- @@@ -59,9 +59,9 @@ -- addsOne = var (+1) -- @ ----- 'var' is also equivalent to 'arr'.+-- 'var' is equivalent to 'arr'. ----- You can create a monadic varying value by lifting a monadic computation+-- You can create a monadic value stream by lifting a monadic computation -- @(a -> m b)@ using 'varM': -- -- @@@ -71,7 +71,7 @@ -- -- You can create either with the raw constructor. You can also create your -- own combinators using the raw constructor, as it allows you full control--- over how varying values are stepped and sampled:+-- over how value streams are stepped and sampled: -- -- @ -- delay :: Monad m => b -> Var m a b -> Var m a b@@ -101,17 +101,15 @@ return (b', mkState f s') -------------------------------------------------------------------------------- -- $running--- The easiest way to sample a 'Var' is to run it in the desired monad with--- 'runVar'. This will give you a sample value and a new 'Var' bundled up in a--- tuple:+-- The easiest way to sample a stream is to run it in the desired monad with+-- 'runVar'. This will produce a sample value and a new stream. -- -- > do (sample, v') <- runVar v inputValue -- -- Much like Control.Monad.State there are other entry points for running--- varying values like 'evalVar', 'execVar'. There are also extra control--- structures like 'loopVar' and 'whileVar' and more.+-- value streams like 'evalVar', 'execVar'. There are also extra control+-- structures such as 'loopVar' and 'whileVar'. --------------------------------------------------------------------------------- -- | Iterate a 'Var' once and return the sample value. evalVar :: Functor m => Var m a b -> a -> m b evalVar v a = fst <$> runVar v a@@ -193,8 +191,8 @@ let b' = f b a return (b', accumulate f b') --- | Delays the given 'Var' by one sample using a parameter as the first--- sample. This enables the programmer to create 'Var's that depend on+-- | Delays the given stream by one sample using the argument as the first+-- sample. This enables the programmer to create streams that depend on -- themselves for values. For example: -- -- > let v = 1 + delay 0 v in testVar_ v@@ -204,11 +202,11 @@ return (b', go a' v'') -------------------------------------------------------------------------------- -- $composition--- You can compose varying values together using '~>' and '<~'. The "right plug"--- ('~>') takes the output from a varying value on the left and "plugs" it--- into the input of the varying value on the right. The "left plug" does--- the same thing only in the opposite direction. This allows you to write--- varying values that read naturally.+-- You can compose value streams together using '~>' and '<~'. The "right plug"+-- ('~>') takes the output from a value stream on the left and "plugs" it+-- into the input of the value stream on the right. The "left plug" does+-- the same thing in the opposite direction. This allows you to write value+-- streams that read naturally. -------------------------------------------------------------------------------- -- | Same as '~>' with flipped parameters. (<~) :: Monad m => Var m b c -> Var m a b -> Var m a c@@ -316,7 +314,7 @@ -------------------------------------------------------------------------------- -- Core datatypes ----------------------------------------------------------------------------------- | The vessel of a varying value. A 'Var' is a structure that contains a value+-- | The vessel of a value stream. A 'Var' is a structure that contains a value -- that changes over some input. That input could be time (Float, Double, etc) -- or 'Control.Varying.Event.Event's or 'Char' - whatever. -- It's a kind of Mealy machine (an automaton) with effects.
src/Control/Varying/Event.hs view
@@ -4,61 +4,42 @@ -- License: MIT -- Maintainer: Schell Scivally <schell.scivally@synapsegroup.com> ----- 'Event' streams describe things that happen at a specific time or place--- or value in general. For example, you can think of the event stream--- @Var IO Double (Event ())@ as an occurrence of `()` at a specific time--- (`Double`).------ You can use 'Event' just like you would 'Maybe'.+-- 'Event' streams describe things that happen at a specific domain.+-- For example, you can think of the event stream+-- @Var IO Double (Event ())@ as an occurrence of () at a specific input+-- of type 'Double'. --+-- For sequencing streams please check out 'Control.Varying.Spline' which+-- lets you chain together sequences of event streams using do-notation. module Control.Varying.Event ( Event(..), -- * Transforming event values. toMaybe, isEvent,- -- * Combining event streams and value streams- latchWith,+ -- * Combining event and value streams orE,- tagOn,- tagM,- --ringM,- -- * Generating events from values+ -- * Generating events from value streams use, onTrue, onJust, onUnique, onWhen,- toEvent,- -- * Using event streams+ -- * Folding and gathering event streams foldStream,- collect,- collectWith,- hold,- holdWith,- startingWith,- startWith,- -- * Temporal operations (time - related)- between,- after,- beforeWith,- beforeOne,- before,+ startingWith, startWith,+ -- * List-like operations on event streams filterE, takeE, dropE,+ -- * Primitive event streams once, always, never,- -- * Switching and chaining events- andThen,- andThenWith,- andThenE,+ -- * Switching switchByMode,+ -- * Bubbling onlyWhen, onlyWhenE,- -- * Combining event streams- combineWith,- combine ) where import Prelude hiding (until)@@ -67,7 +48,7 @@ import Control.Monad import Data.Monoid ----------------------------------------------------------------------------------- Transforming event values into usable values.+-- Transforming event values into usable values -------------------------------------------------------------------------------- -- | Turns an 'Event' into a 'Maybe'. toMaybe :: Event a -> Maybe a@@ -80,24 +61,8 @@ isEvent (Event _) = True isEvent _ = False ----------------------------------------------------------------------------------- Combining varying values and events+-- Combining value streams and events ----------------------------------------------------------------------------------- | Holds the last value of one event stream while waiting for another event--- stream to produce a value. Once both streams have produced a value, combine--- the two using the given combine function and emit an event with the--- value.-latchWith :: (Applicative m, Monad m)- => (b -> c -> d) -> Var m a (Event b) -> Var m a (Event c)- -> Var m a (Event d)-latchWith f vb = latchWith' (NoEvent, vb)- where latchWith' (eb, vb') vc =- Var $ \a -> do (eb', vb'') <- runVar vb' a- (ec', vc') <- runVar vc a- let eb'' = eb' <|> eb- return ( f <$> eb'' <*> ec'- , latchWith' (eb'', vb'') vc'- )- -- | Produces values from the first unless the second produces event -- values and if so, produces the values of those events. orE :: (Applicative m, Monad m) => Var m a b -> Var m a (Event b) -> Var m a b@@ -107,37 +72,6 @@ return $ case e of NoEvent -> (b, orE y' ye') Event b' -> (b', orE y' ye')---- | Injects the values of the `vb` into the events of `ve`.-tagOn :: (Applicative m, Monad m)- => Var m a b -> Var m a (Event c) -> Var m a (Event b)-tagOn vb ve = (<$) <$> vb <*> ve---- | Injects a monadic computation into an event stream, using the event--- values of type `b` as a parameter to produce an event stream of type--- `c`. After the first time an event is generated the result of the--- previous event is used in a clean up function.------ This is like `tagM` but performs a cleanup function first.---ringM :: (Applicative m, Monad m)--- => (c -> m ()) -> (b -> m c) -> Var m a (Event b) -> Var m a (Event c)---ringM cln = (go (const $ return ()) .) . tagM--- where go f ve = Var $ \a -> do (ec, ve') <- runVar ve a--- case ec of--- NoEvent -> return (ec, go f ve')--- Event c -> do f c--- return (ec, go cln ve')---- | Injects a monadic computation into the events of `vb`, providing a way--- to perform side-effects inside an `Event` inside a `Var`.-tagM :: (Applicative m, Monad m)- => (b -> m c) -> Var m a (Event b) -> Var m a (Event c)-tagM f vb = Var $ \a -> do- (eb, vb') <- runVar vb a- case eb of- Event b -> do c <- f b- return (Event c, tagM f vb')- NoEvent -> return (NoEvent, tagM f vb') -------------------------------------------------------------------------------- -- Generating events from values --------------------------------------------------------------------------------@@ -171,23 +105,9 @@ -- | Triggers an `Event a` when the condition is met. onWhen :: Applicative m => (a -> Bool) -> Var m a (Event a) onWhen f = var $ \a -> if f a then Event a else NoEvent---- | Wraps all produced values of the given var with events.-toEvent :: (Applicative m, Monad m) => Var m a b -> Var m a (Event b)-toEvent = (~> var Event) ----------------------------------------------------------------------------------- Using event values+-- Collecting ----------------------------------------------------------------------------------- | Collect all produced values into a monoidal structure using the given--- insert function.-collectWith :: (Monoid b, Applicative m, Monad m)- => (a -> b -> b) -> Var m (Event a) b-collectWith f = Var $ \a -> collect' mempty a- where collect' b e = let b' = case e of- NoEvent -> b- Event a' -> f a' b- in return (b', Var $ \a' -> collect' b' a')- -- | Like a left fold over all the stream's produced values. foldStream :: Monad m => (a -> t -> a) -> a -> Var m (Event t) a foldStream f acc = Var $ \e ->@@ -196,95 +116,15 @@ in return (acc', foldStream f acc') NoEvent -> return (acc, foldStream f acc) --- | Collect all produced values into a list. The latest event value will--- be at the head of the list.-collect :: (Applicative m, Monad m) => Var m (Event a) [a]-collect = collectWith (:)- -- | Produces the given value until the input events produce a value, then -- produce that value until a new input event produces. This always holds -- the last produced value, starting with the given value. -- @ -- time ~> after 3 ~> startingWith 0 -- @--- This is similar to 'hold' except that it takes events from its input value--- instead of another 'Var'. startingWith, startWith :: (Applicative m, Monad m) => a -> Var m (Event a) a startingWith = startWith-startWith a = Var $ \e ->- return $ case e of- NoEvent -> (a, startWith a)- Event a' -> (a', startWith a')---- | Flipped version of 'hold'.-holdWith :: (Applicative m, Monad m) => b -> Var m a (Event b) -> Var m a b-holdWith = flip hold---- | Produces the 'initial' value until the given 'Var' produces an event.--- After an event is produced that event's value will be produced until the--- next event produced by the given 'Var'.-hold :: (Applicative m, Monad m) => Var m a (Event b) -> b -> Var m a b-hold w initial = Var $ \x -> do- (mb, w') <- runVar w x- return $ case mb of- NoEvent -> (initial, hold w' initial)- Event e -> (e, hold w' e)---- | Produce events after the first until the second. After a successful--- cycle it will start over.-between :: (Applicative m, Monad m)- => Var m a (Event b) -> Var m a (Event c) -> Var m a (Event ())-between vb vc = (never `before` vb) `andThenE` (toEvent vu `before` vc) `andThen` between vb vc- where vu = pure ()---- | Produce events with the initial value only after the input stream has--- produced one event.-after :: (Applicative m, Monad m)- => Var m a b -> Var m a (Event c) -> Var m a (Event b)-after vb ve = Var $ \a -> do- (_, vb') <- runVar vb a- (e, ve') <- runVar ve a- case e of- Event _ -> return (NoEvent, toEvent vb')- NoEvent -> return (NoEvent, vb' `after` ve')---- | Like before, but use the value produced by the switching stream to--- create a stream to switch to.-beforeWith :: (Applicative m, Monad m)- => Var m a b- -> (Var m a (Event b), b -> Var m a (Event b))- -> Var m a (Event b)-beforeWith vb (ve, f) = Var $ \a -> do- (b, vb') <- runVar vb a- (e, ve') <- runVar ve a- case e of- Event b' -> runVar (f b') a- NoEvent -> return (Event b, beforeWith vb' (ve', f))---- | Like before, but sample the value of the second stream once before--- inhibiting.-beforeOne :: (Applicative m, Monad m) => Var m a b -> Var m a (Event b) -> Var m a (Event b)-beforeOne vb ve = Var $ \a -> do- (b, vb') <- runVar vb a- (e, ve') <- runVar ve a- case e of- Event b' -> return (Event b', never)- NoEvent -> return (Event b, vb' `beforeOne` ve')---- | Produce events of the initial varying value until the given event stream--- produces its first event, then inhibit forever.-before :: (Applicative m, Monad m)- => Var m a b -> Var m a (Event c) -> Var m a (Event b)-before vb ve = Var $ \a -> do- (b, vb') <- runVar vb a- (e, ve') <- runVar ve a- case e of- Event _ -> return (NoEvent, never)- NoEvent -> return (Event b, vb' `before` ve')---- | Produce the given value once and then inhibit forever.-once :: (Applicative m, Monad m) => b -> Var m a (Event b)-once b = Var $ \_ -> return (Event b, never)+startWith = foldStream (\_ a -> a) -- | Stream through some number of successful events and then inhibit forever. takeE :: (Applicative m, Monad m)@@ -312,6 +152,12 @@ filterE p v = v ~> var check where check (Event b) = if p b then Event b else NoEvent check _ = NoEvent+--------------------------------------------------------------------------------+-- Primitive event streams+--------------------------------------------------------------------------------+-- | Produce the given value once and then inhibit forever.+once :: (Applicative m, Monad m) => b -> Var m a (Event b)+once b = Var $ \_ -> return (Event b, never) -- | Never produces any event values. never :: (Applicative m, Monad m) => Var m b (Event c)@@ -321,36 +167,9 @@ always :: (Applicative m, Monad m) => b -> Var m a (Event b) always = pure . Event ----------------------------------------------------------------------------------- Switching on events+-- Switching ----------------------------------------------------------------------------------- | Produces the first 'Var's Event values until that stops producing, then--- switches to the second 'Var'.-andThen :: (Applicative m, Monad m) => Var m a (Event b) -> Var m a b -> Var m a b-andThen w1 w2 = w1 `andThenWith` const w2---- | Switches from one event stream to another once the first stops--- producing.-andThenE :: (Applicative m, Monad m)- => Var m a (Event b) -> Var m a (Event b) -> Var m a (Event b)-andThenE y1 y2 = Var $ \a -> do- (e, y1') <- runVar y1 a- case e of- NoEvent -> runVar y2 a- Event b -> return (Event b, y1' `andThenE` y2)---- | Switches from one event stream when that stream stops producing. A new--- stream is created using the last produced value (or `Nothing`) and used--- as the second stream.-andThenWith :: (Applicative m, Monad m)- => Var m a (Event b) -> (Maybe b -> Var m a b) -> Var m a b-andThenWith = go Nothing- where go mb w1 f = Var $ \a -> do- (e, w1') <- runVar w1 a- case e of- NoEvent -> runVar (f mb) a- Event b -> return (b, go (Just b) w1' f)---- | Switches using a mode signal. Signals maintain state for the duration+-- | Switches using a mode signal. Streams maintain state only for the duration -- of the mode. switchByMode :: (Applicative m, Monad m, Eq b) => Var m a b -> (b -> Var m a c) -> Var m a c@@ -365,22 +184,24 @@ where vOf eb = case eb of NoEvent -> v Event b -> f b---- | Produce events of a varying value 'v' only when its input value passes a+--------------------------------------------------------------------------------+-- Bubbling+--------------------------------------------------------------------------------+-- | Produce events of a value stream 'v' only when its input value passes a -- predicate 'f'. -- 'v' maintains state while cold. onlyWhen :: (Applicative m, Monad m)- => Var m a b -- ^ 'v' - The varying value+ => Var m a b -- ^ 'v' - The value stream -> (a -> Bool) -- ^ 'f' - The predicate to run on 'v''s input values. -> Var m a (Event b) onlyWhen v f = v `onlyWhenE` hot where hot = var id ~> onWhen f --- | Produce events of a varying value 'v' only when an event stream 'h'+-- | Produce events of a value stream 'v' only when an event stream 'h' -- produces an event. -- 'v' and 'h' maintain state while cold. onlyWhenE :: (Applicative m, Monad m)- => Var m a b -- ^ 'v' - The varying value+ => Var m a b -- ^ 'v' - The value stream -> Var m a (Event c) -- ^ 'h' - The event stream -> Var m a (Event b) onlyWhenE v hot = Var $ \a -> do@@ -390,22 +211,7 @@ return (Event b, onlyWhenE v' hot') else return (NoEvent, onlyWhenE v hot') ----------------------------------------------------------------------------------- Combining event streams------------------------------------------------------------------------------------ | Combine two events streams into one event stream. Like `combine` but--- uses a combining function instead of (,).-combineWith :: (Applicative m, Monad m)- => (b -> c -> d) -> Var m a (Event b) -> Var m a (Event c)- -> Var m a (Event d)-combineWith f vb vc = (uncurry f <$>) <$> combine vb vc---- | Combine two event streams into an event stream of tuples. A tuple is--- only produced when both event streams produce a value.-combine :: (Applicative m, Monad m)- => Var m a (Event b) -> Var m a (Event c) -> Var m a (Event (b,c))-combine vb vc = (\eb ec -> (,) <$> eb <*> ec) <$> vb <*> vc------------------------------------------------------------------------------------ Operations on Events+-- Event typeclass instances -------------------------------------------------------------------------------- instance Show a => Show (Event a) where show (Event a) = "Event " ++ show a@@ -431,7 +237,9 @@ signum = fmap signum fromInteger = pure . fromInteger -instance MonadPlus Event+instance MonadPlus Event where+ mzero = mempty+ mplus = (<|>) instance Monad Event where return = Event@@ -460,8 +268,7 @@ fmap f (Event a) = Event $ f a fmap _ NoEvent = NoEvent --- | For all intents and purposes you can think of an Event as a Maybe.--- A value of @Event ()@ means that an event has occurred and that the+-- | A value of @Event ()@ means that an event has occurred and that the -- result is a @()@. A value of @NoEvent@ means that an event did not -- occur. --
src/Control/Varying/Spline.hs view
@@ -4,23 +4,24 @@ -- License: MIT -- Maintainer: Schell Scivally <schell.scivally@synapsegroup.com> ----- Using splines we can easily create continuously varying values from+-- Using splines we can easily create continuous value streams from -- multiple piecewise event streams. A spline is a monadic layer on top of -- event streams which are only continuous over a certain domain. The idea -- is that we use do notation to "run an event stream" from which we will--- consume produced values. Once the event stream inhibits the do-notation--- computation completes and returns a result value. That result value is then--- used to determine the next spline in the sequence. This allows us to build--- up long, complex behaviors sequentially using a very familiar notation--- that can be easily turned into a continuously varying value.-+-- consume produced values. Once the event stream inhibits the computation+-- completes and returns a result value. That result value is then+-- used to determine the next spline in the sequence.+--+-- A spline can be converted back into a value stream using 'execSpline' or+-- 'execSplineT'. This allows us to build long, complex, sequential behaviors+-- using familiar notation.+-- {-# LANGUAGE GADTs #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TupleSections #-} module Control.Varying.Spline ( -- * Spline Spline,- runSpline, execSpline, spline, -- * Spline Transformer@@ -28,8 +29,12 @@ runSplineT, evalSplineT, execSplineT,- varyUntilEvent,+ -- * Special operations.+ untilEvent,+ race,+ mix, capture,+ mapOutput, -- * Step Step(..), ) where@@ -37,6 +42,8 @@ import Control.Varying.Core import Control.Varying.Event import Control.Monad.IO.Class+import Control.Monad.Trans.Class+import Control.Monad import Control.Applicative import Data.Monoid @@ -54,6 +61,10 @@ stepResult :: Step f b -> Event b stepResult (Step _ b) = b +toIter :: (Functor f, Monoid (f b))+ => (f a -> f b) -> Step (f a) c -> Step (f b) c+toIter f (Step a b) = Step (f a) b+ -- | A discrete step is a functor by applying a function to the contained -- event's value. instance Functor (Step f) where@@ -80,24 +91,24 @@ -- Much like the State monad it has an "internal state" and an eventual -- return value, where the internal state is the output value. The result -- value is used only in determining the next spline to sequence.-data SplineT m f a b c = SplineT { unSplineT :: Var m a (Step (f b) c) }+data SplineT f a b m c = SplineT { unSplineT :: Var m a (Step (f b) c) } | SplineTConst c --- | Unwrap a spline into a varying value.+-- | Unwrap a spline into a value stream. runSplineT :: (Applicative m, Monad m, Monoid (f b))- => SplineT m f a b c -> Var m a (Step (f b) c)+ => SplineT f a b m c -> Var m a (Step (f b) c) runSplineT (SplineT v) = v runSplineT (SplineTConst x) = pure $ pure x -- | 'Spline' is a specialized 'SplineT' that uses Event as its output -- container. This means that new values overwrite/replace old values due to -- Event's 'Last'-like monoid instance.-type Spline m a b c = SplineT m Event a b c+type Spline a b m c = SplineT Event a b m c -- | A spline is a functor by applying the function to the result.-instance (Applicative m, Monad m) => Functor (SplineT m f a b) where- fmap f (SplineT v) = SplineT $ fmap (fmap f) v+instance (Applicative m, Monad m) => Functor (SplineT f a b m) where fmap f (SplineTConst c) = SplineTConst $ f c+ fmap f (SplineT v) = SplineT $ fmap (fmap f) v -- | A spline is an applicative if its output type is a monoid. It -- responds to 'pure' by returning a spline that immediately returns the@@ -105,7 +116,7 @@ -- value (the function) to the right arguments eventual value. The -- output values will me combined with 'mappend'. instance (Monoid (f b), Applicative m, Monad m)- => Applicative (SplineT m f a b) where+ => Applicative (SplineT f a b m) where pure = SplineTConst (SplineTConst f) <*> (SplineTConst x) = SplineTConst $ f x (SplineT vf) <*> (SplineTConst x) = SplineT $ fmap (fmap ($ x)) vf@@ -115,7 +126,8 @@ -- | A spline is monad if its output type is a monoid. A spline responds -- to bind by running until it produces an eventual value, then uses that -- value to run the next spline.-instance (Monoid (f b), Applicative m, Monad m) => Monad (SplineT m f a b) where+instance (Monoid (f b), Applicative m, Monad m) => Monad (SplineT f a b m) where+ return = pure (SplineTConst x) >>= f = f x (SplineT v) >>= f = SplineT $ Var $ \i -> do (Step b e, v') <- runVar v i@@ -123,25 +135,28 @@ NoEvent -> return (Step b NoEvent, runSplineT $ SplineT v' >>= f) Event x -> runVar (runSplineT $ f x) i +-- | A spline is a transformer and other monadic computations can be lifted+-- int a spline.+instance Monoid (f b) => MonadTrans (SplineT f a b) where+ lift f = SplineT $ varM $ const $ liftM (Step mempty . Event) f+ -- | A spline can do IO if its underlying monad has a MonadIO instance. It -- takes the result of the IO action as its immediate return value and -- uses 'mempty' to generate an empty output value. instance (Monoid (f b), Functor m, Applicative m, MonadIO m)- => MonadIO (SplineT m f a b) where- liftIO f = SplineT $ Var $ \_ -> do- n <- (Step mempty . Event) <$> liftIO f- return (n, pure n)+ => MonadIO (SplineT f a b m) where+ liftIO = lift . liftIO --- | Evaluates a spline to a varying value of its output type.+-- | Evaluates a spline to a value stream of its output type. execSplineT :: (Applicative m, Monad m, Monoid (f b))- => SplineT m f a b c -> Var m a (f b)+ => SplineT f a b m c -> Var m a (f b) execSplineT = (stepIter <$>) . runSplineT -- | Evaluates a spline to an event stream of its result. The resulting--- varying value inhibits until the spline's domain is complete and then it+-- value stream inhibits until the spline's domain is complete and then it -- produces events of the result type. evalSplineT :: (Applicative m, Monad m, Monoid (f b))- => SplineT m f a b c -> Var m a (Event c)+ => SplineT f a b m c -> Var m a (Event c) evalSplineT = (stepResult <$>) . runSplineT -- | Create a spline using an event stream. The spline will run until the@@ -149,47 +164,83 @@ -- output value. In the case the stream inhibits before producing -- a value the default value is used. The spline's result value is the last -- output value.-spline :: (Applicative m, Monad m) => b -> Var m a (Event b) -> Spline m a b b+spline :: (Applicative m, Monad m) => b -> Var m a (Event b) -> Spline a b m b spline x ve = SplineT $ Var $ \a -> do (ex, ve') <- runVar ve a case ex of NoEvent -> let n = Step (Event x) (Event x) in return (n, pure n) Event x' -> return (Step (Event x') NoEvent, runSplineT $ spline x' ve') --- | Unwrap a spline into a varying value. This is an alias of--- 'runSplineT'.-runSpline :: (Applicative m, Monad m) => Spline m a b c -> Var m a (Step (Event b) c)-runSpline = runSplineT---- | Using a default start value, evaluate the spline to a varying value.+-- | Using a default start value, evaluate the spline to a value stream. -- A spline is only defined over a finite domain so we must supply a default -- value to use before the spline produces its first output value.-execSpline :: (Applicative m, Monad m) => b -> Spline m a b c -> Var m a b+execSpline :: (Applicative m, Monad m) => b -> Spline a b m c -> Var m a b execSpline x (SplineTConst _) = pure x execSpline x s = execSplineT s ~> foldStream (\_ y -> y) x --- | Create a spline from a varying value and an event stream. The spline--- uses the varying value as its output value. The spline will run until+-- | Create a spline from a value stream and an event stream. The spline+-- uses the value stream as its output value. The spline will run until -- the event stream produces a value, at that point the last output--- value and the event value are used in a merge function to produce the--- spline's result value.-varyUntilEvent :: (Applicative m, Monad m)- => Var m a b -> Var m a (Event c) -> (b -> c -> d)- -> Spline m a b d-varyUntilEvent v ve f = SplineT $ Var $ \a -> do- (b, v') <- runVar v a- (ec, ve') <- runVar ve a- case ec of- NoEvent -> return (Step (Event b) NoEvent,- runSplineT $ varyUntilEvent v' ve' f)- Event c -> let n = Step (Event b) (Event $ f b c)- in return (n, pure n)+-- value and the event value are tupled and returned as the spline's result+-- value.+untilEvent :: (Applicative m, Monad m)+ => Var m a b -> Var m a (Event c)+ -> Spline a b m (b,c)+untilEvent v ve = SplineT $ t ~> var (uncurry f)+ where t = (,) <$> v <*> ve+ f b ec = case ec of+ NoEvent -> Step (Event b) NoEvent+ Event c -> Step (Event b) (Event (b, c)) +-- | Run two splines concurrently and return the result of the SplineT that+-- concludes first. If they conclude at the same time the result is taken from+-- the spline on the left.+race :: (Applicative m, Monad m, Monoid (f u))+ => SplineT f i u m a -> SplineT f i u m a -> SplineT f i u m a+race (SplineTConst a) s =+ race (SplineT $ pure $ Step mempty $ Event a) s+race s (SplineTConst b) =+ race s (SplineT $ pure $ Step mempty $ Event b)+race (SplineT va) (SplineT vb) = SplineT $ Var $ \i -> do+ (Step ua ea, va') <- runVar va i+ (Step ub eb, vb') <- runVar vb i+ case (ea,eb) of+ (Event _,_) -> return (Step (ua <> ub) ea, va')+ (_,Event _) -> return (Step (ua <> ub) eb, vb')+ (_,_) -> return (Step (ua <> ub) NoEvent,+ runSplineT $ race (SplineT va') (SplineT vb'))++-- | Run a list of splines concurrently. Restart individual splines whenever+-- they conclude in a value. Return a list of the most recent result values once+-- the control spline concludes.+mix :: (Applicative m, Monad m, Monoid (f b))+ => [Maybe c -> SplineT f a b m c] -> SplineT f a b m ()+ -> SplineT f a b m [Maybe c]+mix gs = go gs es $ zipWith ($) gs xs+ where es = replicate n NoEvent+ xs = replicate n Nothing+ n = length gs+ go fs evs guis egui = SplineT $ Var $ \a -> do+ let step (ecs, fb, vs) (f, ec, g) = do+ (Step fb' ec', v) <- runVar (runSplineT g) a+ let ec'' = ec <> ec'+ fb'' = fb <> fb'+ v' = case ec' of+ NoEvent -> v+ Event c -> runSplineT $ f $ Just c+ return (ecs ++ [ec''], fb'', vs ++ [SplineT v'])+ (ecs, fb, guis') <- foldM step ([],mempty,[]) (zip3 fs evs guis)+ (Step fb' ec, v) <- runVar (runSplineT egui) a+ let fb'' = fb <> fb'+ ec' = map toMaybe ecs <$ ec+ return (Step fb'' ec',+ runSplineT $ go fs ecs guis' $ SplineT v)+ -- | Capture the spline's latest output value and tuple it with the -- spline's result value. This is helpful when you want to sample the last -- output value in order to determine the next spline to sequence. capture :: (Applicative m, Monad m, Monoid (f b), Eq (f b))- => SplineT m f a b c -> SplineT m f a b (f b, c)+ => SplineT f a b m c -> SplineT f a b m (f b, c) capture (SplineTConst x) = SplineTConst (mempty, x) capture (SplineT v) = capture' mempty v where capture' mb v' = SplineT $ Var $ \a -> do@@ -197,3 +248,9 @@ let mb' = if fb == mempty then mb else fb ec' = (mb',) <$> ec return (Step fb ec', runSplineT $ capture' mb' v'')++-- | Map the output value of a spline.+mapOutput :: (Functor f, Monoid (f t), Applicative m, Monad m)+ => Var m a (f b -> f t) -> SplineT f a b m c -> SplineT f a t m c+mapOutput _ (SplineTConst c) = SplineTConst c+mapOutput vf (SplineT vx) = SplineT $ toIter <$> vf <*> vx
src/Control/Varying/Time.hs view
@@ -6,15 +6,15 @@ module Control.Varying.Time where import Control.Varying.Core-import Control.Varying.Event hiding (after, before)+import Control.Varying.Event import Control.Applicative import Data.Time.Clock --- | Produces "time" deltas using 'getCurrentTime' and 'diffUTCTime'.+-- | Produces time deltas using 'getCurrentTime' and 'diffUTCTime'. deltaUTC :: Fractional t => Var IO b t deltaUTC = delta getCurrentTime (\a b -> realToFrac $ diffUTCTime a b) --- | Produces "time" deltas using a monadic computation and a difference+-- | Produces time deltas using a monadic computation and a difference -- function. delta :: (Num t, Fractional t, Applicative m, Monad m) => m a -> (a -> a -> t) -> Var m b t@@ -32,16 +32,16 @@ -- Note that as soon as we have accumulated >= t we stop emitting events -- and there is no guarantee that an event will be emitted at time == t. before :: (Applicative m, Monad m, Num t, Ord t) => t -> Var m t (Event ())-before t = Var $ \dt -> do+before t = Var $ \dt -> return $ if t - dt >= 0- then return (Event (), before $ t - dt)- else return (NoEvent, never)+ then (Event (), before $ t - dt)+ else (NoEvent, never) -- | Emits events after t input has been accumulated. -- Note that event emission is not guaranteed to begin exactly at t, -- only at some small delta after t. after :: (Applicative m, Monad m, Num t, Ord t) => t -> Var m t (Event ())-after t = Var $ \dt -> do+after t = Var $ \dt -> return $ if t - dt <= 0- then return (Event (), pure $ Event ())- else return (NoEvent, after $ t - dt)+ then (Event (), pure $ Event ())+ else (NoEvent, after $ t - dt)
src/Control/Varying/Tween.hs view
@@ -22,39 +22,30 @@ -- $creation tween, constant,- -- * Tweening with splines- -- $splines- tweenTo, -- * Interpolation functions -- $lerping linear, easeInCirc, easeOutCirc,- easeInOutCirc, easeInExpo, easeOutExpo,- easeInOutExpo, easeInSine, easeOutSine, easeInOutSine, easeInPow, easeOutPow,- easeInOutPow, easeInCubic, easeOutCubic,- easeInOutCubic, easeInQuad, easeOutQuad,- easeInOutQuad,- -- * Interpolation helpers- easeInOut, -- * Writing your own tweens+ -- $writing Tween, Easing ) where import Control.Varying.Core-import Control.Varying.Event hiding (after, before)+import Control.Varying.Event import Control.Varying.Spline import Control.Varying.Time import Control.Arrow@@ -76,10 +67,6 @@ easeOutQuad :: Num t => Easing t easeOutQuad c t b = (-c) * (t * (t - 2)) + b --- | Ease in and out quadratic.-easeInOutQuad :: (Ord t, Fractional t) => Easing t-easeInOutQuad = easeInOut easeInQuad easeOutQuad- -- | Ease in cubic. easeInCubic :: Num t => Easing t easeInCubic c t b = c * t*t*t + b@@ -88,14 +75,6 @@ easeOutCubic :: Num t => Easing t easeOutCubic c t b = let t' = t - 1 in c * (t'*t'*t' + 1) + b --- | Ease in and out cubic.-easeInOutCubic :: (Ord t, Fractional t) => Easing t-easeInOutCubic = easeInOut easeInCubic easeOutCubic---- | Ease in and out by some power.-easeInOutPow :: (Fractional t, Ord t) => Int -> Easing t-easeInOutPow p = easeInOut (easeInPow p) (easeOutPow p)- -- | Ease in by some power. easeInPow :: Num t => Int -> Easing t easeInPow power c t b = c * (t^power) + b@@ -130,10 +109,6 @@ easeOutExpo :: Floating t => Easing t easeOutExpo c t b = let e = -10 * t in c * (-(2**e) + 1) + b --- | Ease in and out exponential.-easeInOutExpo :: (Ord t, Floating t) => Easing t-easeInOutExpo = easeInOut easeInExpo easeOutExpo- -- | Ease in circular. easeInCirc :: Floating t => Easing t easeInCirc c t b = let s = sqrt (1 - t*t) in -c * (s - 1) + b@@ -144,96 +119,61 @@ s = sqrt (1 - t'*t') in c * s + b --- | Ease in and out circular.-easeInOutCirc :: (Ord t, Floating t) => Easing t-easeInOutCirc = easeInOut easeInCirc easeOutCirc---- | Ease in and out using the given easing equations.-easeInOut :: (Ord t, Num t, Fractional t) => Easing t -> Easing t -> Easing t-easeInOut ein eout c t b = if t >= 0.5 then ein c t b else eout c t b- -- | Ease linear. linear :: Num t => Easing t linear c t b = c * t + b -------------------------------------------------------------------------------- -- $creation--- -- The most direct route toward tweening values is to use 'tween'--- along with an interpolation function such as 'easeInOutExpo'. For example,--- @tween easeInOutExpo 0 100 10@, this will create an event stream that--- produces @Event t@s where `t` is tweened from 0 to 100 over 10 seconds.--- Once the 10 seconds are up, the stream will inhibit (produce `NoEvent`)--- forever. To create a stream of `t` that is tweened from 0 to 100 and--- then stays at 100 forever after requires you to use a combinator from the--- 'Event' module, like so:------ >tween easeInOutExpo 0 100 10 `andThen` 100------ The 'andThen' combinator "disolves" our 'Event's by switching to--- another stream once the first inhibits.+-- along with an interpolation function such as 'easeInExpo'. For example,+-- @tween easeInOutExpo 0 100 10@, this will create a spline that produces a+-- number interpolated from 0 to 100 over 10 seconds. At the end of the+-- tween the spline will return the result value. -------------------------------------------------------------------------------- --- | Creates an event stream that produces an event value interpolated between--- a start and end value using an easing equation ('Easing') over a duration.--- The resulting 'Var' will take a time delta as input. For example:+-- | Creates a spline that produces a value interpolated between a start and+-- end value using an easing equation ('Easing') over a duration. The+-- resulting spline will take a time delta as input. For example: -- -- @--- testWhile_ isEvent v+-- testWhile_ isEvent (deltaUTC ~> v) -- where v :: Var IO a (Event Double)--- v = deltaUTC ~> tween easeOutExpo 0 100 5+-- v = execSpline 0 $ tween easeOutExpo 0 100 5 -- @ -- -- Keep in mind `tween` must be fed time deltas, not absolute time or -- duration. This is mentioned because the author has made that mistake -- more than once ;) tween :: (Applicative m, Monad m, Fractional t, Ord t)- => Easing t -> t -> t -> t -> Var m t (Event t)-tween f start end dur = proc dt -> do- -- Current time as percentage / amount of interpolation (0.0 - 1.0)- t <- timeAsPercentageOf dur -< dt- -- Emitted event- e <- before dur -< dt- -- Total change in value- let c = end - start- b = start- x = f c t b- -- Tag the event with the value.- returnA -< x <$ e+ => Easing t -> t -> t -> t -> Spline t t m t+tween f start end dur = spline start $ timeAsPercentageOf dur ~> var g+ where g t = let c = end - start+ b = start+ x = f c t b+ in if t <= 1.0 then Event x else NoEvent --- Creates a tween that performs no interpolation over the duration.+-- | Creates a tween that performs no interpolation over the duration. constant :: (Applicative m, Monad m, Num t, Ord t)- => a -> t -> Var m t (Event a)-constant value duration = use value $ before duration+ => a -> t -> Spline t a m a+constant value duration = spline value $ use value $ before duration +-- | Varies 0.0 to 1.0 linearly for duration `t` and 1.0 after `t`.+timeAsPercentageOf :: (Applicative m, Monad m, Ord t, Num t, Fractional t)+ => t -> Var m t t+timeAsPercentageOf t = (\t' -> min 1 (t' / t)) <$> accumulate (+) 0+ ----------------------------------------------------------------------------------- $splines--- If you plan on doing a lot of tweening it's probably easiest to build up--- your tweens as splines using do-notation.--- A spline in this context is a numeric computation that is "smooth" over some--- domain. It is defined in a piecewise manner by sequencing other splines--- together using do-notation.--- You can then run the spline, transforming it back into a continuous--- varying value.+-- $writing+-- To create your own tweens just write a function that takes a start+-- value, end value and a duration and return an event stream. -- -- @--- thereAndBack = execSpline 0 $ do--- x <- tweenTo easeOutExpo 0 100 1--- tweenTo easeOutExpo x 0 1+-- tweenInOutExpo s e d = execSpline s $ do+-- x <- tween easeInExpo s e (d/2)+-- tween easeOutExpo x e (d/2) -- @ ----------------------------------------------------------------------------------- |-tweenTo :: (Applicative m, Monad m, Fractional t, Ord t)- => Easing t -> t -> t -> t -> Spline m t t t-tweenTo f start end dur = spline start $ tween f start end dur---- | Varies 0.0 to 1.0 linearly for duration `t` and 1.0 after `t`.-timeAsPercentageOf :: (Applicative m, Monad m, Ord t, Num t, Fractional t)- => t -> Var m t t-timeAsPercentageOf t = proc dt -> do- t' <- accumulate (+) 0 -< dt- returnA -< min 1 (t' / t)- -- | An easing function. The parameters or often named `c`, `t` and `b`, -- where `c` is the total change in value over the complete duration -- (endValue - startValue), `t` is the current percentage of the duration
src/Example.hs view
@@ -1,68 +1,62 @@ module Main where import Control.Varying-import Control.Varying.Time as Time -- time is not auto-exported import Control.Applicative import Text.Printf -- | A simple 2d point type.-data Point = Point { x :: Float- , y :: Float+data Point = Point { px :: Float+ , py :: Float } deriving (Show, Eq) --- | Our Point value that varies over time continuously in x and y.-backAndForth :: Var IO a Point-backAndForth =- -- Here we use Applicative to construct a varying Point that takes time- -- as an input.- (Point <$> tweenx <*> tweeny)- -- Here we feed the varying Point a time signal using the 'plug left'- -- function. We could similarly use the 'plug right' (~>) function- -- and put the time signal before the Point. This is needed because the- -- tweens take time as an input.- <~ time---- An exponential tween back and forth from 0 to 100 over 2 seconds.-tweenx :: (Applicative m, Monad m) => Var m Float Float-tweenx =- -- Tweens only happen for a certain duration and so their sample- -- values have the type (Ord t, Fractional t => Event t). After construction- -- a tween's full type will be- -- (Ord t, Fractional t, Monad m) => Var m t (Event t).- tween easeOutExpo 0 100 1- -- We can chain another tween back to the starting position using- -- `andThenE`, which will sample the first tween until it ends and then- -- switch to sampling the next tween.- `andThenE`- -- Tween back to the starting position.- tween easeOutExpo 100 0 1- -- At this point our resulting sample values will still have the- -- type (Event Float). The tween as a whole will be an event- -- stream. The tween also only runs back and forth once. We'd- -- like the tween to loop forever so that our point cycles back- -- and forth between 0 and 100 indefinitely.- -- We can accomplish this with recursion and the `andThen`- -- combinator, which samples an event stream until it- -- inhibits and then switches to a normal value stream (a- -- varying value). Put succinctly, it disolves our events into- -- values.- `andThen` tweenx+-- An exponential tween back and forth from 0 to 100 over 2 seconds that+-- loops forever. This spline takes float values of delta time as input,+-- outputs the current x value at every step and would result in () if it+-- terminated.+tweenx :: (Applicative m, Monad m) => Spline Float Float m ()+tweenx = do+ -- Tween from 0 to 100 over 1 second+ x <- tween easeOutExpo 0 100 1+ -- Chain another tween back to the starting position+ _ <- tween easeOutExpo x 0 1+ -- Loop forever+ tweenx --- A quadratic tween back and forth from 0 to 100 over 2 seconds.-tweeny :: (Applicative m, Monad m) => Var m Float Float-tweeny =- tween easeOutQuad 0 100 1 `andThenE` tween easeOutQuad 100 0 1 `andThen` tweeny+-- A quadratic tween back and forth from 0 to 100 over 2 seconds that never+-- ends.+tweeny :: (Applicative m, Monad m) => Spline Float Float m ()+tweeny = do+ y <- tween easeOutQuad 0 100 1+ _ <- tween easeOutQuad y 0 1+ tweeny --- Our time signal.+-- Our time signal that provides delta time samples. time :: Var IO a Float time = deltaUTC +-- | Our Point value that varies over time continuously in x and y.+backAndForth :: Var IO a Point+backAndForth =+ -- Turn our splines back into continuous value streams. We must provide+ -- a starting value since splines are not guaranteed to be defined at+ -- their edges.+ let x = execSpline 0 tweenx+ y = execSpline 0 tweeny+ in+ -- Construct a varying Point that takes time as an input.+ (Point <$> x <*> y)+ -- Stream in a time signal using the 'plug left' combinator.+ -- We could similarly use the 'plug right' (~>) function+ -- and put the time signal before the construction above. This is needed+ -- because the tween streams take time as an input.+ <~ time+ main :: IO () main = do putStrLn "Varying Values" loop backAndForth where loop :: Var IO () Point -> IO () loop v = do (point, vNext) <- runVar v ()- printf "\nPoint %03.1f %03.1f" (x point) (y point)+ printf "\nPoint %03.1f %03.1f" (px point) (py point) loop vNext
varying.cabal view
@@ -10,15 +10,15 @@ -- PVP summary: +-+------- breaking API changes -- | | +----- non-breaking API additions -- | | | +--- code changes with no API change-version: 0.1.5.0+version: 0.2.0.0 -- A short (one-line) description of the package.-synopsis: FRP through varying values and monadic splines.+synopsis: FRP through value streams and monadic splines. -- A longer description of the package. description: Varying is a FRP implentation aimed at providing a- simple way to describe values that change over some domain.- It allows monadic, applicative or arrow notation and has+ simple way to describe values that change over a domain.+ It allows monadic, applicative and arrow notation and has convenience functions for tweening. -- URL for the project homepage or repository.