Use gifs to improve documentation. (#21)

Former-commit-id: 40e5ccedcf5d1ca8f0d5cc373ba3b3c4cc2d6c6a
This commit is contained in:
David Himmelstrup 2019-09-28 21:35:42 +08:00 committed by GitHub
commit dfd52961ae
50 changed files with 512 additions and 129 deletions

11
docs/gen_doc_gifs.sh Executable file
View file

@ -0,0 +1,11 @@
#!/usr/bin/env bash
ROOT=`stack path --project-root`
for src in $ROOT/examples/doc_*.hs; do
BASE=`basename $src .hs`
DST=$ROOT/docs/gifs/$BASE.gif
if [[ "$src" -nt "$DST" ]]; then
echo stack $src render -o $DST
stack $src render -o $DST
fi
done

View file

@ -1,5 +1,6 @@
#!/usr/bin/env bash
ROOT=`stack path --project-root`
EXAMPLES='boundingbox colormaps goo latex_basic latex_color latex_draw
latex_wheel raster sphere sunflower tangent_and_normal vector_field'
@ -7,18 +8,18 @@ WIDTH=640
HEIGHT=$((WIDTH*9/16))
FPS=30
SRC_DIR=examples
DST_DIR=docs/rendered
SRC_DIR=$ROOT/examples
DST_DIR=$ROOT/docs/rendered
OPTS='--fps $FPS --width $WIDTH --height $HEIGHT'
cat << EOF > docs/gallery.md
cat << EOF > $ROOT/docs/gallery.md
# Gallery
This file is auto-generated by docs/render_all.sh. DO NOT EDIT.
EOF
for e in $EXAMPLES; do
cat << EOF >> docs/gallery.md
cat << EOF >> $ROOT/docs/gallery.md
## $e
<details>

View file

@ -10,8 +10,8 @@ main :: IO ()
main = reanimate bbox
bbox :: Animation
bbox = bg `sim`
mapA (translate (-screenWidth/4) 0) bbox1 `sim`
bbox = bg `parA`
mapA (translate (-screenWidth/4) 0) bbox1 `parA`
mapA (translate (screenWidth/4) 0) bbox2
where
bg = animate $ const $ mkBackground "black"

View file

@ -19,8 +19,8 @@ youtube = id
main :: IO ()
main = reanimate $ youtube $ pauseAtEnd 2 $ playThenReverseA $ pauseAtEnd 2 $ mkAnimation 5 $ \t ->
let s = signalCurve 2 t
offsetWidth = screenWidth * 0.5
let s = curveS 2 t
offsetWidth = screenWidth * 0.5
nubWidth = 0.2
textYOffset = 0.2
in mkGroup

View file

@ -6,6 +6,6 @@ import Reanimate
main :: IO ()
main = reanimate $ mkAnimation dur $
mkCircle . signalFromTo 0 (dur*60-1) signalLinear
mkCircle . fromToS 0 (dur*60-1)
where
dur = 2

9
examples/doc_andThen.hs Normal file
View file

@ -0,0 +1,9 @@
#!/usr/bin/env stack
-- stack runghc --package reanimate
module Main(main) where
import Reanimate
import Reanimate.Builtin.Documentation
main :: IO ()
main = reanimate $ docEnv $ drawBox `andThen` drawCircle

9
examples/doc_bellS.hs Normal file
View file

@ -0,0 +1,9 @@
#!/usr/bin/env stack
-- stack runghc --package reanimate
module Main(main) where
import Reanimate
import Reanimate.Builtin.Documentation
main :: IO ()
main = reanimate $ docEnv $ signalA (bellS 2) drawProgress

View file

@ -0,0 +1,9 @@
#!/usr/bin/env stack
-- stack runghc --package reanimate
module Main(main) where
import Reanimate
import Reanimate.Builtin.Documentation
main :: IO ()
main = reanimate $ docEnv $ signalA (constantS 0.5) drawProgress

9
examples/doc_curveS.hs Normal file
View file

@ -0,0 +1,9 @@
#!/usr/bin/env stack
-- stack runghc --package reanimate
module Main(main) where
import Reanimate
import Reanimate.Builtin.Documentation
main :: IO ()
main = reanimate $ docEnv $ signalA (curveS 2) drawProgress

9
examples/doc_drawBox.hs Normal file
View file

@ -0,0 +1,9 @@
#!/usr/bin/env stack
-- stack runghc --package reanimate
module Main(main) where
import Reanimate
import Reanimate.Builtin.Documentation
main :: IO ()
main = reanimate $ docEnv drawBox

View file

@ -0,0 +1,9 @@
#!/usr/bin/env stack
-- stack runghc --package reanimate
module Main(main) where
import Reanimate
import Reanimate.Builtin.Documentation
main :: IO ()
main = reanimate $ docEnv drawCircle

View file

@ -0,0 +1,9 @@
#!/usr/bin/env stack
-- stack runghc --package reanimate
module Main(main) where
import Reanimate
import Reanimate.Builtin.Documentation
main :: IO ()
main = reanimate $ docEnv drawProgress

9
examples/doc_fromToS.hs Normal file
View file

@ -0,0 +1,9 @@
#!/usr/bin/env stack
-- stack runghc --package reanimate
module Main(main) where
import Reanimate
import Reanimate.Builtin.Documentation
main :: IO ()
main = reanimate $ docEnv $ signalA (fromToS 0.8 0.2) drawProgress

9
examples/doc_mapA.hs Normal file
View file

@ -0,0 +1,9 @@
#!/usr/bin/env stack
-- stack runghc --package reanimate
module Main(main) where
import Reanimate
import Reanimate.Builtin.Documentation
main :: IO ()
main = reanimate $ docEnv $ mapA (scale 0.5) drawCircle

View file

@ -0,0 +1,9 @@
#!/usr/bin/env stack
-- stack runghc --package reanimate
module Main(main) where
import Reanimate
import Reanimate.Builtin.Documentation
main :: IO ()
main = reanimate $ docEnv $ signalA oscillateS drawProgress

9
examples/doc_parA.hs Normal file
View file

@ -0,0 +1,9 @@
#!/usr/bin/env stack
-- stack runghc --package reanimate
module Main(main) where
import Reanimate
import Reanimate.Builtin.Documentation
main :: IO ()
main = reanimate $ docEnv $ drawBox `parA` adjustDuration (*2) drawCircle

9
examples/doc_parDropA.hs Normal file
View file

@ -0,0 +1,9 @@
#!/usr/bin/env stack
-- stack runghc --package reanimate
module Main(main) where
import Reanimate
import Reanimate.Builtin.Documentation
main :: IO ()
main = reanimate $ docEnv $ drawBox `parDropA` adjustDuration (*2) drawCircle

9
examples/doc_parLoopA.hs Normal file
View file

@ -0,0 +1,9 @@
#!/usr/bin/env stack
-- stack runghc --package reanimate
module Main(main) where
import Reanimate
import Reanimate.Builtin.Documentation
main :: IO ()
main = reanimate $ docEnv $ drawBox `parLoopA` adjustDuration (*2) drawCircle

9
examples/doc_pause.hs Normal file
View file

@ -0,0 +1,9 @@
#!/usr/bin/env stack
-- stack runghc --package reanimate
module Main(main) where
import Reanimate
import Reanimate.Builtin.Documentation
main :: IO ()
main = reanimate $ docEnv $ pause 1 `seqA` drawCircle

View file

@ -0,0 +1,9 @@
#!/usr/bin/env stack
-- stack runghc --package reanimate
module Main(main) where
import Reanimate
import Reanimate.Builtin.Documentation
main :: IO ()
main = reanimate $ docEnv $ pauseAround 1 1 drawProgress

View file

@ -0,0 +1,9 @@
#!/usr/bin/env stack
-- stack runghc --package reanimate
module Main(main) where
import Reanimate
import Reanimate.Builtin.Documentation
main :: IO ()
main = reanimate $ docEnv $ pauseAtBeginning 1 drawProgress

View file

@ -0,0 +1,9 @@
#!/usr/bin/env stack
-- stack runghc --package reanimate
module Main(main) where
import Reanimate
import Reanimate.Builtin.Documentation
main :: IO ()
main = reanimate $ docEnv $ pauseAtEnd 1 drawProgress

View file

@ -0,0 +1,9 @@
#!/usr/bin/env stack
-- stack runghc --package reanimate
module Main(main) where
import Reanimate
import Reanimate.Builtin.Documentation
main :: IO ()
main = reanimate $ docEnv $ playThenReverseA drawCircle

9
examples/doc_repeatA.hs Normal file
View file

@ -0,0 +1,9 @@
#!/usr/bin/env stack
-- stack runghc --package reanimate
module Main(main) where
import Reanimate
import Reanimate.Builtin.Documentation
main :: IO ()
main = reanimate $ docEnv $ repeatA 1.5 drawCircle

9
examples/doc_reverseA.hs Normal file
View file

@ -0,0 +1,9 @@
#!/usr/bin/env stack
-- stack runghc --package reanimate
module Main(main) where
import Reanimate
import Reanimate.Builtin.Documentation
main :: IO ()
main = reanimate $ docEnv $ reverseA drawCircle

9
examples/doc_reverseS.hs Normal file
View file

@ -0,0 +1,9 @@
#!/usr/bin/env stack
-- stack runghc --package reanimate
module Main(main) where
import Reanimate
import Reanimate.Builtin.Documentation
main :: IO ()
main = reanimate $ docEnv $ signalA reverseS drawProgress

9
examples/doc_seqA.hs Normal file
View file

@ -0,0 +1,9 @@
#!/usr/bin/env stack
-- stack runghc --package reanimate
module Main(main) where
import Reanimate
import Reanimate.Builtin.Documentation
main :: IO ()
main = reanimate $ docEnv (drawBox `seqA` drawCircle)

9
examples/doc_signalA.hs Normal file
View file

@ -0,0 +1,9 @@
#!/usr/bin/env stack
-- stack runghc --package reanimate
module Main(main) where
import Reanimate
import Reanimate.Builtin.Documentation
main :: IO ()
main = reanimate $ docEnv $ signalA (fromToS 0.25 0.75) drawCircle

View file

@ -20,13 +20,13 @@ waveMultiplier = 2 -- Square wave
main :: IO ()
main = reanimate $
fourierAnimation 1 `before`
fourierAnimation 2 `before`
fourierAnimation 3 `before`
fourierAnimation 5 `before`
fourierAnimation 10 `before`
fourierAnimation 25 `before`
fourierAnimation 50 `before`
fourierAnimation 1 `seqA`
fourierAnimation 2 `seqA`
fourierAnimation 3 `seqA`
fourierAnimation 5 `seqA`
fourierAnimation 10 `seqA`
fourierAnimation 25 `seqA`
fourierAnimation 50 `seqA`
fourierAnimation 100
sWidth :: Double
@ -34,7 +34,7 @@ sWidth = 0.02
fourierAnimation :: Int -> Animation
fourierAnimation nCircles = repeatA 2 $ mkAnimation 3 $ \t ->
let phi = signalFromTo 0 (2*pi) signalLinear t
let phi = fromToS 0 (2*pi) t
in mkGroup
[ mkBackground "black"
, translate (-screenWidth/4) 0 $ mkGroup

View file

@ -7,12 +7,7 @@ import Data.Complex
import qualified Data.Text as T
import Graphics.SvgTree
import Linear.V2
import Reanimate.Animation
import Reanimate.Constants
import Reanimate.Driver (reanimate)
import Reanimate.LaTeX
import Reanimate.Signal
import Reanimate.Svg
import Reanimate
main :: IO ()
main = reanimate $ pauseAtEnd 2 $
@ -34,7 +29,7 @@ fourierAnimation_ = mkAnimation 50 $ \t ->
let fLength = t
circles = setFourierLength (fLength*maxLength) piFourier
maxLength = sum $ map magnitude $ take 499 $ drop 1 $ fourierCoefficients piFourier
phi = signalFromTo 0 15 signalLinear t
phi = fromToS 0 15 t
in mkGroup
[ mkBackground "black"
, drawCircles $ fourierCoefficients $ rotateFourier phi circles

View file

@ -10,7 +10,7 @@ import Reanimate
main :: IO ()
main = reanimate $ playThenReverseA $ mkAnimation 5 $ \t ->
let s = signalFromTo 0 1.5 (signalCurve 2) t in
let s = fromToS 0 1.5 $ curveS 2 t in
mkGroup
[ mkBackground "black"
, FilterTree $ mkFilter "blur"

View file

@ -7,7 +7,7 @@ import Reanimate
main :: IO ()
main = reanimate $
bg `sim` (playThenReverseA $ drawText `andThen` fillText)
bg `parA` (playThenReverseA $ drawText `andThen` fillText)
where
bg = animate $ const $ mkBackground "black"
msg = "\\sum_{k=1}^\\infty {1 \\over k^2} = {\\pi^2 \\over 6}"

View file

@ -12,7 +12,7 @@ import Reanimate.Effect
import Reanimate.Scene
main :: IO ()
main = reanimate $ bg `sim` mainScene
main = reanimate $ bg `parA` mainScene
where
bg = animate $ const $ mkBackground "black"

View file

@ -88,6 +88,6 @@ starPolyShape = PolyShape $ G.ClosedPath
]
main :: IO ()
main = reanimate $ bg `sim` polygonTest
main = reanimate $ bg `parA` polygonTest
where
bg = animate $ const $ mkBackground "black"

View file

@ -9,7 +9,7 @@ import Reanimate.PolyShape
polygonTest :: Animation
polygonTest = mkAnimation 10 $ \t ->
let s = signalFromTo 0.5 (-0.5) signalLinear t
let s = fromToS 0.5 (-0.5) t
bigBox = head $ svgToPolyShapes $ pathify $
mkRect 2 2
smallBox = head $ svgToPolyShapes $ pathify $
@ -30,6 +30,6 @@ polygonTest = mkAnimation 10 $ \t ->
withStrokeColor "white"
main :: IO ()
main = reanimate $ bg `sim` polygonTest
main = reanimate $ bg `parA` polygonTest
where
bg = animate $ const $ mkBackground "black"

View file

@ -126,7 +126,7 @@ reorient :: Tree -> Tree
reorient = id -- scale 4 . translate 0 (-0.9)
main :: IO ()
main = reanimate $ bg `sim` mapA reorient (line `sim` mapA chunkPolyshapes test)
main = reanimate $ bg `parA` mapA reorient (line `parA` mapA chunkPolyshapes test)
where
bg = animate $ const $ mkBackground "black"
line = animate $ const $ withStrokeColor "white" $

View file

@ -18,30 +18,30 @@ import Reanimate.Svg
main :: IO ()
main = reanimate $ pauseAtEnd 5 $
curvesExample (\_ -> ([], "[]"))
`before`
`seqA`
curvesExample (\s ->
( [(1, signalFlat s)]
, "[(1, signalFlat " <> ppD s <> ")]"))
`before`
( [(1, constantS s)]
, "[(1, constantS " <> ppD s <> ")]"))
`seqA`
curvesExample (\s ->
( [(s, signalFlat 0), (1, signalLinear)]
, "[(" <> ppD s <> ", signalFlat 0), (1, signalLinear)]"))
`before`
( [(s, constantS 0), (1, id)]
, "[(" <> ppD s <> ", constantS 0), (1, id)]"))
`seqA`
curvesExample (\s ->
( [(s, signalFlat 1), (1, signalReverse signalLinear)]
, "[(" <> ppD s <> ", signalFlat 0), (1, signalReverse signalLinear)]"))
`before`
( [(s, constantS 1), (1, reverseS)]
, "[(" <> ppD s <> ", constantS 1), (1, reverseS)]"))
`seqA`
curvesExample (\s ->
( [(1, signalCurve (2+s*3))]
, "[(1, signalCurve "<> ppD (2+s*3) <>")]"))
`before`
( [(1, curveS (2+s*3))]
, "[(1, curveS "<> ppD (2+s*3) <>")]"))
`seqA`
curvesExample (\s ->
( [(1, signalFromTo s 1 $ signalCurve 5)]
, "[(1, signalFromTo "<> ppD s <>" 1 \\$ signalCurve 5)]"))
`before`
( [(1, fromToS s 1 . curveS 5)]
, "[(1, fromToS "<> ppD s <>" 1 \\$ curveS 5)]"))
`seqA`
curvesExample (\s ->
( [(1, signalBell (2+s*3))]
, "[(1, signalBell "<> ppD (2+s*3)<>")]"))
( [(1, bellS (2+s*3))]
, "[(1, bellS "<> ppD (2+s*3)<>")]"))
where
ppD s = pack (showFFloat (Just 2) s "")
@ -77,7 +77,7 @@ curvesExample gen = mkAnimation 2 $ \t ->
, translate (convertX $ 205) (convertX $ -5) $ scale 0.5 $ center $ latex "1"
, translate (convertX $ 100) (convertX $ -30)$ scale 0.6 $ center $ latex name ]
, withFillOpacity 0 $ withStrokeColor "green" $ -- withStrokeWidth 0.5 $
lowerTransformations $ scaleXY (convertX $ 200) (convertX $ (50)) $ mkSignalLine (signalFromList curveFns)
lowerTransformations $ scaleXY (convertX $ 200) (convertX $ (50)) $ mkSignalLine (fromListS curveFns)
]
]

View file

@ -192,7 +192,7 @@ reorient = scale 6 . translate 0 (-0.9)
-- unionPolyShapes
main :: IO ()
main = reanimate $ bg `sim` mapA reorient (line `sim` mapA chunkPolyshapes test)
main = reanimate $ bg `parA` mapA reorient (line `parA` mapA chunkPolyshapes test)
where
bg = animate $ const $ mkBackground "black"
line = animate $ const $ withStrokeColor "white" $

View file

@ -70,7 +70,7 @@ test = unsafePerformIO $ do
main :: IO ()
main = reanimate $ bg `sim` line `sim` test
main = reanimate $ bg `parA` line `parA` test
where
bg = animate $ const $ mkBackground "black"
line = animate $ const $ withStrokeColor "white" $

View file

@ -17,11 +17,11 @@ import System.Random.Shuffle
main :: IO ()
main = reanimate $
demonstrateAlgorithm "Bubble sort" bubbleSort `before`
demonstrateAlgorithm "Merge sort (left leaning)" mergeSort `before`
demonstrateAlgorithm "Merge sort" mergeSortUp `before`
demonstrateAlgorithm "Insertion sort" insertSort `before`
demonstrateAlgorithm "Selection sort" selectionSort `before`
demonstrateAlgorithm "Bubble sort" bubbleSort `seqA`
demonstrateAlgorithm "Merge sort (left leaning)" mergeSort `seqA`
demonstrateAlgorithm "Merge sort" mergeSortUp `seqA`
demonstrateAlgorithm "Insertion sort" insertSort `seqA`
demonstrateAlgorithm "Selection sort" selectionSort `seqA`
adjustDuration (*3) (demonstrateAlgorithm "Quicksort" quicksort)
demonstrateAlgorithm :: Text -> (forall s. S s ()) -> Animation

View file

@ -9,7 +9,7 @@ import Data.String.Here
main :: IO ()
main = reanimate $ mkAnimation 5 $ \t ->
let s = signalFromTo 0 360 signalLinear t in
let s = fromToS 0 360 t in
mkGroup
[ mkBackground "black"
, povray [] (script s) ]

View file

@ -14,8 +14,8 @@ import Reanimate
main :: IO ()
main = reanimate $ mkAnimation 10 $ \t ->
let n = signalFromTo 1 500 signalLinear t
rot = signalFromTo 0 45 signalLinear t
let n = fromToS 1 500 t
rot = fromToS 0 45 t
in mkGroup
[ mkBackground "black"
, rotate rot $ translate (-320/2) (-180/2)

View file

@ -17,7 +17,7 @@ import Reanimate.Svg
main :: IO ()
main = reanimate $ playThenReverseA $ mkAnimation 5 $ \t ->
let s = signalCurve 2 t in
let s = curveS 2 t in
mkGroup
[ mkBackground "black"
, scale (2/50) $ scaleXY 1 (-1) $

View file

@ -13,6 +13,7 @@ synopsis: Animation library based on SVGs.
homepage: https://github.com/Lemmih/reanimate
build-type: Simple
extra-source-files: ChangeLog.md
extra-doc-files: docs/gifs/*.gif
cabal-version: >=1.10
description:
@ -65,6 +66,7 @@ library
Reanimate.Constants
Reanimate.Chiphunk
Reanimate.PolyShape
Reanimate.Builtin.Documentation
other-modules: Reanimate.Cache
Reanimate.Driver.Check
Reanimate.Driver.CLI

View file

@ -25,15 +25,16 @@ module Reanimate
-- * Key commands for pausing, frame stepping, forward/rewind.
reanimate,
-- * Animations
SVG,
Animation(..),
mkAnimation,
animate,
duration,
-- ** Composition
before,
sim,
simLoop,
simDrop,
seqA,
parA,
parLoopA,
parDropA,
pause,
andThen,
mapA,
@ -46,16 +47,16 @@ module Reanimate
playThenReverseA,
repeatA,
freezeAtPercentage,
signalA,
-- ** Signals
Signal,
signalFlat,
signalLinear,
signalFromTo,
signalReverse,
signalCurve,
signalBell,
signalOscillate,
signalFromList,
constantS,
fromToS,
reverseS,
curveS,
bellS,
oscillateS,
fromListS,
-- * SVG
module Reanimate.Svg.Constructors,
module Reanimate.Svg.LineCommand,

View file

@ -8,6 +8,7 @@ import Graphics.SvgTree (Document (..), Number (..),
import Graphics.SvgTree.Printer
import Reanimate.Constants
import Reanimate.Svg.Constructors
import Reanimate.Signal
import Text.XML.Light.Output
-- | Duration of an animation or effect. Usually measured in seconds.
@ -32,9 +33,15 @@ duration :: Animation -> Duration
duration (Animation d _) = d
-- | Play animations in sequence. The @lhs@ animation is removed after it has
-- completed. New animation duration is @duration lhs + duration rhs@.
before :: Animation -> Animation -> Animation
before (Animation d1 f1) (Animation d2 f2) =
-- completed. New animation duration is '@duration lhs + duration rhs@'.
--
-- Example:
--
-- > drawBox `seqA` drawCircle
--
-- <<docs/gifs/doc_seqA.gif>>
seqA :: Animation -> Animation -> Animation
seqA (Animation d1 f1) (Animation d2 f2) =
Animation totalD $ \t ->
if t < d1/totalD
then f1 (t * totalD/d1)
@ -43,9 +50,15 @@ before (Animation d1 f1) (Animation d2 f2) =
totalD = d1+d2
-- | Play two animation concurrently. Shortest animation freezes on last frame.
-- New animation duration is @max (duration lhs) (duration rhs)@.
sim :: Animation -> Animation -> Animation
sim (Animation d1 f1) (Animation d2 f2) =
-- New animation duration is '@max (duration lhs) (duration rhs)@'.
--
-- Example:
--
-- > drawBox `parA` adjustDuration (*2) drawCircle
--
-- <<docs/gifs/doc_parA.gif>>
parA :: Animation -> Animation -> Animation
parA (Animation d1 f1) (Animation d2 f2) =
Animation (max d1 d2) $ \t ->
let t1 = t * totalD/d1
t2 = t * totalD/d2 in
@ -54,10 +67,17 @@ sim (Animation d1 f1) (Animation d2 f2) =
, f2 (min 1 t2) ]
where
totalD = max d1 d2
-- | Play two animation concurrently. Shortest animation loops.
-- New animation duration is @max (duration lhs) (duration rhs)@.
simLoop :: Animation -> Animation -> Animation
simLoop (Animation d1 f1) (Animation d2 f2) =
-- New animation duration is '@max (duration lhs) (duration rhs)@'.
--
-- Example:
--
-- > drawBox `parLoopA` adjustDuration (*2) drawCircle
--
-- <<docs/gifs/doc_parLoopA.gif>>
parLoopA :: Animation -> Animation -> Animation
parLoopA (Animation d1 f1) (Animation d2 f2) =
Animation totalD $ \t ->
let t1 = t * totalD/d1
t2 = t * totalD/d2 in
@ -68,9 +88,15 @@ simLoop (Animation d1 f1) (Animation d2 f2) =
totalD = max d1 d2
-- | Play two animation concurrently. Animations disappear after playing once.
-- New animation duration is @max (duration lhs) (duration rhs)@.
simDrop :: Animation -> Animation -> Animation
simDrop (Animation d1 f1) (Animation d2 f2) =
-- New animation duration is '@max (duration lhs) (duration rhs)@'.
--
-- Example:
--
-- > drawBox `parLoopA` adjustDuration (*2) drawCircle
--
-- <<docs/gifs/doc_parDropA.gif>>
parDropA :: Animation -> Animation -> Animation
parDropA (Animation d1 f1) (Animation d2 f2) =
Animation totalD $ \t ->
let t1 = t * totalD/d1
t2 = t * totalD/d2 in
@ -81,13 +107,25 @@ simDrop (Animation d1 f1) (Animation d2 f2) =
totalD = max d1 d2
-- | Empty animation (no SVG output) with a fixed duration.
pause :: Double -> Animation
--
-- Example:
--
-- > pause 1 `seqA` drawCircle
--
-- <<docs/gifs/doc_pause.gif>>
pause :: Duration -> Animation
pause d = Animation d (const None)
-- | Play left animation and freeze on the last frame, then play the right
-- animation. New duration is @duration lhs + duration rhs@.
-- animation. New duration is '@duration lhs + duration rhs@'.
--
-- Example:
--
-- > drawBox `andThen` drawCircle
--
-- <<docs/gifs/doc_andThen.gif>>
andThen :: Animation -> Animation -> Animation
andThen a b = a `sim` (pause (duration a) `before` b)
andThen a b = a `parA` (pause (duration a) `seqA` b)
frameAt :: Double -> Animation -> Tree
frameAt t (Animation d f) = f t'
@ -114,19 +152,44 @@ renderSvg w h t = ppDocument doc
}
-- | Map over the SVG produced by an animation at every frame.
--
-- Example:
--
-- > mapA (scale 0.5) drawCircle
--
-- <<docs/gifs/doc_mapA.gif>>
mapA :: (Tree -> Tree) -> Animation -> Animation
mapA fn (Animation d f) = Animation d (fn . f)
-- | Freeze the last frame for @t@ seconds at the end of the animation.
--
-- Example:
--
-- > pauseAtEnd 1 drawProgress
--
-- <<docs/gifs/doc_pauseAtEnd.gif>>
pauseAtEnd :: Duration -> Animation -> Animation
pauseAtEnd t a = a `andThen` pause t
-- | Freeze the first frame for @t@ seconds at the beginning of the animation.
--
-- Example:
--
-- > pauseAtBeginning 1 drawProgress
--
-- <<docs/gifs/doc_pauseAtBeginning.gif>>
pauseAtBeginning :: Duration -> Animation -> Animation
pauseAtBeginning t a =
Animation t (freezeFrame 0 a) `before` a
Animation t (freezeFrame 0 a) `seqA` a
-- | Freeze the first and the last frame of the animation for a specified duration.
--
-- Example:
--
-- > pauseAround 1 1 drawProgress
--
-- <<docs/gifs/doc_pauseAround.gif>>
pauseAround :: Duration -> Duration -> Animation -> Animation
pauseAround start end = pauseAtEnd end . pauseAtBeginning start
@ -145,23 +208,51 @@ adjustDuration fn (Animation d gen) =
setDuration :: Duration -> Animation -> Animation
setDuration newD = adjustDuration (const newD)
-- | Play an animation in reverse. Duration remains unchanged.
-- | Play an animation in reverse. Duration remains unchanged. Shorthand for:
-- @'signalA' 'reverseS'@.
--
-- Example:
--
-- > reverseA drawCircle
--
-- <<docs/gifs/doc_reverseA.gif>>
reverseA :: Animation -> Animation
reverseA (Animation d fn) = Animation d $ \t ->
fn (1-t)
reverseA = signalA reverseS
-- | Play animation before playing it again in reverse. Duration is twice
-- the duration of the input.
--
-- Example:
--
-- > playThenReverseA drawCircle
--
-- <<docs/gifs/doc_playThenReverseA.gif>>
playThenReverseA :: Animation -> Animation
playThenReverseA a = a `before` reverseA a
playThenReverseA a = a `seqA` reverseA a
-- | Loop animation @n@ number of times. This number may be fractional and it
-- may be less than 1. It must be greater than or equal to 0, though.
-- New duration is @n*duration input@.
--
-- Example:
--
-- > repeatA 1.5 drawCircle
--
-- <<docs/gifs/doc_repeatA.gif>>
repeatA :: Double -> Animation -> Animation
repeatA n (Animation d f) = Animation (d*n) $ \t ->
f (t `mod'` recip n)
f ((t*n) `mod'` 1)
freezeAtPercentage :: Time -> Animation -> Animation
freezeAtPercentage frac (Animation d genFrame) =
Animation d $ const $ genFrame frac
-- | Modify the time component of an animation. Animation duration is unchanged.
--
-- Example:
--
-- > signalA (fromToS 0.25 0.75) drawCircle
--
-- <<docs/gifs/doc_signalA.gif>>
signalA :: Signal -> Animation -> Animation
signalA fn (Animation d gen) = Animation d $ gen . fn

View file

@ -0,0 +1,35 @@
module Reanimate.Builtin.Documentation where
import Reanimate.Animation
import Reanimate.Svg
import Reanimate.Constants
docEnv :: Animation -> Animation
docEnv = mapA $ \svg -> mkGroup
[ mkBackground "white"
, withFillOpacity 0 $
withStrokeWidth 0.1 $
withStrokeColor "black" svg ]
-- | <<docs/gifs/doc_drawBox.gif>>
drawBox :: Animation
drawBox = mkAnimation 2 $ \t ->
partialSvg t $ pathify $
mkRect (screenWidth/2) (screenHeight/2)
-- | <<docs/gifs/doc_drawCircle.gif>>
drawCircle :: Animation
drawCircle = mkAnimation 2 $ \t ->
partialSvg t $ pathify $
mkCircle (screenHeight/3)
-- | <<docs/gifs/doc_drawProgress.gif>>
drawProgress :: Animation
drawProgress = mkAnimation 2 $ \t ->
mkGroup
[ mkLine (-screenWidth/2*widthP,0)
(screenWidth/2*widthP,0)
, translate (-screenWidth/2*widthP + screenWidth*widthP*t) 0 $
withFillOpacity 1 $ mkCircle 0.5 ]
where
widthP = 0.8

View file

@ -1,19 +1,20 @@
module Reanimate.Signal
( Signal
, signalFlat
, signalLinear
, signalFromTo
, signalReverse
, signalCurve
, signalBell
, signalOscillate
, signalFromList
, constantS
, fromToS
, reverseS
, curveS
, bellS
, oscillateS
, fromListS
) where
-- | Signals are time-varying variables. Signals can be composed using function
-- composition.
type Signal = Double -> Double
signalFromList :: [(Double, Signal)] -> Signal
signalFromList fns t = worker 0 fns
fromListS :: [(Double, Signal)] -> Signal
fromListS fns t = worker 0 fns
where
worker _ [] = 0
worker now [(len, fn)] = fn (min 1 ((t-now) / min (1-now) len))
@ -21,29 +22,68 @@ signalFromList fns t = worker 0 fns
| now+len < t = worker (now+len) rest
| otherwise = fn ((t-now) / len)
signalFlat :: Double -> Signal
signalFlat x = const x
-- | Constant signal.
--
-- Example:
--
-- > signalA (constantS 0.5) drawProgress
--
-- <<docs/gifs/doc_constantS.gif>>
constantS :: Double -> Signal
constantS x = const x
signalLinear :: Signal
signalLinear = id
-- | Signal with new starting and end values.
--
-- Example:
--
-- > signalA (fromToS 0.8 0.2) drawProgress
--
-- <<docs/gifs/doc_fromToS.gif>>
fromToS :: Double -> Double -> Signal
fromToS from to t = from + (to-from)*t
signalFromTo :: Double -> Double -> Signal -> Signal
signalFromTo from to c t = from + (to-from)*(c t)
-- | Reverse signal order.
--
-- Example:
--
-- > signalA reverseS drawProgress
--
-- <<docs/gifs/doc_reverseS.gif>>
reverseS :: Signal
reverseS t = 1-t
signalReverse :: Signal -> Signal
signalReverse fn t = fn (1-t)
signalCurve :: Double -> Signal
signalCurve steepness s =
-- | S-curve signal. Takes a steepness parameter. 2 is a good default.
--
-- Example:
--
-- > signalA (curveS 2) drawProgress
--
-- <<docs/gifs/doc_curveS.gif>>
curveS :: Double -> Signal
curveS steepness s =
if s < 0.5
then 0.5 * (2*s)**steepness
else 1-0.5 * (2 - 2*s)**steepness
signalOscillate :: Signal -> Signal
signalOscillate fn t =
-- | Oscillate signal.
--
-- Example:
--
-- > signalA oscillateS drawProgress
--
-- <<docs/gifs/doc_oscillateS.gif>>
oscillateS :: Signal
oscillateS t =
if t < 1/2
then fn (t*2)
else fn (2-t*2)
then t*2
else 2-t*2
signalBell :: Double -> Signal
signalBell = signalOscillate . signalCurve
-- | Bell-curve signal. Takes a steepness parameter. 2 is a good default.
--
-- Example:
--
-- > signalA (bellS 2) drawProgress
--
-- <<docs/gifs/doc_bellS.gif>>
bellS :: Double -> Signal
bellS steepness = curveS steepness . oscillateS

View file

@ -29,7 +29,7 @@ run out.
-}
main :: IO ()
main = reanimate $ pauseAtEnd 2
(animate $ const $ mkBackground "black") `sim`
(animate $ const $ mkBackground "black") `parA`
drawBox
drawBox :: Animation
@ -38,9 +38,9 @@ drawBox = mkAnimation 5 $ \t ->
[ withFillColor "white" $
translate 0 (-70) $
scale 2 $ center $ latex "Baker's Algorithm"
, let s = signalFromList [(0.7, signalFlat 0), (1, signalLinear)] t
d = signalFromList [(0.7, signalFlat 0), (1, signalLinear)] t
draw = signalFromList [(0.5, signalLinear), (1, signalFlat 1)] t
, let s = fromListS [(0.7, constantS 0), (1, id)] t
d = fromListS [(0.7, constantS 0), (1, id)] t
draw = fromListS [(0.5, id), (1, constantS 1)] t
mlc = MemoryLineChart
{ mlcWidth = 230
, mlcHeight = 50 + s*50
@ -59,7 +59,7 @@ highlightBox = mkAnimation 2 $ \t ->
boxY = negate mlcHeight / 2
mlcWidth = 230
mlcHeight = 50
s = signalFromList [(0.0, signalFlat 0), (1, signalBell 2)] t
s = fromListS [(0.0, constantS 0), (1, bellS 2)] t
in
withStrokeColor "white" $
withStrokeWidth (0.5 + s) $

View file

@ -23,7 +23,7 @@ import System.Random
import System.Random.Shuffle
fixed :: Tree -> Animation -> Animation
fixed svg ani = animate (const svg) `sim` ani
fixed svg ani = animate (const svg) `parA` ani
digitWidth = 25
digitCount = 10
@ -53,7 +53,7 @@ main = reanimate $ fixed bg $ pauseAtEnd 1 $
-- msg = "Eve"
glyphs = lowerTransformations $ scale 3 $ pathify $ center $ latexAlign msg
fillText = mkAnimation 1 $ \t ->
let sat = signalFromTo 0 0.7 signalLinear t in
let sat = fromToS 0 0.7 t in
withFillColor "white" $ withStrokeColor "white" $ withStrokeWidth (0.4 * (1-t)) $
withFillOpacity t glyphs
-- withSubglyphs [0] (withFillColorPixel $ toRGBString sat 0.0) $
@ -146,7 +146,7 @@ renderSortElement SortElement{..} t
| t > sortElementStartTime + sortElementDuration =
translate (fromIntegral sortElementEndPosition * digitWidth) 0 sortElementTree
| otherwise =
let pos = signalCurve 2 $ (t - sortElementStartTime) / sortElementDuration
let pos = curveS 2 $ (t - sortElementStartTime) / sortElementDuration
from = sortElementStartPosition
to = sortElementEndPosition
linear = fromIntegral from + (fromIntegral (to-from))*pos