diff --git a/haddock.txt b/haddock.txt index daaadb7..354fe73 100644 --- a/haddock.txt +++ b/haddock.txt @@ -1,24 +1,24 @@ 100% ( 55 / 55) in 'Reanimate.Svg.Constructors' + 100% ( 41 / 41) in 'Reanimate.Animation' 100% ( 40 / 40) in 'Reanimate.GeoProjection' 100% ( 22 / 22) in 'Reanimate.Effect' 100% ( 17 / 17) in 'Reanimate.Parameters' + 100% ( 14 / 14) in 'Reanimate.Raster' 100% ( 13 / 13) in 'Reanimate.ColorMap' 100% ( 12 / 12) in 'Reanimate.ColorComponents' 100% ( 9 / 9) in 'Reanimate.Voice' 100% ( 9 / 9) in 'Reanimate.Povray' 100% ( 9 / 9) in 'Reanimate.Constants' 100% ( 8 / 8) in 'Reanimate.Builtin.TernaryPlot' + 100% ( 7 / 7) in 'Reanimate.Builtin.Documentation' 100% ( 6 / 6) in 'Reanimate.Builtin.Images' 100% ( 5 / 5) in 'Reanimate.Transform' 100% ( 4 / 4) in 'Reanimate.Svg.BoundingBox' 100% ( 3 / 3) in 'Reanimate.Blender' 99% (155 /156) in 'Reanimate' - 95% ( 40 / 42) in 'Reanimate.Animation' - 93% ( 13 / 14) in 'Reanimate.Raster' 91% ( 10 / 11) in 'Reanimate.Ease' 88% ( 7 / 8) in 'Reanimate.Transition' 81% ( 92 /114) in 'Reanimate.Scene' - 75% ( 6 / 8) in 'Reanimate.Builtin.Documentation' 75% ( 3 / 4) in 'Reanimate.Svg.Unuse' 62% ( 23 / 37) in 'Reanimate.Internal.CubicBezier' 56% ( 10 / 18) in 'Reanimate.Svg' @@ -34,7 +34,6 @@ 0% ( 0 / 13) in 'Reanimate.Morph.Common' 0% ( 0 / 10) in 'Reanimate.ColorSpace' 0% ( 0 / 8) in 'Reanimate.Misc' - 0% ( 0 / 8) in 'Reanimate.Math.Balloon' 0% ( 0 / 7) in 'Reanimate.Morph.Linear' 0% ( 0 / 7) in 'Reanimate.Math.Visibility' 0% ( 0 / 6) in 'Reanimate.Math.Triangulate' @@ -43,4 +42,5 @@ 0% ( 0 / 4) in 'Reanimate.Debug' 0% ( 0 / 3) in 'Reanimate.Morph.Rotational' 0% ( 0 / 3) in 'Reanimate.Memo' + 0% ( 0 / 3) in 'Reanimate.Math.Balloon' 0% ( 0 / 2) in 'Reanimate.Morph.Cache' diff --git a/haddock_badge.json b/haddock_badge.json index 2db4f48..c1aeaf0 100644 --- a/haddock_badge.json +++ b/haddock_badge.json @@ -1 +1 @@ - { "schemaVersion": 1, "label": "api docs", "message": "68%", "color": "success" } + { "schemaVersion": 1, "label": "api docs", "message": "70%", "color": "success" } diff --git a/hpc_index.html b/hpc_index.html index 54dc114..da8b21e 100644 --- a/hpc_index.html +++ b/hpc_index.html @@ -11,10 +11,10 @@ table.dashboard { border-collapse: collapse ; border: solid 1px black } 20%3/15
- 0/0 20%12/58
  module reanimate-0.4.1.0-inplace/Reanimate.Animation -87%28/32
57%8/14
85%298/347
+90%28/31
57%8/14
87%298/341
  module reanimate-0.4.1.0-inplace/Reanimate.Builtin.Documentation -85%6/7
- 0/0 87%117/134
+100%6/6
- 0/0 100%117/117
  module reanimate-0.4.1.0-inplace/Reanimate.Builtin.Images 57%4/7
25%1/4
48%24/49
@@ -128,5 +128,5 @@ table.dashboard { border-collapse: collapse ; border: solid 1px black } 100%6/6
50%1/2
95%43/45
  Program Coverage Total -30%247/805
15%131/833
29%4647/15897
+30%247/803
15%131/833
29%4647/15874
diff --git a/hpc_index_alt.html b/hpc_index_alt.html index 5f536ca..043aa16 100644 --- a/hpc_index_alt.html +++ b/hpc_index_alt.html @@ -20,7 +20,7 @@ table.dashboard { border-collapse: collapse ; border: solid 1px black } 75%12/16
58%43/74
69%659/943
  module reanimate-0.4.1.0-inplace/Reanimate.Animation -87%28/32
57%8/14
85%298/347
+90%28/31
57%8/14
87%298/341
  module reanimate-0.4.1.0-inplace/Reanimate.ColorComponents 75%9/12
50%1/2
81%135/166
@@ -110,7 +110,7 @@ table.dashboard { border-collapse: collapse ; border: solid 1px black } 20%3/15
- 0/0 20%12/58
  module reanimate-0.4.1.0-inplace/Reanimate.Builtin.Documentation -85%6/7
- 0/0 87%117/134
+100%6/6
- 0/0 100%117/117
  module reanimate-0.4.1.0-inplace/Reanimate.Builtin.Slide 33%1/3
- 0/0 30%18/60
@@ -128,5 +128,5 @@ table.dashboard { border-collapse: collapse ; border: solid 1px black } 15%3/20
- 0/0 14%7/49
  Program Coverage Total -30%247/805
15%131/833
29%4647/15897
+30%247/803
15%131/833
29%4647/15874
diff --git a/hpc_index_exp.html b/hpc_index_exp.html index 0f53b82..aaeb97a 100644 --- a/hpc_index_exp.html +++ b/hpc_index_exp.html @@ -7,17 +7,17 @@ table.dashboard { border-collapse: collapse ; border: solid 1px black } + + + - - - - + @@ -128,5 +128,5 @@ table.dashboard { border-collapse: collapse ; border: solid 1px black } - +
moduleTop Level DefinitionsAlternativesExpressions
%covered / total%covered / total%covered / total
  module reanimate-0.4.1.0-inplace/Reanimate.Builtin.Documentation100%6/6
0/0 100%117/117
  module reanimate-0.4.1.0-inplace/Reanimate.ColorMap 100%14/14
60%3/5
99%1176/1180
  module reanimate-0.4.1.0-inplace/Reanimate.Transition 100%6/6
50%1/2
95%43/45
  module reanimate-0.4.1.0-inplace/Reanimate.Builtin.Documentation85%6/7
0/0 87%117/134
  module reanimate-0.4.1.0-inplace/Reanimate.Animation87%28/32
57%8/14
85%298/347
90%28/31
57%8/14
87%298/341
  module reanimate-0.4.1.0-inplace/Reanimate.ColorComponents 75%9/12
50%1/2
81%135/166
0%0/1
0%0/4
0%0/61
  Program Coverage Total30%247/805
15%131/833
29%4647/15897
30%247/803
15%131/833
29%4647/15874
diff --git a/hpc_index_fun.html b/hpc_index_fun.html index 1908f3a..d278f25 100644 --- a/hpc_index_fun.html +++ b/hpc_index_fun.html @@ -7,6 +7,9 @@ table.dashboard { border-collapse: collapse ; border: solid 1px black } + + + @@ -14,10 +17,7 @@ table.dashboard { border-collapse: collapse ; border: solid 1px black } - - - - + @@ -128,5 +128,5 @@ table.dashboard { border-collapse: collapse ; border: solid 1px black } - +
moduleTop Level DefinitionsAlternativesExpressions
%covered / total%covered / total%covered / total
  module reanimate-0.4.1.0-inplace/Reanimate.Builtin.Documentation100%6/6
0/0 100%117/117
  module reanimate-0.4.1.0-inplace/Reanimate.ColorMap 100%14/14
60%3/5
99%1176/1180
100%6/6
50%1/2
95%43/45
  module reanimate-0.4.1.0-inplace/Reanimate.Animation87%28/32
57%8/14
85%298/347
  module reanimate-0.4.1.0-inplace/Reanimate.Builtin.Documentation85%6/7
0/0 87%117/134
90%28/31
57%8/14
87%298/341
  module reanimate-0.4.1.0-inplace/Reanimate.Transform 83%5/6
33%4/12
45%76/166
0%0/1
0%0/4
0%0/61
  Program Coverage Total30%247/805
15%131/833
29%4647/15897
30%247/803
15%131/833
29%4647/15874
diff --git a/playground/snippets.js b/playground/snippets.js index b09b3d6..02332c5 100644 --- a/playground/snippets.js +++ b/playground/snippets.js @@ -8,4 +8,4 @@ const snippets = [{"title": "Hello World","url": "https://reanimate.clozecards.c ,{"title": "Easing Graphs","url": "https://reanimate.clozecards.com/FEyCDsj0V82/255.svg","code": "colorPalette = parula -- try: viridis, sinebow, turbo, cividis\nfns =\n [(\"curveS\", curveS 2)\n ,(\"bellS\", bellS 2)\n ,(\"constantS\", constantS 0.7)\n ,(\"oscillateS\", oscillateS)\n ,(\"powerS\", powerS 2)\n ,(\"reverseS\", reverseS)\n ,(\"id\", id)\n ]\n\nanimation :: Animation\nanimation = docEnv $ pauseAtEnd 1 $ sceneAnimation $ do\n newSpriteSVG_ $ mkBackground \"white\"\n play $ signalA (curveS 2) $ animate $ \\t -> partialSvg t grid\n newSpriteSVG_ grid\n wait 1\n flip mapM_ (zip [0..] fns) $ \\(nth, (txt, fn)) -> do\n let color = promotePixel $ colorPalette (nth / fromIntegral (length fns-1))\n showEasing nth txt fn color\n {- showEasing 3 \"oscillateS\" oscillateS\n showEasing 4 \"powerS\" (powerS 2)\n showEasing 5 \"reverseS\" reverseS\n showEasing 6 \"id\" id -}\n\ngridOffset = -2\ngridHeight = 5\ngridWidth = 8\n\ngrid :: SVG\ngrid = translate gridOffset 0 $\n withStrokeWidth defaultStrokeWidth $\n withStrokeColor \"grey\" $ mkGroup\n [ mkPath $ concat\n [[ SVG.MoveTo SVG.OriginAbsolute [V2 (-gridWidth/2) (gridHeight/2-n)]\n ,SVG.HorizontalTo SVG.OriginRelative [gridWidth] ]\n | n <- [1..gridHeight-1]\n ]\n , mkPath \n [ SVG.MoveTo SVG.OriginAbsolute [V2 (-gridWidth/2) (gridHeight/2)]\n , SVG.VerticalTo SVG.OriginRelative [-gridHeight]\n , SVG.HorizontalTo SVG.OriginRelative [gridWidth]\n , SVG.VerticalTo SVG.OriginRelative [gridHeight]\n , SVG.EndPath\n ]\n ]\n\nshowEasing nth txt fn color = do\n let steps = 100\n slope = withStrokeColorPixel color $\n translate gridOffset 0 $ mkLinePath\n [ ((x/steps-0.5)*gridWidth, (y-0.5)*gridHeight)\n | x <- [0..steps]\n , let y = fn (x/steps) ]\n s <- newSpriteSVG $\n withFillColorPixel color $\n translate (gridWidth/2+gridOffset+0.5) (gridHeight/2-nth) $\n label txt\n spriteE s $ overBeginning 0.2 fadeInE\n play $ animate $ \\t -> partialSvg t slope\n newSpriteSVG_ slope\n wait 1\n \nlabel txt = withStrokeColor \"black\" $\n withStrokeWidth (defaultStrokeWidth*2) $\n withFillOpacity 1 $\n latex txt\n"} ,{"title": "Object Positions","url": "https://reanimate.clozecards.com/IAQhjO0Ke7h/195.svg","code": "env =\n addStatic (mkBackground \"white\") .\n mapA (withStrokeColor \"black\")\n\nanimation :: Animation\nanimation = env $\n sceneAnimation $ do\n -- Configure objects\n txt <- newText \"Center\"\n top <- newText \"Top\"\n oModifyS top $ \n oTopY .= screenTop\n topR <- newText \"Top right\"\n oModifyS topR $ do\n oTopY .= screenTop\n oRightX .= screenRight\n botR <- newText \"Bottom right\"\n oModifyS botR $ do\n oBottomY .= screenBottom\n oRightX .= screenRight\n botL <- newText \"Bottom left\"\n oModifyS botL $ do\n oBottomY .= screenBottom\n oLeftX .= screenLeft\n topL <- newText \"Top left\"\n oModifyS topL $ do\n oTopY .= screenTop\n oLeftX .= screenLeft\n -- Show objects\n oShow txt\n wait 1\n switchTo txt top\n switchTo top topR\n switchTo topR botR\n switchTo botR botL\n switchTo botL topL\n switchTo topL txt\n\nswitchTo src dst = do\n fork $ oFadeOut src 1\n oModify dst $ oOpacity .~ 1\n oFadeIn dst 1\n wait 1\n\nnewText txt =\n newObject $ scale 1.5 $ center $ latex txt\n"} ]; -const playgroundVersion = "2020-08-25 (41d6a)"; +const playgroundVersion = "2020-08-25 (fd9fc)"; diff --git a/reanimate-0.4.1.0-inplace/Reanimate.Animation.hs.html b/reanimate-0.4.1.0-inplace/Reanimate.Animation.hs.html index a016622..75d0f2c 100644 --- a/reanimate-0.4.1.0-inplace/Reanimate.Animation.hs.html +++ b/reanimate-0.4.1.0-inplace/Reanimate.Animation.hs.html @@ -17,394 +17,400 @@ span.spaces { background: white } never executed always true always false
-    1 module Reanimate.Animation
-    2   ( Duration
-    3   , Time
-    4   , SVG
-    5   , Animation
-    6   -- * Creating animations
-    7   , mkAnimation
-    8   , animate
-    9   , staticFrame
-   10   , pause
-   11   -- * Querying animations
-   12   , duration
-   13   , frameAt
-   14   -- * Composing animations
-   15   , seqA
-   16   , andThen
-   17   , parA
-   18   , parLoopA
-   19   , parDropA
-   20   -- * Modifying animations
-   21   , setDuration
-   22   , adjustDuration
-   23   , mapA
-   24   , takeA
-   25   , dropA
-   26   , lastA
-   27   , pauseAtEnd
-   28   , pauseAtBeginning
-   29   , pauseAround
-   30   , pauseUntil
-   31   , repeatA
-   32   , reverseA
-   33   , playThenReverseA
-   34   , signalA
-   35   , freezeAtPercentage
-   36   , addStatic
-   37   -- * Misc
-   38   , getAnimationFrame
-   39   , Sync(..)
-   40   -- * Rendering
-   41   , renderTree
-   42   , renderSvg
-   43   ) where
-   44 
-   45 import           Control.Arrow              ()
-   46 import           Data.Fixed                 (mod')
-   47 import           Graphics.SvgTree           (Alignment (..), Document (..),
-   48                                              Number (..),
-   49                                              PreserveAspectRatio (..),
-   50                                              Tree (..), xmlOfTree)
-   51 import           Graphics.SvgTree.Printer
-   52 import           Reanimate.Constants
-   53 import           Reanimate.Ease
-   54 import           Reanimate.Svg.Constructors
-   55 import           Text.XML.Light.Output
-   56 
-   57 -- | Duration of an animation or effect. Usually measured in seconds.
-   58 type Duration = Double
-   59 -- | Time signal. Goes from 0 to 1, inclusive.
-   60 type Time = Double
-   61 
-   62 -- | SVG node.
-   63 type SVG = Tree
-   64 
-   65 -- | Animations are SVGs over a finite time.
-   66 data Animation = Animation Duration (Time -> SVG)
+    1 {-|
+    2 Module      : Reanimate.Animation
+    3 Copyright   : Written by David Himmelstrup
+    4 License     : Unlicense
+    5 Maintainer  : lemmih@gmail.com
+    6 Stability   : experimental
+    7 Portability : POSIX
+    8 
+    9 Declarative animation API based on combinators. For a higher-level interface,
+   10 see 'Reanimate.Scene'.
+   11 
+   12 -}
+   13 module Reanimate.Animation
+   14   ( Duration
+   15   , Time
+   16   , SVG
+   17   , Animation
+   18   -- * Creating animations
+   19   , mkAnimation
+   20   , animate
+   21   , staticFrame
+   22   , pause
+   23   -- * Querying animations
+   24   , duration
+   25   , frameAt
+   26   -- * Composing animations
+   27   , seqA
+   28   , andThen
+   29   , parA
+   30   , parLoopA
+   31   , parDropA
+   32   -- * Modifying animations
+   33   , setDuration
+   34   , adjustDuration
+   35   , mapA
+   36   , takeA
+   37   , dropA
+   38   , lastA
+   39   , pauseAtEnd
+   40   , pauseAtBeginning
+   41   , pauseAround
+   42   , repeatA
+   43   , reverseA
+   44   , playThenReverseA
+   45   , signalA
+   46   , freezeAtPercentage
+   47   , addStatic
+   48   -- * Misc
+   49   , getAnimationFrame
+   50   , Sync(..)
+   51   -- * Rendering
+   52   , renderTree
+   53   , renderSvg
+   54   ) where
+   55 
+   56 import           Control.Arrow              ()
+   57 import           Data.Fixed                 (mod')
+   58 import           Graphics.SvgTree           (Alignment (..), Document (..),
+   59                                              Number (..),
+   60                                              PreserveAspectRatio (..),
+   61                                              Tree (..), xmlOfTree)
+   62 import           Graphics.SvgTree.Printer
+   63 import           Reanimate.Constants
+   64 import           Reanimate.Ease
+   65 import           Reanimate.Svg.Constructors
+   66 import           Text.XML.Light.Output
    67 
-   68 -- | Construct an animation with a given duration.
-   69 mkAnimation :: Duration -> (Time -> SVG) -> Animation
-   70 mkAnimation = Animation
-   71 
-   72 -- | Construct an animation with a duration of @1@.
-   73 animate :: (Time -> SVG) -> Animation
-   74 animate = Animation 1
+   68 -- | Duration of an animation or effect. Usually measured in seconds.
+   69 type Duration = Double
+   70 -- | Time signal. Goes from 0 to 1, inclusive.
+   71 type Time = Double
+   72 
+   73 -- | SVG node.
+   74 type SVG = Tree
    75 
-   76 -- | Create an animation with provided @duration@, which consists of stationary frame displayed for its entire duration.
-   77 staticFrame :: Duration -> SVG -> Animation
-   78 staticFrame d svg = Animation d (const svg)
-   79 
-   80 -- | Query the duration of an animation.
-   81 duration :: Animation -> Duration
-   82 duration (Animation d _) = d
-   83 
-   84 -- | Play animations in sequence. The @lhs@ animation is removed after it has
-   85 --   completed. New animation duration is '@duration lhs + duration rhs@'.
-   86 --
-   87 --   Example:
-   88 --
-   89 --   > drawBox `seqA` drawCircle
-   90 --
-   91 --   <<docs/gifs/doc_seqA.gif>>
-   92 seqA :: Animation -> Animation -> Animation
-   93 seqA (Animation d1 f1) (Animation d2 f2) =
-   94   Animation totalD $ \t ->
-   95     if t < d1/totalD
-   96       then f1 (t * totalD/d1)
-   97       else f2 ((t-d1/totalD) * totalD/d2)
-   98   where
-   99     totalD = d1+d2
-  100 
-  101 -- | Play two animation concurrently. Shortest animation freezes on last frame.
-  102 --   New animation duration is '@max (duration lhs) (duration rhs)@'.
-  103 --
-  104 --   Example:
-  105 --
-  106 --   > drawBox `parA` adjustDuration (*2) drawCircle
-  107 --
-  108 --   <<docs/gifs/doc_parA.gif>>
-  109 parA :: Animation -> Animation -> Animation
-  110 parA (Animation d1 f1) (Animation d2 f2) =
-  111   Animation (max d1 d2) $ \t ->
-  112     let t1 = t * totalD/d1
-  113         t2 = t * totalD/d2 in
-  114     mkGroup
-  115     [ f1 (min 1 t1)
-  116     , f2 (min 1 t2) ]
-  117   where
-  118     totalD = max d1 d2
-  119 
-  120 -- | Play two animation concurrently. Shortest animation loops.
-  121 --   New animation duration is '@max (duration lhs) (duration rhs)@'.
-  122 --
-  123 --   Example:
-  124 --
-  125 --   > drawBox `parLoopA` adjustDuration (*2) drawCircle
-  126 --
-  127 --   <<docs/gifs/doc_parLoopA.gif>>
-  128 parLoopA :: Animation -> Animation -> Animation
-  129 parLoopA (Animation d1 f1) (Animation d2 f2) =
-  130   Animation totalD $ \t ->
-  131     let t1 = t * totalD/d1
-  132         t2 = t * totalD/d2 in
-  133     mkGroup
-  134     [ f1 (t1 `mod'` 1)
-  135     , f2 (t2 `mod'` 1) ]
-  136   where
-  137     totalD = max d1 d2
-  138 
-  139 -- | Play two animation concurrently. Animations disappear after playing once.
-  140 --   New animation duration is '@max (duration lhs) (duration rhs)@'.
-  141 --
-  142 --   Example:
-  143 --
-  144 --   > drawBox `parLoopA` adjustDuration (*2) drawCircle
-  145 --
-  146 --   <<docs/gifs/doc_parDropA.gif>>
-  147 parDropA :: Animation -> Animation -> Animation
-  148 parDropA (Animation d1 f1) (Animation d2 f2) =
-  149   Animation totalD $ \t ->
-  150     let t1 = t * totalD/d1
-  151         t2 = t * totalD/d2 in
-  152     mkGroup
-  153     [ if t1>1 then None else f1 t1
-  154     , if t2>1 then None else f2 t2 ]
-  155   where
-  156     totalD = max d1 d2
-  157 
-  158 -- | Empty animation (no SVG output) with a fixed duration.
-  159 --
-  160 --   Example:
-  161 --
-  162 --   > pause 1 `seqA` drawProgress
-  163 --
-  164 --   <<docs/gifs/doc_pause.gif>>
-  165 pause :: Duration -> Animation
-  166 pause d = Animation d (const None)
-  167 
-  168 -- | Play left animation and freeze on the last frame, then play the right
-  169 --   animation. New duration is '@duration lhs + duration rhs@'.
+   76 -- | Animations are SVGs over a finite time.
+   77 data Animation = Animation Duration (Time -> SVG)
+   78 
+   79 -- | Construct an animation with a given duration.
+   80 mkAnimation :: Duration -> (Time -> SVG) -> Animation
+   81 mkAnimation = Animation
+   82 
+   83 -- | Construct an animation with a duration of @1@.
+   84 animate :: (Time -> SVG) -> Animation
+   85 animate = Animation 1
+   86 
+   87 -- | Create an animation with provided @duration@, which consists of stationary frame displayed for its entire duration.
+   88 staticFrame :: Duration -> SVG -> Animation
+   89 staticFrame d svg = Animation d (const svg)
+   90 
+   91 -- | Query the duration of an animation.
+   92 duration :: Animation -> Duration
+   93 duration (Animation d _) = d
+   94 
+   95 -- | Play animations in sequence. The @lhs@ animation is removed after it has
+   96 --   completed. New animation duration is '@duration lhs + duration rhs@'.
+   97 --
+   98 --   Example:
+   99 --
+  100 --   > drawBox `seqA` drawCircle
+  101 --
+  102 --   <<docs/gifs/doc_seqA.gif>>
+  103 seqA :: Animation -> Animation -> Animation
+  104 seqA (Animation d1 f1) (Animation d2 f2) =
+  105   Animation totalD $ \t ->
+  106     if t < d1/totalD
+  107       then f1 (t * totalD/d1)
+  108       else f2 ((t-d1/totalD) * totalD/d2)
+  109   where
+  110     totalD = d1+d2
+  111 
+  112 -- | Play two animation concurrently. Shortest animation freezes on last frame.
+  113 --   New animation duration is '@max (duration lhs) (duration rhs)@'.
+  114 --
+  115 --   Example:
+  116 --
+  117 --   > drawBox `parA` adjustDuration (*2) drawCircle
+  118 --
+  119 --   <<docs/gifs/doc_parA.gif>>
+  120 parA :: Animation -> Animation -> Animation
+  121 parA (Animation d1 f1) (Animation d2 f2) =
+  122   Animation (max d1 d2) $ \t ->
+  123     let t1 = t * totalD/d1
+  124         t2 = t * totalD/d2 in
+  125     mkGroup
+  126     [ f1 (min 1 t1)
+  127     , f2 (min 1 t2) ]
+  128   where
+  129     totalD = max d1 d2
+  130 
+  131 -- | Play two animation concurrently. Shortest animation loops.
+  132 --   New animation duration is '@max (duration lhs) (duration rhs)@'.
+  133 --
+  134 --   Example:
+  135 --
+  136 --   > drawBox `parLoopA` adjustDuration (*2) drawCircle
+  137 --
+  138 --   <<docs/gifs/doc_parLoopA.gif>>
+  139 parLoopA :: Animation -> Animation -> Animation
+  140 parLoopA (Animation d1 f1) (Animation d2 f2) =
+  141   Animation totalD $ \t ->
+  142     let t1 = t * totalD/d1
+  143         t2 = t * totalD/d2 in
+  144     mkGroup
+  145     [ f1 (t1 `mod'` 1)
+  146     , f2 (t2 `mod'` 1) ]
+  147   where
+  148     totalD = max d1 d2
+  149 
+  150 -- | Play two animation concurrently. Animations disappear after playing once.
+  151 --   New animation duration is '@max (duration lhs) (duration rhs)@'.
+  152 --
+  153 --   Example:
+  154 --
+  155 --   > drawBox `parLoopA` adjustDuration (*2) drawCircle
+  156 --
+  157 --   <<docs/gifs/doc_parDropA.gif>>
+  158 parDropA :: Animation -> Animation -> Animation
+  159 parDropA (Animation d1 f1) (Animation d2 f2) =
+  160   Animation totalD $ \t ->
+  161     let t1 = t * totalD/d1
+  162         t2 = t * totalD/d2 in
+  163     mkGroup
+  164     [ if t1>1 then None else f1 t1
+  165     , if t2>1 then None else f2 t2 ]
+  166   where
+  167     totalD = max d1 d2
+  168 
+  169 -- | Empty animation (no SVG output) with a fixed duration.
   170 --
   171 --   Example:
   172 --
-  173 --   > drawBox `andThen` drawCircle
+  173 --   > pause 1 `seqA` drawProgress
   174 --
-  175 --   <<docs/gifs/doc_andThen.gif>>
-  176 andThen :: Animation -> Animation -> Animation
-  177 andThen a b = a `parA` (pause (duration a) `seqA` b)
+  175 --   <<docs/gifs/doc_pause.gif>>
+  176 pause :: Duration -> Animation
+  177 pause d = Animation d (const None)
   178 
-  179 -- | Calculate the frame that would be displayed at given point in @time@ of running @animation@.
-  180 --
-  181 -- The provided time parameter is clamped between 0 and animation duration.
-  182 frameAt :: Time -> Animation -> SVG
-  183 frameAt t (Animation d f) = f t'
-  184   where
-  185     t' = clamp 0 1 (t/d)
-  186 
-  187 -- | Helper function for pretty-printing SVG nodes.
-  188 renderTree :: SVG -> String
-  189 renderTree t = maybe "" ppElement $ xmlOfTree t
-  190 
-  191 -- | Helper function for pretty-printing SVG nodes as SVG documents.
-  192 renderSvg :: Maybe Number -- ^ The number to use as value of the @width@ attribute of the resulting top-level svg element. If @Nothing@, the width attribute won't be rendered.
-  193           -> Maybe Number -- ^ Similar to previous argument, but for @height@ attribute.
-  194           -> SVG          -- ^ SVG to render
-  195           -> String       -- ^ String representation of SVG XML markup
-  196 renderSvg w h t = ppDocument doc
-  197 -- renderSvg w h t = ppFastElement (xmlOfDocument doc)
-  198   where
-  199     width = 16
-  200     height = 9
-  201     doc = Document
-  202       { _viewBox = Just (-width/2, -height/2, width, height)
-  203       , _width = w
-  204       , _height = h
-  205       , _elements = [withStrokeWidth defaultStrokeWidth $ scaleXY 1 (-1) t]
-  206       , _description = ""
-  207       , _documentLocation = ""
-  208       , _documentAspectRatio = PreserveAspectRatio False AlignNone Nothing
-  209       }
-  210 
-  211 -- | Map over the SVG produced by an animation at every frame.
-  212 --
-  213 --   Example:
-  214 --
-  215 --   > mapA (scale 0.5) drawCircle
-  216 --
-  217 --   <<docs/gifs/doc_mapA.gif>>
-  218 
-  219 mapA :: (SVG -> SVG) -> Animation -> Animation
-  220 mapA fn (Animation d f) = Animation d (fn . f)
+  179 -- | Play left animation and freeze on the last frame, then play the right
+  180 --   animation. New duration is '@duration lhs + duration rhs@'.
+  181 --
+  182 --   Example:
+  183 --
+  184 --   > drawBox `andThen` drawCircle
+  185 --
+  186 --   <<docs/gifs/doc_andThen.gif>>
+  187 andThen :: Animation -> Animation -> Animation
+  188 andThen a b = a `parA` (pause (duration a) `seqA` b)
+  189 
+  190 -- | Calculate the frame that would be displayed at given point in @time@ of running @animation@.
+  191 --
+  192 -- The provided time parameter is clamped between 0 and animation duration.
+  193 frameAt :: Time -> Animation -> SVG
+  194 frameAt t (Animation d f) = f t'
+  195   where
+  196     t' = clamp 0 1 (t/d)
+  197 
+  198 -- | Helper function for pretty-printing SVG nodes.
+  199 renderTree :: SVG -> String
+  200 renderTree t = maybe "" ppElement $ xmlOfTree t
+  201 
+  202 -- | Helper function for pretty-printing SVG nodes as SVG documents.
+  203 renderSvg :: Maybe Number -- ^ The number to use as value of the @width@ attribute of the resulting top-level svg element. If @Nothing@, the width attribute won't be rendered.
+  204           -> Maybe Number -- ^ Similar to previous argument, but for @height@ attribute.
+  205           -> SVG          -- ^ SVG to render
+  206           -> String       -- ^ String representation of SVG XML markup
+  207 renderSvg w h t = ppDocument doc
+  208 -- renderSvg w h t = ppFastElement (xmlOfDocument doc)
+  209   where
+  210     width = 16
+  211     height = 9
+  212     doc = Document
+  213       { _viewBox = Just (-width/2, -height/2, width, height)
+  214       , _width = w
+  215       , _height = h
+  216       , _elements = [withStrokeWidth defaultStrokeWidth $ scaleXY 1 (-1) t]
+  217       , _description = ""
+  218       , _documentLocation = ""
+  219       , _documentAspectRatio = PreserveAspectRatio False AlignNone Nothing
+  220       }
   221 
-  222 -- | Freeze the last frame for @t@ seconds at the end of the animation.
+  222 -- | Map over the SVG produced by an animation at every frame.
   223 --
   224 --   Example:
   225 --
-  226 --   > pauseAtEnd 1 drawProgress
+  226 --   > mapA (scale 0.5) drawCircle
   227 --
-  228 --   <<docs/gifs/doc_pauseAtEnd.gif>>
-  229 pauseAtEnd :: Duration -> Animation -> Animation
-  230 pauseAtEnd t a = a `andThen` pause t
-  231 
-  232 -- | Freeze the first frame for @t@ seconds at the beginning of the animation.
-  233 --
-  234 --   Example:
-  235 --
-  236 --   > pauseAtBeginning 1 drawProgress
-  237 --
-  238 --   <<docs/gifs/doc_pauseAtBeginning.gif>>
-  239 pauseAtBeginning :: Duration -> Animation -> Animation
-  240 pauseAtBeginning t a =
-  241     Animation t (freezeFrame 0 a) `seqA` a
+  228 --   <<docs/gifs/doc_mapA.gif>>
+  229 
+  230 mapA :: (SVG -> SVG) -> Animation -> Animation
+  231 mapA fn (Animation d f) = Animation d (fn . f)
+  232 
+  233 -- | Freeze the last frame for @t@ seconds at the end of the animation.
+  234 --
+  235 --   Example:
+  236 --
+  237 --   > pauseAtEnd 1 drawProgress
+  238 --
+  239 --   <<docs/gifs/doc_pauseAtEnd.gif>>
+  240 pauseAtEnd :: Duration -> Animation -> Animation
+  241 pauseAtEnd t a = a `andThen` pause t
   242 
-  243 -- | Freeze the first and the last frame of the animation for a specified duration.
+  243 -- | Freeze the first frame for @t@ seconds at the beginning of the animation.
   244 --
   245 --   Example:
   246 --
-  247 --   > pauseAround 1 1 drawProgress
+  247 --   > pauseAtBeginning 1 drawProgress
   248 --
-  249 --   <<docs/gifs/doc_pauseAround.gif>>
-  250 pauseAround :: Duration -> Duration -> Animation -> Animation
-  251 pauseAround start end = pauseAtEnd end . pauseAtBeginning start
-  252 
-  253 -- XXX: Rename to 'setDurationFreeze'. Add 'setDurationDrop' and
-  254 --      'setDurationLoop'.
-  255 pauseUntil :: Duration -> Animation -> Animation
-  256 pauseUntil d a = pauseAtEnd (d-duration a) a
-  257 
-  258 -- Freeze frame at time @t@.
-  259 freezeFrame :: Time -> Animation -> (Time -> SVG)
-  260 freezeFrame t (Animation d f) = const $ f (t/d)
-  261 
-  262 -- | Change the duration of an animation. Animates are stretched or squished
-  263 --   (rather than truncated) to fit the new duration.
-  264 adjustDuration :: (Duration -> Duration) -> Animation -> Animation
-  265 adjustDuration fn (Animation d gen) =
-  266   Animation (fn d) gen
+  249 --   <<docs/gifs/doc_pauseAtBeginning.gif>>
+  250 pauseAtBeginning :: Duration -> Animation -> Animation
+  251 pauseAtBeginning t a =
+  252     Animation t (freezeFrame 0 a) `seqA` a
+  253 
+  254 -- | Freeze the first and the last frame of the animation for a specified duration.
+  255 --
+  256 --   Example:
+  257 --
+  258 --   > pauseAround 1 1 drawProgress
+  259 --
+  260 --   <<docs/gifs/doc_pauseAround.gif>>
+  261 pauseAround :: Duration -> Duration -> Animation -> Animation
+  262 pauseAround start end = pauseAtEnd end . pauseAtBeginning start
+  263 
+  264 -- Freeze frame at time @t@.
+  265 freezeFrame :: Time -> Animation -> (Time -> SVG)
+  266 freezeFrame t (Animation d f) = const $ f (t/d)
   267 
-  268 -- | Set the duration of an animation by adjusting its playback rate. The
-  269 --   animation is still played from start to finish without being cropped.
-  270 setDuration :: Duration -> Animation -> Animation
-  271 setDuration newD = adjustDuration (const newD)
-  272 
-  273 -- | Play an animation in reverse. Duration remains unchanged. Shorthand for:
-  274 --   @'signalA' 'reverseS'@.
-  275 --
-  276 --   Example:
-  277 --
-  278 --   > reverseA drawCircle
-  279 --
-  280 --   <<docs/gifs/doc_reverseA.gif>>
-  281 reverseA :: Animation -> Animation
-  282 reverseA = signalA reverseS
-  283 
-  284 -- | Play animation before playing it again in reverse. Duration is twice
-  285 --   the duration of the input.
-  286 --
-  287 --   Example:
-  288 --
-  289 --   > playThenReverseA drawCircle
-  290 --
-  291 --   <<docs/gifs/doc_playThenReverseA.gif>>
-  292 playThenReverseA :: Animation -> Animation
-  293 playThenReverseA a = a `seqA` reverseA a
-  294 
-  295 -- | Loop animation @n@ number of times. This number may be fractional and it
-  296 --   may be less than 1. It must be greater than or equal to 0, though.
-  297 --   New duration is @n*duration input@.
-  298 --
-  299 --   Example:
-  300 --
-  301 --   > repeatA 1.5 drawCircle
-  302 --
-  303 --   <<docs/gifs/doc_repeatA.gif>>
-  304 repeatA :: Double -> Animation -> Animation
-  305 repeatA n (Animation d f) = Animation (d*n) $ \t ->
-  306   f ((t*n) `mod'` 1)
-  307 
-  308 
-  309 -- | @freezeAtPercentage time animation@ creates an animation consisting of stationary frame,
-  310 -- that would be displayed in the provided @animation@ at given @time@.
-  311 -- The duration of the new animation is the same as the duration of provided @animation@.
-  312 freezeAtPercentage :: Time  -- ^ value between 0 and 1. The frame displayed at this point in the original animation will be displayed for the duration of the new animation
-  313                    -> Animation -- ^ original animation, from which the frame will be taken
-  314                    -> Animation -- ^ new animation consisting of static frame displayed for the duration of the original animation
-  315 freezeAtPercentage frac (Animation d genFrame) =
-  316   Animation d $ const $ genFrame frac
-  317 
-  318 -- | Overlay animation on top of static SVG image.
-  319 --
-  320 --  Example:
-  321 --
-  322 --  > addStatic (mkBackground "lightblue") drawCircle
-  323 --
-  324 --  <<docs/gifs/doc_addStatic.gif>>
-  325 addStatic :: SVG -> Animation -> Animation
-  326 addStatic static = mapA (\frame -> mkGroup [static, frame])
-  327 
-  328 -- | Modify the time component of an animation. Animation duration is unchanged.
+  268 -- | Change the duration of an animation. Animates are stretched or squished
+  269 --   (rather than truncated) to fit the new duration.
+  270 adjustDuration :: (Duration -> Duration) -> Animation -> Animation
+  271 adjustDuration fn (Animation d gen) =
+  272   Animation (fn d) gen
+  273 
+  274 -- | Set the duration of an animation by adjusting its playback rate. The
+  275 --   animation is still played from start to finish without being cropped.
+  276 setDuration :: Duration -> Animation -> Animation
+  277 setDuration newD = adjustDuration (const newD)
+  278 
+  279 -- | Play an animation in reverse. Duration remains unchanged. Shorthand for:
+  280 --   @'signalA' 'reverseS'@.
+  281 --
+  282 --   Example:
+  283 --
+  284 --   > reverseA drawCircle
+  285 --
+  286 --   <<docs/gifs/doc_reverseA.gif>>
+  287 reverseA :: Animation -> Animation
+  288 reverseA = signalA reverseS
+  289 
+  290 -- | Play animation before playing it again in reverse. Duration is twice
+  291 --   the duration of the input.
+  292 --
+  293 --   Example:
+  294 --
+  295 --   > playThenReverseA drawCircle
+  296 --
+  297 --   <<docs/gifs/doc_playThenReverseA.gif>>
+  298 playThenReverseA :: Animation -> Animation
+  299 playThenReverseA a = a `seqA` reverseA a
+  300 
+  301 -- | Loop animation @n@ number of times. This number may be fractional and it
+  302 --   may be less than 1. It must be greater than or equal to 0, though.
+  303 --   New duration is @n*duration input@.
+  304 --
+  305 --   Example:
+  306 --
+  307 --   > repeatA 1.5 drawCircle
+  308 --
+  309 --   <<docs/gifs/doc_repeatA.gif>>
+  310 repeatA :: Double -> Animation -> Animation
+  311 repeatA n (Animation d f) = Animation (d*n) $ \t ->
+  312   f ((t*n) `mod'` 1)
+  313 
+  314 
+  315 -- | @freezeAtPercentage time animation@ creates an animation consisting of stationary frame,
+  316 -- that would be displayed in the provided @animation@ at given @time@.
+  317 -- The duration of the new animation is the same as the duration of provided @animation@.
+  318 freezeAtPercentage :: Time  -- ^ value between 0 and 1. The frame displayed at this point in the original animation will be displayed for the duration of the new animation
+  319                    -> Animation -- ^ original animation, from which the frame will be taken
+  320                    -> Animation -- ^ new animation consisting of static frame displayed for the duration of the original animation
+  321 freezeAtPercentage frac (Animation d genFrame) =
+  322   Animation d $ const $ genFrame frac
+  323 
+  324 -- | Overlay animation on top of static SVG image.
+  325 --
+  326 --  Example:
+  327 --
+  328 --  > addStatic (mkBackground "lightblue") drawCircle
   329 --
-  330 --   Example:
-  331 --
-  332 --   > signalA (fromToS 0.25 0.75) drawCircle
-  333 --
-  334 --   <<docs/gifs/doc_signalA.gif>>
-  335 signalA :: Signal -> Animation -> Animation
-  336 signalA fn (Animation d gen) = Animation d $ gen . fn
-  337 
-  338 -- | @takeA duration animation@ creates a new animation consisting of initial segment of
-  339 --   @animation@ of given @duration@, played at the same rate as the original animation.
-  340 --
-  341 --  The @duration@ parameter is clamped to be between 0 and @animation@'s duration.
-  342 --  New animation duration is equal to (eventually clamped) @duration@.
-  343 takeA :: Duration -> Animation -> Animation
-  344 takeA len (Animation d gen) = Animation len' $ \t ->
-  345     gen (t * len'/d)
-  346   where
-  347     len' = clamp 0 d len
-  348 
-  349 -- | @dropA duration animation@ creates a new animation by dropping initial segment
-  350 --   of length @duration@ from the provided @animation@, played at the same rate as the original animation.
-  351 --
-  352 --  The @duration@ parameter is clamped to be between 0 and @animation@'s duration.
-  353 --  The duration of the resulting animation is duration of provided @animation@ minus (eventually clamped) @duration@.
-  354 dropA :: Duration -> Animation -> Animation
-  355 dropA len (Animation d gen) = Animation len' $ \t ->
-  356     gen (t * len'/d + len/d)
-  357   where
-  358     len' = d - clamp 0 d len
-  359 
-  360 -- | @lastA duration animation@ return the last @duration@ seconds of the animation.
-  361 lastA :: Duration -> Animation -> Animation
-  362 lastA len a = dropA (duration a - len) a
-  363 
-  364 clamp :: Double -> Double -> Double -> Double
-  365 clamp a b number
-  366   | a < b     = max a (min b number)
-  367   | otherwise = max b (min a number)
-  368 
-  369 -- (#) :: a -> (a -> b) -> b
-  370 -- o # f = f o
-  371 
-  372 -- | Ask for an animation frame using a given synchronization policy.
-  373 getAnimationFrame :: Sync -> Animation -> Time -> Duration -> SVG
-  374 getAnimationFrame sync (Animation aDur aGen) t d =
-  375   case sync of
-  376     SyncStretch -> aGen (t/d)
-  377     SyncLoop    -> aGen (takeFrac $ t/aDur)
-  378     SyncDrop    -> if t > aDur then None else aGen (t/aDur)
-  379     SyncFreeze  -> aGen (min 1 $ t/aDur)
-  380   where
-  381     takeFrac f = snd (properFraction f :: (Int, Double))
-  382 
-  383 -- | Animation synchronization policies.
-  384 data Sync
-  385   = SyncStretch
-  386   | SyncLoop
-  387   | SyncDrop
-  388   | SyncFreeze
+  330 --  <<docs/gifs/doc_addStatic.gif>>
+  331 addStatic :: SVG -> Animation -> Animation
+  332 addStatic static = mapA (\frame -> mkGroup [static, frame])
+  333 
+  334 -- | Modify the time component of an animation. Animation duration is unchanged.
+  335 --
+  336 --   Example:
+  337 --
+  338 --   > signalA (fromToS 0.25 0.75) drawCircle
+  339 --
+  340 --   <<docs/gifs/doc_signalA.gif>>
+  341 signalA :: Signal -> Animation -> Animation
+  342 signalA fn (Animation d gen) = Animation d $ gen . fn
+  343 
+  344 -- | @takeA duration animation@ creates a new animation consisting of initial segment of
+  345 --   @animation@ of given @duration@, played at the same rate as the original animation.
+  346 --
+  347 --  The @duration@ parameter is clamped to be between 0 and @animation@'s duration.
+  348 --  New animation duration is equal to (eventually clamped) @duration@.
+  349 takeA :: Duration -> Animation -> Animation
+  350 takeA len (Animation d gen) = Animation len' $ \t ->
+  351     gen (t * len'/d)
+  352   where
+  353     len' = clamp 0 d len
+  354 
+  355 -- | @dropA duration animation@ creates a new animation by dropping initial segment
+  356 --   of length @duration@ from the provided @animation@, played at the same rate as the original animation.
+  357 --
+  358 --  The @duration@ parameter is clamped to be between 0 and @animation@'s duration.
+  359 --  The duration of the resulting animation is duration of provided @animation@ minus (eventually clamped) @duration@.
+  360 dropA :: Duration -> Animation -> Animation
+  361 dropA len (Animation d gen) = Animation len' $ \t ->
+  362     gen (t * len'/d + len/d)
+  363   where
+  364     len' = d - clamp 0 d len
+  365 
+  366 -- | @lastA duration animation@ return the last @duration@ seconds of the animation.
+  367 lastA :: Duration -> Animation -> Animation
+  368 lastA len a = dropA (duration a - len) a
+  369 
+  370 clamp :: Double -> Double -> Double -> Double
+  371 clamp a b number
+  372   | a < b     = max a (min b number)
+  373   | otherwise = max b (min a number)
+  374 
+  375 -- (#) :: a -> (a -> b) -> b
+  376 -- o # f = f o
+  377 
+  378 -- | Ask for an animation frame using a given synchronization policy.
+  379 getAnimationFrame :: Sync -> Animation -> Time -> Duration -> SVG
+  380 getAnimationFrame sync (Animation aDur aGen) t d =
+  381   case sync of
+  382     SyncStretch -> aGen (t/d)
+  383     SyncLoop    -> aGen (takeFrac $ t/aDur)
+  384     SyncDrop    -> if t > aDur then None else aGen (t/aDur)
+  385     SyncFreeze  -> aGen (min 1 $ t/aDur)
+  386   where
+  387     takeFrac f = snd (properFraction f :: (Int, Double))
+  388 
+  389 -- | Animation synchronization policies.
+  390 data Sync
+  391   = SyncStretch
+  392   | SyncLoop
+  393   | SyncDrop
+  394   | SyncFreeze
 
 
diff --git a/reanimate-0.4.1.0-inplace/Reanimate.Builtin.Documentation.hs.html b/reanimate-0.4.1.0-inplace/Reanimate.Builtin.Documentation.hs.html index a5e7513..d6a0692 100644 --- a/reanimate-0.4.1.0-inplace/Reanimate.Builtin.Documentation.hs.html +++ b/reanimate-0.4.1.0-inplace/Reanimate.Builtin.Documentation.hs.html @@ -17,62 +17,69 @@ span.spaces { background: white } never executed always true always false
-    1 module Reanimate.Builtin.Documentation where
-    2 
-    3 import Reanimate.Animation
-    4 import Reanimate.Svg
-    5 import Reanimate.Raster
-    6 import Reanimate.Constants
-    7 import Codec.Picture
+    1 {-|
+    2 Module      : Reanimate.Builtin.Documentation
+    3 Copyright   : Written by David Himmelstrup
+    4 License     : Unlicense
+    5 Maintainer  : lemmih@gmail.com
+    6 Stability   : experimental
+    7 Portability : POSIX
     8 
-    9 -- | Default environment for API documentation GIFs.
-   10 docEnv :: Animation -> Animation
-   11 docEnv = mapA $ \svg -> mkGroup
-   12   [ mkBackground "white"
-   13   , withFillOpacity 0 $
-   14     withStrokeWidth 0.1 $
-   15     withStrokeColor "black" (mkGroup [svg]) ]
-   16 
-   17 -- | <<docs/gifs/doc_drawBox.gif>>
-   18 drawBox :: Animation
-   19 drawBox = mkAnimation 2 $ \t ->
-   20   partialSvg t $ pathify $
-   21   mkRect (screenWidth/2) (screenHeight/2)
-   22 
-   23 -- | <<docs/gifs/doc_drawCircle.gif>>
-   24 drawCircle :: Animation
-   25 drawCircle = mkAnimation 2 $ \t ->
-   26   partialSvg t $ pathify $
-   27   mkCircle (screenHeight/3)
+    9 This module contains convenience functions used in documention
+   10 GIFs for a consistent look and feel.
+   11 
+   12 -}
+   13 module Reanimate.Builtin.Documentation where
+   14 
+   15 import Reanimate.Animation
+   16 import Reanimate.Svg
+   17 import Reanimate.Raster
+   18 import Reanimate.Constants
+   19 import Codec.Picture
+   20 
+   21 -- | Default environment for API documentation GIFs.
+   22 docEnv :: Animation -> Animation
+   23 docEnv = mapA $ \svg -> mkGroup
+   24   [ mkBackground "white"
+   25   , withFillOpacity 0 $
+   26     withStrokeWidth 0.1 $
+   27     withStrokeColor "black" (mkGroup [svg]) ]
    28 
-   29 drawBall :: Animation
-   30 drawBall = mkAnimation 2 $ \t ->
-   31   scale t $ withFillOpacity 1 $ withFillColor "red" $
-   32   mkCircle (screenHeight/3)
-   33 
-   34 -- | <<docs/gifs/doc_drawProgress.gif>>
-   35 drawProgress :: Animation
-   36 drawProgress = mkAnimation 2 $ \t ->
-   37   mkGroup
-   38   [ mkLine (-screenWidth/2*widthP,0)
-   39            (screenWidth/2*widthP,0)
-   40   , translate (-screenWidth/2*widthP + screenWidth*widthP*t) 0 $
-   41     withFillOpacity 1 $ mkCircle 0.5 ]
-   42   where
-   43     widthP = 0.8
-   44 
-   45 -- | Render a full-screen view of a color-map.
-   46 showColorMap :: (Double -> PixelRGB8) -> SVG
-   47 showColorMap f = center $ scaleToSize screenWidth screenHeight $ embedImage img
-   48   where
-   49     width = 256
-   50     height = 1
-   51     img = generateImage pixelRenderer width height
-   52     pixelRenderer x _y = f (fromIntegral x / fromIntegral (width-1))
-   53 
-   54 -- | Default background color for videos on reanimate.rtfd.io
-   55 rtfdBackgroundColor :: PixelRGBA8
-   56 rtfdBackgroundColor = PixelRGBA8 252 252 252 0xFF
+   29 -- | <<docs/gifs/doc_drawBox.gif>>
+   30 drawBox :: Animation
+   31 drawBox = mkAnimation 2 $ \t ->
+   32   partialSvg t $ pathify $
+   33   mkRect (screenWidth/2) (screenHeight/2)
+   34 
+   35 -- | <<docs/gifs/doc_drawCircle.gif>>
+   36 drawCircle :: Animation
+   37 drawCircle = mkAnimation 2 $ \t ->
+   38   partialSvg t $ pathify $
+   39   mkCircle (screenHeight/3)
+   40 
+   41 -- | <<docs/gifs/doc_drawProgress.gif>>
+   42 drawProgress :: Animation
+   43 drawProgress = mkAnimation 2 $ \t ->
+   44   mkGroup
+   45   [ mkLine (-screenWidth/2*widthP,0)
+   46            (screenWidth/2*widthP,0)
+   47   , translate (-screenWidth/2*widthP + screenWidth*widthP*t) 0 $
+   48     withFillOpacity 1 $ mkCircle 0.5 ]
+   49   where
+   50     widthP = 0.8
+   51 
+   52 -- | Render a full-screen view of a color-map.
+   53 showColorMap :: (Double -> PixelRGB8) -> SVG
+   54 showColorMap f = center $ scaleToSize screenWidth screenHeight $ embedImage img
+   55   where
+   56     width = 256
+   57     height = 1
+   58     img = generateImage pixelRenderer width height
+   59     pixelRenderer x _y = f (fromIntegral x / fromIntegral (width-1))
+   60 
+   61 -- | Default background color for videos on reanimate.rtfd.io
+   62 rtfdBackgroundColor :: PixelRGBA8
+   63 rtfdBackgroundColor = PixelRGBA8 252 252 252 0xFF
 
 
diff --git a/reanimate-0.4.1.0-inplace/Reanimate.Raster.hs.html b/reanimate-0.4.1.0-inplace/Reanimate.Raster.hs.html index cafdfd0..aa23fe4 100644 --- a/reanimate-0.4.1.0-inplace/Reanimate.Raster.hs.html +++ b/reanimate-0.4.1.0-inplace/Reanimate.Raster.hs.html @@ -17,310 +17,321 @@ span.spaces { background: white } never executed always true always false
-    1 module Reanimate.Raster
-    2   ( mkImage           -- :: Double -> Double -> FilePath -> SVG
-    3   , cacheImage        -- :: (PngSavable pixel, Hashable a) => a -> Image pixel -> FilePath
-    4   , prerenderSvg      -- :: Hashable a => a -> SVG -> SVG
-    5   , prerenderSvgFile  -- :: Hashable a => a -> Width -> Height -> SVG -> FilePath
-    6   , embedImage        -- :: PngSavable a => Image a -> SVG
-    7   , embedDynamicImage -- :: DynamicImage -> SVG
-    8   , embedPng          -- :: Double -> Double -> LBS.ByteString -> SVG
-    9   , raster            -- :: SVG -> DynamicImage
-   10   , rasterSized       -- :: Width -> Height -> SVG -> DynamicImage
-   11   , vectorize         -- :: FilePath -> SVG
-   12   , vectorize_        -- :: [String] -> FilePath -> SVG
-   13   , svgAsPngFile      -- :: SVG -> FilePath
-   14   , svgAsPngFile'     -- :: Width -> Height -> SVG -> FilePath
-   15   )
-   16 where
-   17 
-   18 import           Codec.Picture
-   19 import           Control.Lens                             ( (&)
-   20                                                           , (.~)
-   21                                                           )
-   22 import           Control.Monad
-   23 import qualified Data.ByteString               as B
-   24 import qualified Data.ByteString.Base64.Lazy   as Base64
-   25 import qualified Data.ByteString.Lazy.Char8    as LBS
-   26 import           Data.Hashable
-   27 import qualified Data.Text                     as T
-   28 import           Graphics.SvgTree                         ( Number(..)
-   29                                                           , Tree(..)
-   30                                                           , defaultSvg
-   31                                                           , parseSvgFile
+    1 {-|
+    2 Module      : Reanimate.Raster
+    3 Copyright   : Written by David Himmelstrup
+    4 License     : Unlicense
+    5 Maintainer  : lemmih@gmail.com
+    6 Stability   : experimental
+    7 Portability : POSIX
+    8 
+    9 Tools for generating, manipulating, and embedding raster images.
+   10 
+   11 -}
+   12 module Reanimate.Raster
+   13   ( mkImage           -- :: Double -> Double -> FilePath -> SVG
+   14   , cacheImage        -- :: (PngSavable pixel, Hashable a) => a -> Image pixel -> FilePath
+   15   , prerenderSvg      -- :: Hashable a => a -> SVG -> SVG
+   16   , prerenderSvgFile  -- :: Hashable a => a -> Width -> Height -> SVG -> FilePath
+   17   , embedImage        -- :: PngSavable a => Image a -> SVG
+   18   , embedDynamicImage -- :: DynamicImage -> SVG
+   19   , embedPng          -- :: Double -> Double -> LBS.ByteString -> SVG
+   20   , raster            -- :: SVG -> DynamicImage
+   21   , rasterSized       -- :: Width -> Height -> SVG -> DynamicImage
+   22   , vectorize         -- :: FilePath -> SVG
+   23   , vectorize_        -- :: [String] -> FilePath -> SVG
+   24   , svgAsPngFile      -- :: SVG -> FilePath
+   25   , svgAsPngFile'     -- :: Width -> Height -> SVG -> FilePath
+   26   )
+   27 where
+   28 
+   29 import           Codec.Picture
+   30 import           Control.Lens                             ( (&)
+   31                                                           , (.~)
    32                                                           )
-   33 import qualified Graphics.SvgTree              as Svg
-   34 import           Reanimate.Animation
-   35 import           Reanimate.Cache
-   36 import           Reanimate.Driver.Magick
-   37 import           Reanimate.Misc
-   38 import           Reanimate.Render
-   39 import           Reanimate.Parameters
-   40 import           Reanimate.Constants
-   41 import           Reanimate.Svg.Constructors
-   42 import           Reanimate.Svg.Unuse
-   43 import           System.Directory
-   44 import           System.FilePath
-   45 import           System.IO
-   46 import           System.IO.Temp
-   47 import           System.IO.Unsafe
-   48 
-   49 -- | Load an external image. Width and height must be specified,
-   50 --   ignoring the image's aspect ratio. The center of the image is
-   51 --   placed at position (0,0).
-   52 --
-   53 --   For security reasons, must SVG renderer do not allow arbitrary
-   54 --   image links. For some renderers, we can get around this by placing
-   55 --   the images in the same root directory as the parent SVG file. Other
-   56 --   renderers (like Chrome and ffmpeg) requires that the image is inlined
-   57 --   as base64 data. External SVG files are an exception, though, as must
-   58 --   always be inlined directly. `mkImage` attempts to hide all the complexity
-   59 --   but edge-cases may exist.
-   60 --
-   61 --   Example:
-   62 --
-   63 --   > mkImage screenWidth screenHeight "../data/haskell.svg"
-   64 --
-   65 --   <<docs/gifs/doc_mkImage.gif>>
-   66 mkImage
-   67   :: Double -- ^ Desired image width.
-   68   -> Double -- ^ Desired image height.
-   69   -> FilePath -- ^ Path to external image file.
-   70   -> SVG
-   71 mkImage width height path | takeExtension path == ".svg" = unsafePerformIO $ do
-   72   svg_data <- B.readFile path
-   73   case parseSvgFile path svg_data of
-   74     Nothing -> error "Malformed svg"
-   75     Just svg ->
-   76       return
-   77         $ scaleXY (width / screenWidth) (height / screenHeight)
-   78         $ embedDocument svg
-   79 mkImage width height path | pRaster == RasterNone = unsafePerformIO $ do
-   80   inp <- LBS.readFile path
-   81   let imgData = LBS.unpack $ Base64.encode inp
-   82   return
-   83     $  flipYAxis
-   84     $  ImageTree
-   85     $  defaultSvg
-   86     &  Svg.imageWidth
-   87     .~ Svg.Num width
-   88     &  Svg.imageHeight
-   89     .~ Svg.Num height
-   90     &  Svg.imageHref
-   91     .~ ("data:" ++ mimeType ++ ";base64," ++ imgData)
-   92     &  Svg.imageCornerUpperLeft
-   93     .~ (Svg.Num (-width / 2), Svg.Num (-height / 2))
-   94     &  Svg.imageAspectRatio
-   95     .~ Svg.PreserveAspectRatio False Svg.AlignNone Nothing
-   96  where
-   97     -- FIXME: Is there a better way to do this?
-   98   mimeType = case takeExtension path of
-   99     ".jpg" -> "image/jpeg"
-  100     ext    -> "image/" ++ drop 1 ext
-  101 mkImage width height path = unsafePerformIO $ do
-  102   exists <- doesFileExist target
-  103   unless exists $ copyFile path target
-  104   return
-  105     $  flipYAxis
-  106     $  ImageTree
-  107     $  defaultSvg
-  108     &  Svg.imageWidth
-  109     .~ Svg.Num width
-  110     &  Svg.imageHeight
-  111     .~ Svg.Num height
-  112     &  Svg.imageHref
-  113     .~ ("file://" ++ target)
-  114     &  Svg.imageCornerUpperLeft
-  115     .~ (Svg.Num (-width / 2), Svg.Num (-height / 2))
-  116     &  Svg.imageAspectRatio
-  117     .~ Svg.PreserveAspectRatio False Svg.AlignNone Nothing
-  118  where
-  119   target   = pRootDirectory </> encodeInt hashPath <.> takeExtension path
-  120   hashPath = hash path
-  121 
-  122 -- | Write in-memory image to cache file if (and only if) such cache file doesn't
-  123 --   already exist.
-  124 cacheImage :: (PngSavable pixel, Hashable a) => a -> Image pixel -> FilePath
-  125 cacheImage key gen = unsafePerformIO $ cacheFile template $ \path ->
-  126   writePng path gen
-  127   where template = encodeInt (hash key) <.> "png"
-  128 
-  129 -- Warning: Caching svg elements with links to external objects does
-  130 --          not work. 2020-06-01
-  131 -- | Same as 'prerenderSvg' but returns the location of the rendered image
-  132 --   as a FilePath.
-  133 prerenderSvgFile :: Hashable a => a -> Width -> Height -> SVG -> FilePath
-  134 prerenderSvgFile key width height svg =
-  135   unsafePerformIO $ cacheFile template $ \path -> do
-  136     let svgPath = replaceExtension path "svg"
-  137     writeFile svgPath rendered
-  138     engine <- requireRaster pRaster
-  139     applyRaster engine svgPath
-  140  where
-  141   template = encodeInt (hash (key, width, height)) <.> "png"
-  142   rendered = renderSvg (Just $ Px $ fromIntegral width)
-  143                        (Just $ Px $ fromIntegral height)
-  144                        svg
-  145 
-  146 -- | Render SVG node to a PNG file and return a new node containing
-  147 --   that image. For static SVG nodes, this can hugely improve performance.
-  148 --   The first argument is the key that determines SVG uniqueness. It
-  149 --   is entirely your responsibility to ensure that all keys are unique.
-  150 --   If they are not, you will be served stale results from the cache.
-  151 prerenderSvg :: Hashable a => a -> SVG -> SVG
-  152 prerenderSvg key =
-  153   mkImage screenWidth screenHeight . prerenderSvgFile key pWidth pHeight
-  154 
-  155 
-  156 {-# INLINE embedImage #-}
-  157 -- | Embed an in-memory PNG image. Note, the pixel size of the image
-  158 --   is used as the dimensions. As such, embedding a 100x100 PNG will
-  159 --   result in an image 100 units wide and 100 units high. Consider
-  160 --   using with 'scaleToSize'.
-  161 embedImage :: PngSavable a => Image a -> SVG
-  162 embedImage img = embedPng width height (encodePng img)
-  163  where
-  164   width  = fromIntegral $ imageWidth img
-  165   height = fromIntegral $ imageHeight img
+   33 import           Control.Monad
+   34 import qualified Data.ByteString               as B
+   35 import qualified Data.ByteString.Base64.Lazy   as Base64
+   36 import qualified Data.ByteString.Lazy.Char8    as LBS
+   37 import           Data.Hashable
+   38 import qualified Data.Text                     as T
+   39 import           Graphics.SvgTree                         ( Number(..)
+   40                                                           , Tree(..)
+   41                                                           , defaultSvg
+   42                                                           , parseSvgFile
+   43                                                           )
+   44 import qualified Graphics.SvgTree              as Svg
+   45 import           Reanimate.Animation
+   46 import           Reanimate.Cache
+   47 import           Reanimate.Driver.Magick
+   48 import           Reanimate.Misc
+   49 import           Reanimate.Render
+   50 import           Reanimate.Parameters
+   51 import           Reanimate.Constants
+   52 import           Reanimate.Svg.Constructors
+   53 import           Reanimate.Svg.Unuse
+   54 import           System.Directory
+   55 import           System.FilePath
+   56 import           System.IO
+   57 import           System.IO.Temp
+   58 import           System.IO.Unsafe
+   59 
+   60 -- | Load an external image. Width and height must be specified,
+   61 --   ignoring the image's aspect ratio. The center of the image is
+   62 --   placed at position (0,0).
+   63 --
+   64 --   For security reasons, must SVG renderer do not allow arbitrary
+   65 --   image links. For some renderers, we can get around this by placing
+   66 --   the images in the same root directory as the parent SVG file. Other
+   67 --   renderers (like Chrome and ffmpeg) requires that the image is inlined
+   68 --   as base64 data. External SVG files are an exception, though, as must
+   69 --   always be inlined directly. `mkImage` attempts to hide all the complexity
+   70 --   but edge-cases may exist.
+   71 --
+   72 --   Example:
+   73 --
+   74 --   > mkImage screenWidth screenHeight "../data/haskell.svg"
+   75 --
+   76 --   <<docs/gifs/doc_mkImage.gif>>
+   77 mkImage
+   78   :: Double -- ^ Desired image width.
+   79   -> Double -- ^ Desired image height.
+   80   -> FilePath -- ^ Path to external image file.
+   81   -> SVG
+   82 mkImage width height path | takeExtension path == ".svg" = unsafePerformIO $ do
+   83   svg_data <- B.readFile path
+   84   case parseSvgFile path svg_data of
+   85     Nothing -> error "Malformed svg"
+   86     Just svg ->
+   87       return
+   88         $ scaleXY (width / screenWidth) (height / screenHeight)
+   89         $ embedDocument svg
+   90 mkImage width height path | pRaster == RasterNone = unsafePerformIO $ do
+   91   inp <- LBS.readFile path
+   92   let imgData = LBS.unpack $ Base64.encode inp
+   93   return
+   94     $  flipYAxis
+   95     $  ImageTree
+   96     $  defaultSvg
+   97     &  Svg.imageWidth
+   98     .~ Svg.Num width
+   99     &  Svg.imageHeight
+  100     .~ Svg.Num height
+  101     &  Svg.imageHref
+  102     .~ ("data:" ++ mimeType ++ ";base64," ++ imgData)
+  103     &  Svg.imageCornerUpperLeft
+  104     .~ (Svg.Num (-width / 2), Svg.Num (-height / 2))
+  105     &  Svg.imageAspectRatio
+  106     .~ Svg.PreserveAspectRatio False Svg.AlignNone Nothing
+  107  where
+  108     -- FIXME: Is there a better way to do this?
+  109   mimeType = case takeExtension path of
+  110     ".jpg" -> "image/jpeg"
+  111     ext    -> "image/" ++ drop 1 ext
+  112 mkImage width height path = unsafePerformIO $ do
+  113   exists <- doesFileExist target
+  114   unless exists $ copyFile path target
+  115   return
+  116     $  flipYAxis
+  117     $  ImageTree
+  118     $  defaultSvg
+  119     &  Svg.imageWidth
+  120     .~ Svg.Num width
+  121     &  Svg.imageHeight
+  122     .~ Svg.Num height
+  123     &  Svg.imageHref
+  124     .~ ("file://" ++ target)
+  125     &  Svg.imageCornerUpperLeft
+  126     .~ (Svg.Num (-width / 2), Svg.Num (-height / 2))
+  127     &  Svg.imageAspectRatio
+  128     .~ Svg.PreserveAspectRatio False Svg.AlignNone Nothing
+  129  where
+  130   target   = pRootDirectory </> encodeInt hashPath <.> takeExtension path
+  131   hashPath = hash path
+  132 
+  133 -- | Write in-memory image to cache file if (and only if) such cache file doesn't
+  134 --   already exist.
+  135 cacheImage :: (PngSavable pixel, Hashable a) => a -> Image pixel -> FilePath
+  136 cacheImage key gen = unsafePerformIO $ cacheFile template $ \path ->
+  137   writePng path gen
+  138   where template = encodeInt (hash key) <.> "png"
+  139 
+  140 -- Warning: Caching svg elements with links to external objects does
+  141 --          not work. 2020-06-01
+  142 -- | Same as 'prerenderSvg' but returns the location of the rendered image
+  143 --   as a FilePath.
+  144 prerenderSvgFile :: Hashable a => a -> Width -> Height -> SVG -> FilePath
+  145 prerenderSvgFile key width height svg =
+  146   unsafePerformIO $ cacheFile template $ \path -> do
+  147     let svgPath = replaceExtension path "svg"
+  148     writeFile svgPath rendered
+  149     engine <- requireRaster pRaster
+  150     applyRaster engine svgPath
+  151  where
+  152   template = encodeInt (hash (key, width, height)) <.> "png"
+  153   rendered = renderSvg (Just $ Px $ fromIntegral width)
+  154                        (Just $ Px $ fromIntegral height)
+  155                        svg
+  156 
+  157 -- | Render SVG node to a PNG file and return a new node containing
+  158 --   that image. For static SVG nodes, this can hugely improve performance.
+  159 --   The first argument is the key that determines SVG uniqueness. It
+  160 --   is entirely your responsibility to ensure that all keys are unique.
+  161 --   If they are not, you will be served stale results from the cache.
+  162 prerenderSvg :: Hashable a => a -> SVG -> SVG
+  163 prerenderSvg key =
+  164   mkImage screenWidth screenHeight . prerenderSvgFile key pWidth pHeight
+  165 
   166 
-  167 -- | Embed in-memory PNG bytestring without parsing it.
-  168 embedPng
-  169   :: Double -- ^ Width
-  170   -> Double -- ^ Height
-  171   -> LBS.ByteString -- ^ Raw PNG data
-  172   -> SVG
-  173 -- embedPng w h png = unsafePerformIO $ do
-  174 --     LBS.writeFile path png
-  175 --     return $ ImageTree $ defaultSvg
-  176 --       & Svg.imageCornerUpperLeft .~ (Svg.Num (-w/2), Svg.Num (-h/2))
-  177 --       & Svg.imageWidth .~ Svg.Num w
-  178 --       & Svg.imageHeight .~ Svg.Num h
-  179 --       & Svg.imageHref .~ ("file://"++path)
-  180 --   where
-  181 --     path = "/tmp" </> show (hash png) <.> "png"
-  182 embedPng w h png =
-  183   flipYAxis
-  184     $  ImageTree
-  185     $  defaultSvg
-  186     &  Svg.imageCornerUpperLeft
-  187     .~ (Svg.Num (-w / 2), Svg.Num (-h / 2))
-  188     &  Svg.imageWidth
-  189     .~ Svg.Num w
-  190     &  Svg.imageHeight
-  191     .~ Svg.Num h
-  192     &  Svg.imageHref
-  193     .~ ("data:image/png;base64," ++ imgData)
-  194   where imgData = LBS.unpack $ Base64.encode png
-  195 
-  196 
-  197 {-# INLINE embedDynamicImage #-}
-  198 -- | Embed an in-memory image. Note, the pixel size of the image
-  199 --   is used as the dimensions. As such, embedding a 100x100 image will
-  200 --   result in an image 100 units wide and 100 units high. Consider
-  201 --   using with 'scaleToSize'.
-  202 embedDynamicImage :: DynamicImage -> SVG
-  203 embedDynamicImage img = embedPng width height imgData
-  204  where
-  205   width   = fromIntegral $ dynamicMap imageWidth img
-  206   height  = fromIntegral $ dynamicMap imageHeight img
-  207   imgData = case encodeDynamicPng img of
-  208     Left  err -> error err
-  209     Right dat -> dat
-  210 
-  211 -- embedImageFile :: FilePath -> Tree
-  212 -- embedImageFile path = unsafePerformIO $ do
-  213 --     png <- B.readFile path
-  214 --     case decodePng png of
-  215 --       Left{}    -> error "bad image"
-  216 --       Right img -> return $
-  217 --         let width   = fromIntegral $ dynamicMap imageWidth img
-  218 --             height  = fromIntegral $ dynamicMap imageHeight img in
-  219 --         ImageTree $ defaultSvg
-  220 --           & Svg.imageCornerUpperLeft .~ (Svg.Num (-width/2), Svg.Num (-height/2))
-  221 --           & Svg.imageWidth .~ Svg.Num width
-  222 --           & Svg.imageHeight .~ Svg.Num height
-  223 --           & Svg.imageHref .~ ("file://" ++ path)
-  224 
-  225 
-  226 -- | Convert an SVG object to a pixel-based image. The default resolution
-  227 --   is 2560x1440. See also 'rasterSized'. Multiple raster engines are supported
-  228 --   and are selected using the '--raster' flag in the driver.
-  229 raster :: SVG -> DynamicImage
-  230 raster = rasterSized 2560 1440
-  231 
-  232 -- | Convert an SVG object to a pixel-based image.
-  233 rasterSized
-  234   :: Width  -- ^ X resolution in pixels
-  235   -> Height -- ^ Y resolution in pixels
-  236   -> SVG    -- ^ SVG object
-  237   -> DynamicImage
-  238 rasterSized w h svg = unsafePerformIO $ do
-  239   png <- B.readFile (svgAsPngFile' w h svg)
-  240   case decodePng png of
-  241     Left{}    -> error "bad image"
-  242     Right img -> return img
-  243 
-  244 -- | Use 'potrace' to trace edges in a raster image and convert them to SVG polygons.
-  245 vectorize :: FilePath -> SVG
-  246 vectorize = vectorize_ []
-  247 
-  248 -- | Same as 'vectorize' but takes a list of arguments for 'potrace'.
-  249 vectorize_ :: [String] -> FilePath -> SVG
-  250 vectorize_ _ path | pNoExternals = mkText $ T.pack path
-  251 vectorize_ args path             = unsafePerformIO $ do
-  252   root <- getXdgDirectory XdgCache "reanimate"
-  253   createDirectoryIfMissing True root
-  254   let svgPath = root </> encodeInt key <.> "svg"
-  255   hit <- doesFileExist svgPath
-  256   unless hit $ withSystemTempFile "file.svg" $ \tmpSvgPath svgH ->
-  257     withSystemTempFile "file.bmp" $ \tmpBmpPath bmpH -> do
-  258       hClose svgH
-  259       hClose bmpH
-  260       potrace <- requireExecutable "potrace"
-  261       magick <- requireExecutable magickCmd
-  262       runCmd magick [path, "-flatten", tmpBmpPath]
-  263       runCmd potrace (args ++ ["--svg", "--output", tmpSvgPath, tmpBmpPath])
-  264       renameOrCopyFile tmpSvgPath svgPath
-  265   svg_data <- B.readFile svgPath
-  266   case parseSvgFile svgPath svg_data of
-  267     Nothing -> do
-  268       removeFile svgPath
-  269       error "Malformed svg"
-  270     Just svg -> return $ unbox $ replaceUses svg
-  271   where key = hash (path, args)
-  272 
-  273 -- imageAsFile :: DynamicImage -> FilePath
-  274 -- imageAsFile img
-  275 
-  276 -- | Convert an SVG object to a pixel-based image and save it to disk, returning
-  277 --   the filepath. The default resolution is 2560x1440. See also 'svgAsPngFile''.
-  278 --   Multiple raster engines are supported and are selected using the '--raster'
-  279 --   flag in the driver.
-  280 svgAsPngFile :: SVG -> FilePath
-  281 svgAsPngFile = svgAsPngFile' width height
-  282  where
-  283   width  = 2560
-  284   height = width * 9 `div` 16
-  285 
-  286 -- | Convert an SVG object to a pixel-based image and save it to disk, returning
-  287 --   the filepath.
-  288 svgAsPngFile'
-  289   :: Width  -- ^ Width
-  290   -> Height -- ^ Height
-  291   -> SVG    -- ^ SVG object
-  292   -> FilePath
-  293 svgAsPngFile' _ _ _ | pNoExternals = "/svgAsPngFile/has/been/disabled"
-  294 svgAsPngFile' width height svg =
-  295   unsafePerformIO $ cacheFile template $ \pngPath -> do
-  296     let svgPath = replaceExtension pngPath "svg"
-  297     writeFile svgPath rendered
-  298     engine <- requireRaster pRaster
-  299     applyRaster engine svgPath
-  300  where
-  301   template = encodeInt (hash rendered) <.> "png"
-  302   rendered = renderSvg (Just $ Px $ fromIntegral width)
-  303                        (Just $ Px $ fromIntegral height)
-  304                        svg
+  167 {-# INLINE embedImage #-}
+  168 -- | Embed an in-memory PNG image. Note, the pixel size of the image
+  169 --   is used as the dimensions. As such, embedding a 100x100 PNG will
+  170 --   result in an image 100 units wide and 100 units high. Consider
+  171 --   using with 'scaleToSize'.
+  172 embedImage :: PngSavable a => Image a -> SVG
+  173 embedImage img = embedPng width height (encodePng img)
+  174  where
+  175   width  = fromIntegral $ imageWidth img
+  176   height = fromIntegral $ imageHeight img
+  177 
+  178 -- | Embed in-memory PNG bytestring without parsing it.
+  179 embedPng
+  180   :: Double -- ^ Width
+  181   -> Double -- ^ Height
+  182   -> LBS.ByteString -- ^ Raw PNG data
+  183   -> SVG
+  184 -- embedPng w h png = unsafePerformIO $ do
+  185 --     LBS.writeFile path png
+  186 --     return $ ImageTree $ defaultSvg
+  187 --       & Svg.imageCornerUpperLeft .~ (Svg.Num (-w/2), Svg.Num (-h/2))
+  188 --       & Svg.imageWidth .~ Svg.Num w
+  189 --       & Svg.imageHeight .~ Svg.Num h
+  190 --       & Svg.imageHref .~ ("file://"++path)
+  191 --   where
+  192 --     path = "/tmp" </> show (hash png) <.> "png"
+  193 embedPng w h png =
+  194   flipYAxis
+  195     $  ImageTree
+  196     $  defaultSvg
+  197     &  Svg.imageCornerUpperLeft
+  198     .~ (Svg.Num (-w / 2), Svg.Num (-h / 2))
+  199     &  Svg.imageWidth
+  200     .~ Svg.Num w
+  201     &  Svg.imageHeight
+  202     .~ Svg.Num h
+  203     &  Svg.imageHref
+  204     .~ ("data:image/png;base64," ++ imgData)
+  205   where imgData = LBS.unpack $ Base64.encode png
+  206 
+  207 
+  208 {-# INLINE embedDynamicImage #-}
+  209 -- | Embed an in-memory image. Note, the pixel size of the image
+  210 --   is used as the dimensions. As such, embedding a 100x100 image will
+  211 --   result in an image 100 units wide and 100 units high. Consider
+  212 --   using with 'scaleToSize'.
+  213 embedDynamicImage :: DynamicImage -> SVG
+  214 embedDynamicImage img = embedPng width height imgData
+  215  where
+  216   width   = fromIntegral $ dynamicMap imageWidth img
+  217   height  = fromIntegral $ dynamicMap imageHeight img
+  218   imgData = case encodeDynamicPng img of
+  219     Left  err -> error err
+  220     Right dat -> dat
+  221 
+  222 -- embedImageFile :: FilePath -> Tree
+  223 -- embedImageFile path = unsafePerformIO $ do
+  224 --     png <- B.readFile path
+  225 --     case decodePng png of
+  226 --       Left{}    -> error "bad image"
+  227 --       Right img -> return $
+  228 --         let width   = fromIntegral $ dynamicMap imageWidth img
+  229 --             height  = fromIntegral $ dynamicMap imageHeight img in
+  230 --         ImageTree $ defaultSvg
+  231 --           & Svg.imageCornerUpperLeft .~ (Svg.Num (-width/2), Svg.Num (-height/2))
+  232 --           & Svg.imageWidth .~ Svg.Num width
+  233 --           & Svg.imageHeight .~ Svg.Num height
+  234 --           & Svg.imageHref .~ ("file://" ++ path)
+  235 
+  236 
+  237 -- | Convert an SVG object to a pixel-based image. The default resolution
+  238 --   is 2560x1440. See also 'rasterSized'. Multiple raster engines are supported
+  239 --   and are selected using the '--raster' flag in the driver.
+  240 raster :: SVG -> DynamicImage
+  241 raster = rasterSized 2560 1440
+  242 
+  243 -- | Convert an SVG object to a pixel-based image.
+  244 rasterSized
+  245   :: Width  -- ^ X resolution in pixels
+  246   -> Height -- ^ Y resolution in pixels
+  247   -> SVG    -- ^ SVG object
+  248   -> DynamicImage
+  249 rasterSized w h svg = unsafePerformIO $ do
+  250   png <- B.readFile (svgAsPngFile' w h svg)
+  251   case decodePng png of
+  252     Left{}    -> error "bad image"
+  253     Right img -> return img
+  254 
+  255 -- | Use 'potrace' to trace edges in a raster image and convert them to SVG polygons.
+  256 vectorize :: FilePath -> SVG
+  257 vectorize = vectorize_ []
+  258 
+  259 -- | Same as 'vectorize' but takes a list of arguments for 'potrace'.
+  260 vectorize_ :: [String] -> FilePath -> SVG
+  261 vectorize_ _ path | pNoExternals = mkText $ T.pack path
+  262 vectorize_ args path             = unsafePerformIO $ do
+  263   root <- getXdgDirectory XdgCache "reanimate"
+  264   createDirectoryIfMissing True root
+  265   let svgPath = root </> encodeInt key <.> "svg"
+  266   hit <- doesFileExist svgPath
+  267   unless hit $ withSystemTempFile "file.svg" $ \tmpSvgPath svgH ->
+  268     withSystemTempFile "file.bmp" $ \tmpBmpPath bmpH -> do
+  269       hClose svgH
+  270       hClose bmpH
+  271       potrace <- requireExecutable "potrace"
+  272       magick <- requireExecutable magickCmd
+  273       runCmd magick [path, "-flatten", tmpBmpPath]
+  274       runCmd potrace (args ++ ["--svg", "--output", tmpSvgPath, tmpBmpPath])
+  275       renameOrCopyFile tmpSvgPath svgPath
+  276   svg_data <- B.readFile svgPath
+  277   case parseSvgFile svgPath svg_data of
+  278     Nothing -> do
+  279       removeFile svgPath
+  280       error "Malformed svg"
+  281     Just svg -> return $ unbox $ replaceUses svg
+  282   where key = hash (path, args)
+  283 
+  284 -- imageAsFile :: DynamicImage -> FilePath
+  285 -- imageAsFile img
+  286 
+  287 -- | Convert an SVG object to a pixel-based image and save it to disk, returning
+  288 --   the filepath. The default resolution is 2560x1440. See also 'svgAsPngFile''.
+  289 --   Multiple raster engines are supported and are selected using the '--raster'
+  290 --   flag in the driver.
+  291 svgAsPngFile :: SVG -> FilePath
+  292 svgAsPngFile = svgAsPngFile' width height
+  293  where
+  294   width  = 2560
+  295   height = width * 9 `div` 16
+  296 
+  297 -- | Convert an SVG object to a pixel-based image and save it to disk, returning
+  298 --   the filepath.
+  299 svgAsPngFile'
+  300   :: Width  -- ^ Width
+  301   -> Height -- ^ Height
+  302   -> SVG    -- ^ SVG object
+  303   -> FilePath
+  304 svgAsPngFile' _ _ _ | pNoExternals = "/svgAsPngFile/has/been/disabled"
+  305 svgAsPngFile' width height svg =
+  306   unsafePerformIO $ cacheFile template $ \pngPath -> do
+  307     let svgPath = replaceExtension pngPath "svg"
+  308     writeFile svgPath rendered
+  309     engine <- requireRaster pRaster
+  310     applyRaster engine svgPath
+  311  where
+  312   template = encodeInt (hash rendered) <.> "png"
+  313   rendered = renderSvg (Just $ Px $ fromIntegral width)
+  314                        (Just $ Px $ fromIntegral height)
+  315                        svg