active 0.1.0.19 → 0.2.0.0
raw patch · 5 files changed
+179/−161 lines, 5 filesdep +lensdep +lineardep +vectordep −arraydep −newtypedep −vector-spacedep ~semigroupoidsPVP ok
version bump matches the API change (PVP)
Dependencies added: lens, linear, vector
Dependencies removed: array, newtype, vector-space
Dependency ranges changed: semigroupoids
API changes (from Hackage documentation)
- Data.Active: fromDuration :: Fractional a => Duration -> a
- Data.Active: fromTime :: Fractional a => Time -> a
- Data.Active: instance AdditiveGroup Duration
- Data.Active: instance AdditiveGroup Time
- Data.Active: instance AffineSpace Time
- Data.Active: instance Enum Duration
- Data.Active: instance Enum Time
- Data.Active: instance Eq Duration
- Data.Active: instance Eq Time
- Data.Active: instance Fractional Duration
- Data.Active: instance Fractional Time
- Data.Active: instance InnerSpace Time
- Data.Active: instance Newtype (Active a) (MaybeApply Dynamic a)
- Data.Active: instance Newtype (MaybeApply f a) (Either (f a) a)
- Data.Active: instance Newtype Duration Rational
- Data.Active: instance Newtype Time Rational
- Data.Active: instance Num Duration
- Data.Active: instance Num Time
- Data.Active: instance Ord Duration
- Data.Active: instance Ord Time
- Data.Active: instance Read Duration
- Data.Active: instance Read Time
- Data.Active: instance Real Duration
- Data.Active: instance Real Time
- Data.Active: instance RealFrac Duration
- Data.Active: instance RealFrac Time
- Data.Active: instance Semigroup Era
- Data.Active: instance Show Duration
- Data.Active: instance Show Era
- Data.Active: instance Show Time
- Data.Active: instance VectorSpace Duration
- Data.Active: instance VectorSpace Time
- Data.Active: toDuration :: Real a => a -> Duration
- Data.Active: toTime :: Real a => a -> Time
+ Data.Active: instance Active a1 ~ t0 => Rewrapped (Active a0) t0
+ Data.Active: instance Additive Duration
+ Data.Active: instance Affine Time
+ Data.Active: instance Applicative Duration
+ Data.Active: instance Duration n1 ~ t0 => Rewrapped (Duration n0) t0
+ Data.Active: instance Enum n => Enum (Duration n)
+ Data.Active: instance Enum n => Enum (Time n)
+ Data.Active: instance Eq n => Eq (Duration n)
+ Data.Active: instance Eq n => Eq (Time n)
+ Data.Active: instance Fractional n => Fractional (Duration n)
+ Data.Active: instance Fractional n => Fractional (Time n)
+ Data.Active: instance Functor Duration
+ Data.Active: instance Functor Time
+ Data.Active: instance MaybeApply f1 a1 ~ t0 => Rewrapped (MaybeApply f0 a0) t0
+ Data.Active: instance Num n => Monoid (Duration n)
+ Data.Active: instance Num n => Num (Duration n)
+ Data.Active: instance Num n => Num (Time n)
+ Data.Active: instance Num n => Semigroup (Duration n)
+ Data.Active: instance Ord n => Ord (Duration n)
+ Data.Active: instance Ord n => Ord (Time n)
+ Data.Active: instance Ord n => Semigroup (Era n)
+ Data.Active: instance Read n => Read (Duration n)
+ Data.Active: instance Read n => Read (Time n)
+ Data.Active: instance Real n => Real (Duration n)
+ Data.Active: instance Real n => Real (Time n)
+ Data.Active: instance RealFrac n => RealFrac (Duration n)
+ Data.Active: instance RealFrac n => RealFrac (Time n)
+ Data.Active: instance Show n => Show (Duration n)
+ Data.Active: instance Show n => Show (Era n)
+ Data.Active: instance Show n => Show (Time n)
+ Data.Active: instance Time n1 ~ t0 => Rewrapped (Time n0) t0
+ Data.Active: instance Wrapped (Active a0)
+ Data.Active: instance Wrapped (Duration n0)
+ Data.Active: instance Wrapped (MaybeApply f0 a0)
+ Data.Active: instance Wrapped (Time n0)
- Data.Active: Dynamic :: Era -> (Time -> a) -> Dynamic a
+ Data.Active: Dynamic :: Era Rational -> (Time Rational -> a) -> Dynamic a
- Data.Active: activeEra :: Active a -> Maybe Era
+ Data.Active: activeEra :: Active a -> Maybe (Era Rational)
- Data.Active: atTime :: Time -> Active a -> Active a
+ Data.Active: atTime :: Time Rational -> Active a -> Active a
- Data.Active: data Duration
+ Data.Active: data Duration n
- Data.Active: data Era
+ Data.Active: data Era n
- Data.Active: data Time
+ Data.Active: data Time n
- Data.Active: duration :: Era -> Duration
+ Data.Active: duration :: Num n => Era n -> Duration n
- Data.Active: end :: Era -> Time
+ Data.Active: end :: Era n -> Time n
- Data.Active: era :: Dynamic a -> Era
+ Data.Active: era :: Dynamic a -> Era Rational
- Data.Active: interval :: Fractional a => Time -> Time -> Active a
+ Data.Active: interval :: Fractional a => Time Rational -> Time Rational -> Active a
- Data.Active: mkActive :: Time -> Time -> (Time -> a) -> Active a
+ Data.Active: mkActive :: Time Rational -> Time Rational -> (Time Rational -> a) -> Active a
- Data.Active: mkDynamic :: Time -> Time -> (Time -> a) -> Dynamic a
+ Data.Active: mkDynamic :: Time Rational -> Time Rational -> (Time Rational -> a) -> Dynamic a
- Data.Active: mkEra :: Time -> Time -> Era
+ Data.Active: mkEra :: Time n -> Time n -> Era n
- Data.Active: onDynamic :: (Time -> Time -> (Time -> a) -> b) -> Dynamic a -> b
+ Data.Active: onDynamic :: (Time Rational -> Time Rational -> (Time Rational -> a) -> b) -> Dynamic a -> b
- Data.Active: runActive :: Active a -> (Time -> a)
+ Data.Active: runActive :: Active a -> Time Rational -> a
- Data.Active: runDynamic :: Dynamic a -> Time -> a
+ Data.Active: runDynamic :: Dynamic a -> Time Rational -> a
- Data.Active: setEra :: Era -> Active a -> Active a
+ Data.Active: setEra :: Era Rational -> Active a -> Active a
- Data.Active: shift :: Duration -> Active a -> Active a
+ Data.Active: shift :: Duration Rational -> Active a -> Active a
- Data.Active: shiftDynamic :: Duration -> Dynamic a -> Dynamic a
+ Data.Active: shiftDynamic :: Duration Rational -> Dynamic a -> Dynamic a
- Data.Active: snapshot :: Time -> Active a -> Active a
+ Data.Active: snapshot :: Time Rational -> Active a -> Active a
- Data.Active: start :: Era -> Time
+ Data.Active: start :: Era n -> Time n
- Data.Active: stretchTo :: Duration -> Active a -> Active a
+ Data.Active: stretchTo :: Duration Rational -> Active a -> Active a
Files
- CHANGES +6/−4
- LICENSE +9/−1
- active.cabal +11/−12
- src/Data/Active.hs +115/−114
- test/active-tests.hs +38/−30
CHANGES view
@@ -1,7 +1,9 @@-0.1.0.19 (2 April 2015)-----------------------+0.2.0.0 (19 April 2015)+----------------------- - - allow `vector-space-0.10`+- switch from `vector-space` to `linear`+- allow `lens-4.9`+- allow `base-4.8` 0.1.0.18 (22 Feb 2015) ----------------------@@ -12,7 +14,7 @@ 0.1.0.17 (03 Dec 2014) ---------------------- - * Allow `semigroups-0.16`+- Allow `semigroups-0.16` 0.1.0.16 (2 June 2014) ----------------------
LICENSE view
@@ -1,4 +1,12 @@-Copyright (c) 2011-2012, Brent Yorgey+Copyright (c) 2011-2015, active team:++Andy Gill <andygill@ku.edu>+Ben Gamari <ben@smart-cactus.org>+Brent Yorgey <byorgey@gmail.com>+Christopher Chalmers <c.chalmers@me.com>+Daniel Bergey <bergey@alum.mit.edu>+Jeffrey Rosenbluth <jeffrey.rosenbluth@gmail.com>+Ryan Scott <ryan.gl.scott@ku.edu> All rights reserved.
active.cabal view
@@ -1,17 +1,17 @@ name: active-version: 0.1.0.19+version: 0.2.0.0 synopsis: Abstractions for animation description: "Active" abstraction for animated things with finite start and end times. license: BSD3 license-file: LICENSE author: Brent Yorgey maintainer: byorgey@cis.upenn.edu-copyright: (c) 2011 Brent Yorgey+copyright: (c) 2011-2015 Brent Yorgey category: Data build-type: Simple cabal-version: >=1.10 extra-source-files: CHANGES-tested-with: GHC == 6.12.3, GHC == 7.0.4, GHC == 7.2.1, GHC == 7.4.1, GHC == 7.6.1+tested-with: GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.1 bug-reports: https://github.com/diagrams/active/issues source-repository head type: git@@ -20,11 +20,11 @@ library exposed-modules: Data.Active build-depends: base >= 4.0 && < 4.9,- array >= 0.3 && < 0.6,+ vector >= 0.10, semigroups >= 0.1 && < 0.17,- semigroupoids >= 1.2 && < 4.4,- vector-space >= 0.8 && < 0.11,- newtype >= 0.2 && < 0.3+ semigroupoids >= 1.2 && < 5.0,+ lens >= 4.0 && < 4.10,+ linear >= 1.14 && < 1.19 hs-source-dirs: src default-language: Haskell2010 @@ -32,12 +32,11 @@ type: exitcode-stdio-1.0 main-is: active-tests.hs build-depends: base >= 4.0 && < 4.9,- array >= 0.3 && < 0.6,+ vector >= 0.10, semigroups >= 0.1 && < 0.17,- semigroupoids >= 1.2 && < 4.4,- vector-space >= 0.8 && < 0.11,- newtype >= 0.2 && < 0.3,-+ semigroupoids >= 1.2 && < 5.0,+ lens >= 4.0 && < 4.9,+ linear >= 1.14 && < 1.19, QuickCheck >= 2.4.2 && < 2.8 hs-source-dirs: src, test default-language: Haskell2010
src/Data/Active.hs view
@@ -1,10 +1,16 @@-{-# LANGUAGE DeriveFunctor- , GeneralizedNewtypeDeriving- , TypeSynonymInstances- , MultiParamTypeClasses- , TypeFamilies- , FlexibleInstances- #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE UndecidableInstances #-}+-- UndecidableInstances needed for ghc < 707 {-# OPTIONS_GHC -fno-warn-orphans #-} -----------------------------------------------------------------------------@@ -70,6 +76,10 @@ -- There are many functions for transforming and composing active -- values; see the documentation below for more details. --+--+-- With careful handling, this module should be suitable to generating+-- deep embeddings if 'Active' values.+-- ----------------------------------------------------------------------------- module Data.Active@@ -77,8 +87,8 @@ -- ** Time and duration - Time, toTime, fromTime- , Duration, toDuration, fromDuration+ Time+ , Duration -- ** Eras @@ -132,21 +142,23 @@ ) where -import Control.Applicative-import Control.Arrow ((&&&))-import Control.Newtype+#if __GLASGOW_HASKELL__ < 710+import Control.Applicative+#endif -import Data.Array-import Data.Maybe+import Control.Arrow ((&&&))+import Control.Lens hiding (backwards, (<.>)) -import Data.Functor.Apply-import Data.Semigroup hiding (First(..))-import Data.Monoid (First(..))+import Data.Functor.Apply+import Data.Semigroup hiding (First (..))+import Data.Monoid (First (..))+import qualified Data.Vector as V+import Data.Maybe -import Data.VectorSpace hiding ((<.>))-import qualified Data.VectorSpace as VS-import Data.AffineSpace+import Linear+import Linear.Affine + ------------------------------------------------------------ -- Time ------------------------------------------------------------@@ -156,31 +168,19 @@ -- 'Num' and 'Fractional' instances. 'toTime' and 'fromTime' are -- also provided for convenience in converting between @Time@ and -- other numeric types.-newtype Time = Time { unTime :: Rational }- deriving ( Eq, Ord, Show, Read, Enum, Num, Fractional, Real, RealFrac- , AdditiveGroup- )--instance Newtype Time Rational where- pack = Time- unpack = unTime--instance VectorSpace Time where- type Scalar Time = Rational- s *^ (Time t) = Time (s * t)+newtype Time n = Time { unTime :: n }+ deriving (Eq, Ord, Show, Read, Enum, Num, Fractional, Real, RealFrac, Functor) -instance InnerSpace Time where- (Time a) <.> (Time b) = a * b+makeWrapped ''Time --- | Convert any value of a 'Real' type (including @Int@, @Integer@,--- @Rational@, @Float@, and @Double@) to a 'Time'.-toTime :: Real a => a -> Time-toTime = fromRational . toRational+instance Affine Time where+ type Diff Time = Duration+ (Time t1) .-. (Time t2) = Duration (t1 - t2)+ (Time t) .+^ (Duration d) = Time (t + d) --- | Convert a 'Time' to a value of any 'Fractional' type (such as--- @Rational@, @Float@, or @Double@).-fromTime :: Fractional a => Time -> a-fromTime = fromRational . unTime+-- instance Deadline Time a where+-- -- choose tm deadline (if before / at deadline) (if after deadline)+-- choose t1 t2 a b = if t1 <= t2 then a else b -- | An abstract type representing /elapsed time/ between two points -- in time. Note that durations can be negative. Literal numeric@@ -188,32 +188,24 @@ -- 'Fractional' instances. 'toDuration' and 'fromDuration' are also -- provided for convenience in converting between @Duration@s and -- other numeric types.-newtype Duration = Duration { unDuration :: Rational }- deriving ( Eq, Ord, Show, Read, Enum, Num, Fractional, Real, RealFrac- , AdditiveGroup)+newtype Duration n = Duration n+ deriving (Eq, Ord, Show, Read, Enum, Num, Fractional, Real, RealFrac, Functor) -instance Newtype Duration Rational where- pack = Duration- unpack = unDuration+makeWrapped ''Duration -instance VectorSpace Duration where- type Scalar Duration = Rational- s *^ (Duration d) = Duration (s * d)+instance Applicative Duration where+ pure = Duration+ Duration f <*> Duration x = Duration (f x) -instance AffineSpace Time where- type Diff Time = Duration- (Time t1) .-. (Time t2) = Duration (t1 - t2)- (Time t) .+^ (Duration d) = Time (t + d)+instance Additive Duration where+ zero = 0 --- | Convert any value of a 'Real' type (including @Int@, @Integer@,--- @Rational@, @Float@, and @Double@) to a 'Duration'.-toDuration :: Real a => a -> Duration-toDuration = fromRational . toRational+instance Num n => Semigroup (Duration n) where+ (<>) = (^+^) --- | Convert a 'Duration' to any other 'Fractional' type (such as--- @Rational@, @Float@, or @Double@).-fromDuration :: Fractional a => Duration -> a-fromDuration = fromRational . unDuration+instance Num n => Monoid (Duration n) where+ mappend = (<>)+ mempty = 0 -- | An @Era@ is a concrete span of time, that is, a pair of times -- representing the start and end of the era. @Era@s form a@@ -224,23 +216,24 @@ -- -- @Era@ is abstract. To construct @Era@ values, use 'mkEra'; to -- deconstruct, use 'start' and 'end'.-newtype Era = Era (Min Time, Max Time)- deriving (Semigroup, Show)+newtype Era n = Era (Min (Time n), Max (Time n))+ deriving (Show, Semigroup) + -- | Create an 'Era' by specifying start and end 'Time's.-mkEra :: Time -> Time -> Era+mkEra :: Time n -> Time n -> Era n mkEra s e = Era (Min s, Max e) -- | Get the start 'Time' of an 'Era'.-start :: Era -> Time+start :: Era n -> Time n start (Era (Min t, _)) = t -- | Get the end 'Time' of an 'Era'.-end :: Era -> Time+end :: Era n -> Time n end (Era (_, Max t)) = t -- | Compute the 'Duration' of an 'Era'.-duration :: Era -> Duration+duration :: Num n => Era n -> Duration n duration = (.-.) <$> end <*> start ------------------------------------------------------------@@ -252,10 +245,11 @@ -- will be mostly an internal implementation detail and that -- 'Active' will be most commonly used. But you never know what -- uses people might find for things.-data Dynamic a = Dynamic { era :: Era- , runDynamic :: Time -> a++data Dynamic a = Dynamic { era :: Era Rational+ , runDynamic :: Time Rational -> a }- deriving (Functor)+ deriving Functor -- | 'Dynamic' is an instance of 'Apply' (/i.e./ 'Applicative' without -- 'pure'): a time-varying function is applied to a time-varying@@ -265,6 +259,7 @@ -- 'pure': the era would have to be empty, but there is no such -- thing as an empty era (that is, 'Era' is not an instance of -- 'Monoid').+ instance Apply Dynamic where (Dynamic d1 f1) <.> (Dynamic d2 f2) = Dynamic (d1 <> d2) (f1 <.> f2) @@ -277,15 +272,15 @@ -- | Create a 'Dynamic' from a start time, an end time, and a -- time-varying value.-mkDynamic :: Time -> Time -> (Time -> a) -> Dynamic a+mkDynamic :: Time Rational -> Time Rational -> (Time Rational -> a) -> Dynamic a mkDynamic s e = Dynamic (mkEra s e) -- | Fold for 'Dynamic'.-onDynamic :: (Time -> Time -> (Time -> a) -> b) -> Dynamic a -> b+onDynamic :: (Time Rational -> Time Rational -> (Time Rational -> a) -> b) -> Dynamic a -> b onDynamic f (Dynamic e d) = f (start e) (end e) d -- | Shift a 'Dynamic' value by a certain duration.-shiftDynamic :: Duration -> Dynamic a -> Dynamic a+shiftDynamic :: Duration Rational -> Dynamic a -> Dynamic a shiftDynamic sh = onDynamic $ \s e d -> mkDynamic@@ -321,37 +316,29 @@ newtype Active a = Active (MaybeApply Dynamic a) deriving (Functor, Apply, Applicative) -instance Newtype (Active a) (MaybeApply Dynamic a) where- pack = Active- unpack (Active m) = m--instance Newtype (MaybeApply f a) (Either (f a) a) where- pack = MaybeApply- unpack = runMaybeApply+makeWrapped ''Active+makeWrapped ''MaybeApply --- | Ideally this would be defined in the @newtype@ package. If it is--- ever added we can remove it from here.-over2 :: (Newtype n o, Newtype n' o', Newtype n'' o'')- => (o -> n) -> (o -> o' -> o'') -> (n -> n' -> n'')-over2 _ f n1 n2 = pack (f (unpack n1) (unpack n2))+active :: Iso' (Active a) (Either (Dynamic a) a)+active = _Wrapped . _Wrapped -- | Active values over a type with a 'Semigroup' instance are also an -- instance of 'Semigroup'. Two active values are combined -- pointwise; the resulting value is constant iff both inputs are. instance Semigroup a => Semigroup (Active a) where- (<>) = (over2 Active . over2 MaybeApply) combine+ (view active -> a) <> (view active -> b) = review active $ combine a b where- combine (Right m1) (Right m2)- = Right (m1 <> m2)+ combine (Right m1) (Right m2)+ = Right (m1 <> m2) - combine (Left (Dynamic dur f)) (Right m)- = Left (Dynamic dur (f <> const m))+ combine (Left (Dynamic dur f)) (Right m)+ = Left (Dynamic dur (f <> const m)) - combine (Right m) (Left (Dynamic dur f))- = Left (Dynamic dur (const m <> f))+ combine (Right m) (Left (Dynamic dur f))+ = Left (Dynamic dur (const m <> f)) - combine (Left d1) (Left d2)- = Left (d1 <> d2)+ combine (Left d1) (Left d2)+ = Left (d1 <> d2) instance (Monoid a, Semigroup a) => Monoid (Active a) where mempty = Active (MaybeApply (Right mempty))@@ -363,7 +350,7 @@ -- | Create a dynamic 'Active' from a start time, an end time, and a -- time-varying value.-mkActive :: Time -> Time -> (Time -> a) -> Active a+mkActive :: Time Rational -> Time Rational -> (Time Rational -> a) -> Active a mkActive s e f = fromDynamic (mkDynamic s e f) -- | Fold for 'Active's. Process an 'Active a', given a function to@@ -375,11 +362,14 @@ -- | Modify an 'Active' value using a case analysis to see whether it -- is constant or dynamic.-modActive :: (a -> b) -> (Dynamic a -> Dynamic b) -> Active a -> Active b+modActive+ :: (a -> b)+ -> (Dynamic a -> Dynamic b)+ -> Active a -> Active b modActive f g = onActive (pure . f) (fromDynamic . g) -- | Interpret an 'Active' value as a function from time.-runActive :: Active a -> (Time -> a)+runActive :: Active a -> Time Rational -> a runActive = onActive const runDynamic -- | Get the value of an @Active a@ at the beginning of its era.@@ -392,7 +382,7 @@ -- | Get the 'Era' of an 'Active' value (or 'Nothing' if it is -- a constant/pure value).-activeEra :: Active a -> Maybe Era+activeEra :: Active a -> Maybe (Era Rational) activeEra = onActive (const Nothing) (Just . era) -- | Test whether an 'Active' value is constant.@@ -431,7 +421,7 @@ -- | @interval a b@ is an active value starting at time @a@, ending at -- time @b@, and taking the value @t@ at time @t@.-interval :: Fractional a => Time -> Time -> Active a+interval :: Fractional a => Time Rational -> Time Rational -> Active a interval a b = mkActive a b (fromRational . unTime) -- | @stretch s act@ \"stretches\" the active @act@ so that it takes@@ -445,7 +435,9 @@ -- | @stretchTo d@ 'stretch'es an 'Active' so it has duration @d@. -- Has no effect if (1) @d@ is non-positive, or (2) the 'Active' -- value is constant, or (3) the 'Active' value has zero duration.-stretchTo :: Duration -> Active a -> Active a+-- [AJG: conditions (1) and (3) no longer true: to consider changing]++stretchTo :: Duration Rational -> Active a -> Active a stretchTo d a | d <= 0 = a | (duration <$> activeEra a) == Just 0 = a@@ -459,7 +451,7 @@ -- | @shift d act@ shifts the start time of @act@ by duration @d@. -- Has no effect on constant values.-shift :: Duration -> Active a -> Active a+shift :: Duration Rational -> Active a -> Active a shift sh = modActive id (shiftDynamic sh) -- | Reverse an active value so the start of its era gets mapped to@@ -471,11 +463,12 @@ backwards = modActive id . onDynamic $ \s e d -> mkDynamic s e- (\t -> d (e - t + s))+ (\t -> d (s .+^ (e .-. t))) + -- | Take a \"snapshot\" of an active value at a particular time, -- resulting in a constant value.-snapshot :: Time -> Active a -> Active a+snapshot :: Time Rational -> Active a -> Active a snapshot t a = pure (runActive a t) -- | \"Clamp\" an active value so that it is constant before and after@@ -490,6 +483,7 @@ -- -- See also 'clampBefore' and 'clampAfter', which clamp only before -- or after the era, respectively.+ clamp :: Active a -> Active a clamp = modActive id . onDynamic $ \s e d ->@@ -508,6 +502,8 @@ clampBefore :: Active a -> Active a clampBefore = undefined +--- XXX These are undefined!+ -- | \"Clamp\" an active value so that it is constant after the end -- of its era. For example, @clampBefore 'ui'@ can be visualized as --@@ -517,6 +513,7 @@ clampAfter :: Active a -> Active a clampAfter = undefined + -- | \"Trim\" an active value so that it is empty outside its era. -- @trim@ has no effect on constant values. --@@ -532,6 +529,7 @@ -- -- See also 'trimBefore' and 'trimActive', which trim only before or -- after the era, respectively.+ trim :: Monoid a => Active a -> Active a trim = modActive id . onDynamic $ \s e d ->@@ -541,6 +539,7 @@ | otherwise -> d t ) + -- | \"Trim\" an active value so that it is empty /before/ the start -- of its era. For example, @trimBefore 'ui'@ can be visualized as --@@ -572,7 +571,7 @@ -- | Set the era of an 'Active' value. Note that this will change a -- constant 'Active' into a dynamic one which happens to have the -- same value at all times.-setEra :: Era -> Active a -> Active a+setEra :: Era Rational -> Active a -> Active a setEra er = onActive (mkActive (start er) (end er) . const)@@ -581,7 +580,7 @@ -- | @atTime t a@ is an active value with the same behavior as @a@, -- shifted so that it starts at time @t@. If @a@ is constant it is -- returned unchanged.-atTime :: Time -> Active a -> Active a+atTime :: Time Rational -> Active a -> Active a atTime t a = maybe a (\e -> shift (t .-. start e) a) (activeEra a) -- | @a1 \`after\` a2@ produces an active that behaves like @a1@ but is@@ -611,7 +610,7 @@ -- return the first. (|>>) :: Active a -> Active a -> Active a a1 |>> a2 = (fromJust . getFirst) <$>- (trimAfter (First . Just <$> a1) ->> trimBefore (First . Just <$> a2))+ (trimAfter (First . Just <$> a1) ->> trimBefore (First . Just <$> a2)) -- XXX implement 'movie' with a balanced fold @@ -634,12 +633,12 @@ -- It is an error to call @discrete@ on the empty list. discrete :: [a] -> Active a discrete [] = error "Data.Active.discrete must be called with a non-empty list."-discrete xs = f <$> (ui :: Active Rational)- where f t | t <= 0 = arr ! 0- | t >= 1 = arr ! (n-1)- | otherwise = arr ! floor (t * fromIntegral n)- n = length xs- arr = listArray (0, n-1) xs+discrete xs = f <$> ui+ where f (t :: Rational)+ | t <= 0 = V.unsafeHead v+ | t >= 1 = V.unsafeLast v+ | otherwise = V.unsafeIndex v $ floor (t * fromIntegral (V.length v))+ v = V.fromList xs -- | @simulate r act@ simulates the 'Active' value @act@, returning a -- list of \"snapshots\" taken at regular intervals from the start@@ -651,6 +650,7 @@ -- times), a list of length 1 is returned, containing the constant -- value. simulate :: Rational -> Active a -> [a]+simulate 0 = const [] simulate rate = onActive (:[]) (\d -> map (runDynamic d)@@ -659,3 +659,4 @@ in [s, s + 1^/rate .. e] ) )+
test/active-tests.hs view
@@ -1,26 +1,32 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleContexts #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Main where -import Control.Applicative-import Control.Monad (when)-import Data.Semigroup+#if __GLASGOW_HASKELL__ < 710+import Control.Applicative+#endif -import System.Exit (exitFailure)+import Control.Monad (unless)+import Data.Semigroup -import Test.QuickCheck-import Text.Printf (printf)+import System.Exit (exitFailure) -import Data.Active-import Data.VectorSpace-import Data.AffineSpace+import Test.QuickCheck+import Text.Printf (printf) +import Data.Active++import Linear.Affine+import Linear.Vector+ main :: IO () main = do- results <- mapM (\(s,t) -> printf "%-40s" s >> t) tests- when (not . all isSuccess $ results) $ exitFailure+ results <- mapM (\(s,t) -> printf "%-40s" s >> t) tests+ unless (all isSuccess results) exitFailure where isSuccess (Success{}) = True- isSuccess _ = False+ isSuccess _ = False qc x = quickCheckWithResult (stdArgs { maxSuccess = 200 }) x tests = [ ("era/start", qc prop_era_start ) , ("era/end", qc prop_era_end )@@ -42,17 +48,19 @@ , ("atTime/start", qc prop_atTime_start ) , ("atTime/fun", qc prop_atTime_fun ) ]+{-# ANN main ("HLint: ignore Eta reduce" :: String) #-}+-- eta reducing qc breaks it instance Arbitrary Any where arbitrary = Any <$> arbitrary -instance Arbitrary Time where+instance (Arbitrary n, Fractional n, Real n) => Arbitrary (Time n) where arbitrary = fromRational <$> arbitrary -instance CoArbitrary Time where+instance (CoArbitrary n, Real n) => CoArbitrary (Time n) where coarbitrary t = coarbitrary (toRational t) -instance Arbitrary Duration where+instance (Arbitrary n, Fractional n, Real n) => Arbitrary (Duration n) where arbitrary = (fromRational . abs) <$> arbitrary instance Arbitrary a => Arbitrary (Dynamic a) where@@ -71,35 +79,35 @@ instance Show a => Show (Active a) where show = onActive (\c -> "<<" ++ show c ++ ">>")- (\d -> show d)+ show -prop_era_start :: Time -> Time -> Bool+prop_era_start :: Time Rational -> Time Rational -> Bool prop_era_start t1 t2 = start (mkEra t1 t2) == t1 -prop_era_end :: Time -> Time -> Bool+prop_era_end :: Time Rational -> Time Rational -> Bool prop_era_end t1 t2 = end (mkEra t1 t2) == t2 -prop_duration :: Time -> Time -> Bool+prop_duration :: Time Rational -> Time Rational -> Bool prop_duration t1 t2 = duration (mkEra t1 t2) == (t2 .-. t1) -prop_shiftDynamic_start :: Duration -> Dynamic Bool -> Bool+prop_shiftDynamic_start :: Duration Rational -> Dynamic Bool -> Bool prop_shiftDynamic_start dur dyn = (start . era) (shiftDynamic dur dyn) == ((start . era) dyn .+^ dur) -prop_shiftDynamic_end :: Duration -> Dynamic Bool -> Bool+prop_shiftDynamic_end :: Duration Rational -> Dynamic Bool -> Bool prop_shiftDynamic_end dur dyn = (end . era) (shiftDynamic dur dyn) == ((end . era) dyn .+^ dur) -prop_shiftDynamic_fun :: Duration -> Dynamic Bool -> Time -> Bool+prop_shiftDynamic_fun :: Duration Rational -> Dynamic Bool -> Time Rational -> Bool prop_shiftDynamic_fun dur dyn t = runDynamic dyn t == runDynamic (shiftDynamic dur dyn) (t .+^ dur) -prop_active_semi_hom :: Active Any -> Active Any -> Time -> Bool+prop_active_semi_hom :: Active Any -> Active Any -> Time Rational -> Bool prop_active_semi_hom a1 a2 t = runActive a1 t <> runActive a2 t == runActive (a1 <> a2) t -prop_ui_id :: Time -> Bool-prop_ui_id t = runActive (ui :: Active Time) t == t+prop_ui_id :: Time Rational -> Bool+prop_ui_id t = runActive (ui :: Active (Time Rational)) t == t prop_stretch_start :: Rational -> Active Bool -> Bool prop_stretch_start r a@@ -115,7 +123,7 @@ = runActive a t runActive (stretch r t) -} -prop_stretchTo_dur :: Positive Duration -> Active Bool -> Property+prop_stretchTo_dur :: Positive (Duration Rational) -> Active Bool -> Property prop_stretchTo_dur (Positive dur) a = isDynamic a && ((duration <$> activeEra a) /= Just 0) ==> (duration <$> activeEra (stretchTo dur a)) == Just dur@@ -136,20 +144,20 @@ where a1 = fromDynamic d1 a2 = fromDynamic d2 -prop_shift_start :: Duration -> Active Bool -> Bool+prop_shift_start :: Duration Rational -> Active Bool -> Bool prop_shift_start d a = ((.+^ d) . start <$> activeEra a) == (start <$> activeEra (shift d a)) -prop_shift_end :: Duration -> Active Bool -> Bool+prop_shift_end :: Duration Rational -> Active Bool -> Bool prop_shift_end d a = ((.+^ d) . end <$> activeEra a) == (end <$> activeEra (shift d a)) -prop_atTime_start :: Time -> Dynamic Bool -> Bool+prop_atTime_start :: Time Rational -> Dynamic Bool -> Bool prop_atTime_start t dyn = (start <$> activeEra (atTime t a)) == Just t where a = fromDynamic dyn -prop_atTime_fun :: Time -> Dynamic Bool -> Duration -> Bool+prop_atTime_fun :: Time Rational -> Dynamic Bool -> Duration Rational -> Bool prop_atTime_fun t dyn d = runActive (atTime t a) (t .+^ d) == runActive a (s .+^ d) where a = fromDynamic dyn