From dfd52961aea3a7e468dafa474aa9b6232f5747d1 Mon Sep 17 00:00:00 2001 From: David Himmelstrup Date: Sat, 28 Sep 2019 21:35:42 +0800 Subject: [PATCH] Use gifs to improve documentation. (#21) Former-commit-id: 40e5ccedcf5d1ca8f0d5cc373ba3b3c4cc2d6c6a --- docs/gen_doc_gifs.sh | 11 ++ docs/{render_all.sh => gen_gallery.sh} | 9 +- examples/boundingbox.hs | 4 +- examples/colormaps.hs | 4 +- examples/counter.hs | 2 +- examples/doc_andThen.hs | 9 ++ examples/doc_bellS.hs | 9 ++ examples/doc_constantS.hs | 9 ++ examples/doc_curveS.hs | 9 ++ examples/doc_drawBox.hs | 9 ++ examples/doc_drawCircle.hs | 9 ++ examples/doc_drawProgress.hs | 9 ++ examples/doc_fromToS.hs | 9 ++ examples/doc_mapA.hs | 9 ++ examples/doc_oscillateS.hs | 9 ++ examples/doc_parA.hs | 9 ++ examples/doc_parDropA.hs | 9 ++ examples/doc_parLoopA.hs | 9 ++ examples/doc_pause.hs | 9 ++ examples/doc_pauseAround.hs | 9 ++ examples/doc_pauseAtBeginning.hs | 9 ++ examples/doc_pauseAtEnd.hs | 9 ++ examples/doc_playThenReverseA.hs | 9 ++ examples/doc_repeatA.hs | 9 ++ examples/doc_reverseA.hs | 9 ++ examples/doc_reverseS.hs | 9 ++ examples/doc_seqA.hs | 9 ++ examples/doc_signalA.hs | 9 ++ examples/fourier.hs | 16 +-- examples/fourier_draw.hs | 9 +- examples/goo.hs | 2 +- examples/latex_draw.hs | 2 +- examples/latex_wheel.hs | 2 +- examples/polyshape_test1.hs | 2 +- examples/polyshape_test2.hs | 4 +- examples/shatter.hs | 2 +- examples/signals.hs | 38 ++--- examples/simulate_equation.hs | 2 +- examples/simulate_gravity.hs | 2 +- examples/sorting.hs | 10 +- examples/sphere.hs | 2 +- examples/sunflower.hs | 4 +- examples/tangent_and_normal.hs | 2 +- reanimate.cabal | 2 + src/Reanimate.hs | 25 ++-- src/Reanimate/Animation.hs | 133 +++++++++++++++--- src/Reanimate/Builtin/Documentation.hs | 35 +++++ src/Reanimate/Signal.hs | 94 +++++++++---- videos/bakers-algorithm/bakers-algorithm.hs | 10 +- .../sorting-algorithms/sorting-algorithms.hs | 6 +- 50 files changed, 512 insertions(+), 129 deletions(-) create mode 100755 docs/gen_doc_gifs.sh rename docs/{render_all.sh => gen_gallery.sh} (84%) create mode 100644 examples/doc_andThen.hs create mode 100644 examples/doc_bellS.hs create mode 100644 examples/doc_constantS.hs create mode 100644 examples/doc_curveS.hs create mode 100644 examples/doc_drawBox.hs create mode 100644 examples/doc_drawCircle.hs create mode 100644 examples/doc_drawProgress.hs create mode 100644 examples/doc_fromToS.hs create mode 100644 examples/doc_mapA.hs create mode 100644 examples/doc_oscillateS.hs create mode 100644 examples/doc_parA.hs create mode 100644 examples/doc_parDropA.hs create mode 100644 examples/doc_parLoopA.hs create mode 100644 examples/doc_pause.hs create mode 100644 examples/doc_pauseAround.hs create mode 100644 examples/doc_pauseAtBeginning.hs create mode 100644 examples/doc_pauseAtEnd.hs create mode 100644 examples/doc_playThenReverseA.hs create mode 100644 examples/doc_repeatA.hs create mode 100644 examples/doc_reverseA.hs create mode 100644 examples/doc_reverseS.hs create mode 100644 examples/doc_seqA.hs create mode 100644 examples/doc_signalA.hs create mode 100644 src/Reanimate/Builtin/Documentation.hs diff --git a/docs/gen_doc_gifs.sh b/docs/gen_doc_gifs.sh new file mode 100755 index 0000000..376b759 --- /dev/null +++ b/docs/gen_doc_gifs.sh @@ -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 diff --git a/docs/render_all.sh b/docs/gen_gallery.sh similarity index 84% rename from docs/render_all.sh rename to docs/gen_gallery.sh index 7dc88ef..4b8a93c 100755 --- a/docs/render_all.sh +++ b/docs/gen_gallery.sh @@ -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
diff --git a/examples/boundingbox.hs b/examples/boundingbox.hs index a4f4c89..c0159e1 100755 --- a/examples/boundingbox.hs +++ b/examples/boundingbox.hs @@ -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" diff --git a/examples/colormaps.hs b/examples/colormaps.hs index db0dcb9..96b47d2 100755 --- a/examples/colormaps.hs +++ b/examples/colormaps.hs @@ -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 diff --git a/examples/counter.hs b/examples/counter.hs index f86130e..f5a0a1b 100755 --- a/examples/counter.hs +++ b/examples/counter.hs @@ -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 diff --git a/examples/doc_andThen.hs b/examples/doc_andThen.hs new file mode 100644 index 0000000..101a4c4 --- /dev/null +++ b/examples/doc_andThen.hs @@ -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 diff --git a/examples/doc_bellS.hs b/examples/doc_bellS.hs new file mode 100644 index 0000000..991ee0f --- /dev/null +++ b/examples/doc_bellS.hs @@ -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 diff --git a/examples/doc_constantS.hs b/examples/doc_constantS.hs new file mode 100644 index 0000000..f3a8711 --- /dev/null +++ b/examples/doc_constantS.hs @@ -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 diff --git a/examples/doc_curveS.hs b/examples/doc_curveS.hs new file mode 100644 index 0000000..7c5ec53 --- /dev/null +++ b/examples/doc_curveS.hs @@ -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 diff --git a/examples/doc_drawBox.hs b/examples/doc_drawBox.hs new file mode 100644 index 0000000..998bed4 --- /dev/null +++ b/examples/doc_drawBox.hs @@ -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 diff --git a/examples/doc_drawCircle.hs b/examples/doc_drawCircle.hs new file mode 100644 index 0000000..9d00231 --- /dev/null +++ b/examples/doc_drawCircle.hs @@ -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 diff --git a/examples/doc_drawProgress.hs b/examples/doc_drawProgress.hs new file mode 100644 index 0000000..011e47c --- /dev/null +++ b/examples/doc_drawProgress.hs @@ -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 diff --git a/examples/doc_fromToS.hs b/examples/doc_fromToS.hs new file mode 100644 index 0000000..0b0dfeb --- /dev/null +++ b/examples/doc_fromToS.hs @@ -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 diff --git a/examples/doc_mapA.hs b/examples/doc_mapA.hs new file mode 100644 index 0000000..d1c2010 --- /dev/null +++ b/examples/doc_mapA.hs @@ -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 diff --git a/examples/doc_oscillateS.hs b/examples/doc_oscillateS.hs new file mode 100644 index 0000000..32f713e --- /dev/null +++ b/examples/doc_oscillateS.hs @@ -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 diff --git a/examples/doc_parA.hs b/examples/doc_parA.hs new file mode 100644 index 0000000..0f5fa8e --- /dev/null +++ b/examples/doc_parA.hs @@ -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 diff --git a/examples/doc_parDropA.hs b/examples/doc_parDropA.hs new file mode 100644 index 0000000..9be14c6 --- /dev/null +++ b/examples/doc_parDropA.hs @@ -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 diff --git a/examples/doc_parLoopA.hs b/examples/doc_parLoopA.hs new file mode 100644 index 0000000..a31f3c4 --- /dev/null +++ b/examples/doc_parLoopA.hs @@ -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 diff --git a/examples/doc_pause.hs b/examples/doc_pause.hs new file mode 100644 index 0000000..b18a2f0 --- /dev/null +++ b/examples/doc_pause.hs @@ -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 diff --git a/examples/doc_pauseAround.hs b/examples/doc_pauseAround.hs new file mode 100644 index 0000000..b8abafd --- /dev/null +++ b/examples/doc_pauseAround.hs @@ -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 diff --git a/examples/doc_pauseAtBeginning.hs b/examples/doc_pauseAtBeginning.hs new file mode 100644 index 0000000..a446b82 --- /dev/null +++ b/examples/doc_pauseAtBeginning.hs @@ -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 diff --git a/examples/doc_pauseAtEnd.hs b/examples/doc_pauseAtEnd.hs new file mode 100644 index 0000000..c45549a --- /dev/null +++ b/examples/doc_pauseAtEnd.hs @@ -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 diff --git a/examples/doc_playThenReverseA.hs b/examples/doc_playThenReverseA.hs new file mode 100644 index 0000000..65ebf9b --- /dev/null +++ b/examples/doc_playThenReverseA.hs @@ -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 diff --git a/examples/doc_repeatA.hs b/examples/doc_repeatA.hs new file mode 100644 index 0000000..18bcdb5 --- /dev/null +++ b/examples/doc_repeatA.hs @@ -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 diff --git a/examples/doc_reverseA.hs b/examples/doc_reverseA.hs new file mode 100644 index 0000000..29eb1ad --- /dev/null +++ b/examples/doc_reverseA.hs @@ -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 diff --git a/examples/doc_reverseS.hs b/examples/doc_reverseS.hs new file mode 100644 index 0000000..3428d6f --- /dev/null +++ b/examples/doc_reverseS.hs @@ -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 diff --git a/examples/doc_seqA.hs b/examples/doc_seqA.hs new file mode 100644 index 0000000..1fd2e36 --- /dev/null +++ b/examples/doc_seqA.hs @@ -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) diff --git a/examples/doc_signalA.hs b/examples/doc_signalA.hs new file mode 100644 index 0000000..4d422d7 --- /dev/null +++ b/examples/doc_signalA.hs @@ -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 diff --git a/examples/fourier.hs b/examples/fourier.hs index 9d9037c..6a4eb01 100755 --- a/examples/fourier.hs +++ b/examples/fourier.hs @@ -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 diff --git a/examples/fourier_draw.hs b/examples/fourier_draw.hs index f2a703c..bfa52bd 100755 --- a/examples/fourier_draw.hs +++ b/examples/fourier_draw.hs @@ -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 diff --git a/examples/goo.hs b/examples/goo.hs index 119492c..34a5933 100755 --- a/examples/goo.hs +++ b/examples/goo.hs @@ -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" diff --git a/examples/latex_draw.hs b/examples/latex_draw.hs index cace4ff..521701d 100755 --- a/examples/latex_draw.hs +++ b/examples/latex_draw.hs @@ -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}" diff --git a/examples/latex_wheel.hs b/examples/latex_wheel.hs index 4ef24f5..a33efc7 100644 --- a/examples/latex_wheel.hs +++ b/examples/latex_wheel.hs @@ -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" diff --git a/examples/polyshape_test1.hs b/examples/polyshape_test1.hs index ea15eb0..f3a6ca7 100755 --- a/examples/polyshape_test1.hs +++ b/examples/polyshape_test1.hs @@ -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" diff --git a/examples/polyshape_test2.hs b/examples/polyshape_test2.hs index f488a1b..5f8aaae 100755 --- a/examples/polyshape_test2.hs +++ b/examples/polyshape_test2.hs @@ -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" diff --git a/examples/shatter.hs b/examples/shatter.hs index 1bf2d23..ed84241 100755 --- a/examples/shatter.hs +++ b/examples/shatter.hs @@ -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" $ diff --git a/examples/signals.hs b/examples/signals.hs index b0dbfe2..ed41c3c 100755 --- a/examples/signals.hs +++ b/examples/signals.hs @@ -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) ] ] diff --git a/examples/simulate_equation.hs b/examples/simulate_equation.hs index a7718a3..171d43b 100755 --- a/examples/simulate_equation.hs +++ b/examples/simulate_equation.hs @@ -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" $ diff --git a/examples/simulate_gravity.hs b/examples/simulate_gravity.hs index 5a2e25a..6af3fed 100755 --- a/examples/simulate_gravity.hs +++ b/examples/simulate_gravity.hs @@ -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" $ diff --git a/examples/sorting.hs b/examples/sorting.hs index ebca9cd..7fd9ba4 100755 --- a/examples/sorting.hs +++ b/examples/sorting.hs @@ -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 diff --git a/examples/sphere.hs b/examples/sphere.hs index d42f6e8..c437829 100755 --- a/examples/sphere.hs +++ b/examples/sphere.hs @@ -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) ] diff --git a/examples/sunflower.hs b/examples/sunflower.hs index da19b80..a01a220 100755 --- a/examples/sunflower.hs +++ b/examples/sunflower.hs @@ -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) diff --git a/examples/tangent_and_normal.hs b/examples/tangent_and_normal.hs index 872fa0f..6c5d5cc 100755 --- a/examples/tangent_and_normal.hs +++ b/examples/tangent_and_normal.hs @@ -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) $ diff --git a/reanimate.cabal b/reanimate.cabal index 74ce557..476cf28 100644 --- a/reanimate.cabal +++ b/reanimate.cabal @@ -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 diff --git a/src/Reanimate.hs b/src/Reanimate.hs index 714fe8d..2b52d05 100644 --- a/src/Reanimate.hs +++ b/src/Reanimate.hs @@ -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, diff --git a/src/Reanimate/Animation.hs b/src/Reanimate/Animation.hs index 5d8de3e..400f21d 100644 --- a/src/Reanimate/Animation.hs +++ b/src/Reanimate/Animation.hs @@ -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 +-- +-- <> +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 +-- +-- <> +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 +-- +-- <> +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 +-- +-- <> +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 +-- +-- <> +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 +-- +-- <> 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 +-- +-- <> + 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 +-- +-- <> 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 +-- +-- <> 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 +-- +-- <> 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 +-- +-- <> 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 +-- +-- <> 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 +-- +-- <> 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 +-- +-- <> +signalA :: Signal -> Animation -> Animation +signalA fn (Animation d gen) = Animation d $ gen . fn diff --git a/src/Reanimate/Builtin/Documentation.hs b/src/Reanimate/Builtin/Documentation.hs new file mode 100644 index 0000000..6909c92 --- /dev/null +++ b/src/Reanimate/Builtin/Documentation.hs @@ -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 ] + +-- | <> +drawBox :: Animation +drawBox = mkAnimation 2 $ \t -> + partialSvg t $ pathify $ + mkRect (screenWidth/2) (screenHeight/2) + +-- | <> +drawCircle :: Animation +drawCircle = mkAnimation 2 $ \t -> + partialSvg t $ pathify $ + mkCircle (screenHeight/3) + +-- | <> +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 diff --git a/src/Reanimate/Signal.hs b/src/Reanimate/Signal.hs index eb167b9..f9f61d7 100644 --- a/src/Reanimate/Signal.hs +++ b/src/Reanimate/Signal.hs @@ -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 +-- +-- <> +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 +-- +-- <> +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 +-- +-- <> +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 +-- +-- <> +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 +-- +-- <> +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 +-- +-- <> +bellS :: Double -> Signal +bellS steepness = curveS steepness . oscillateS diff --git a/videos/bakers-algorithm/bakers-algorithm.hs b/videos/bakers-algorithm/bakers-algorithm.hs index e3d7c14..4b787c4 100755 --- a/videos/bakers-algorithm/bakers-algorithm.hs +++ b/videos/bakers-algorithm/bakers-algorithm.hs @@ -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) $ diff --git a/videos/sorting-algorithms/sorting-algorithms.hs b/videos/sorting-algorithms/sorting-algorithms.hs index eabadd1..38a7fb9 100755 --- a/videos/sorting-algorithms/sorting-algorithms.hs +++ b/videos/sorting-algorithms/sorting-algorithms.hs @@ -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