mirror of
https://github.com/reanimate/reanimate.git
synced 2026-09-16 18:42:21 +00:00
Better caching, bundle viewer.
Former-commit-id: cacfc7fd5402fadd381d16c748cdfdbf97a63cd9
This commit is contained in:
parent
c07f4c18ae
commit
a4121d01d9
27 changed files with 296 additions and 359 deletions
|
|
@ -1,5 +1,6 @@
|
|||
#!/usr/bin/env stack
|
||||
-- stack --resolver lts-11.22 runghc --package reanimate
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
module Main (main) where
|
||||
|
||||
import Control.Lens
|
||||
|
|
|
|||
|
|
@ -13,6 +13,14 @@ build-type: Simple
|
|||
extra-source-files: ChangeLog.md
|
||||
cabal-version: >=1.10
|
||||
|
||||
data-files: viewer/build/*.js
|
||||
viewer/build/*.html
|
||||
viewer/build/static/js/2.772a56e7.chunk.js
|
||||
viewer/build/static/js/main.db22f45d.chunk.js
|
||||
viewer/build/static/js/runtime~main.9eb600ee.js
|
||||
viewer/build/static/css/main.6efe09fd.chunk.css
|
||||
|
||||
|
||||
library
|
||||
hs-source-dirs: src
|
||||
default-language: Haskell2010
|
||||
|
|
@ -28,12 +36,15 @@ library
|
|||
Reanimate.Driver
|
||||
Reanimate.Misc
|
||||
other-modules: Reanimate.Svg.NamedColors
|
||||
Reanimate.Cache
|
||||
Paths_reanimate
|
||||
build-depends: base >=4.10 && <4.13,
|
||||
time, text, unix, filepath, process, directory,
|
||||
containers, reanimate-svg >= 0.7.0.0, xml, bytestring, lens, linear, mtl, matrix,
|
||||
JuicyPixels, attoparsec, parallel, diagrams, diagrams-svg,
|
||||
diagrams-core, diagrams-lib, diagrams-contrib,
|
||||
svg-builder, matrices, cubicbezier, palette, hinotify, websockets
|
||||
svg-builder, matrices, cubicbezier, palette, hinotify, websockets,
|
||||
hashable
|
||||
|
||||
Flag gtk-viewer
|
||||
Description: Enable gtk-based viewer
|
||||
|
|
|
|||
81
src/Reanimate/Cache.hs
Normal file
81
src/Reanimate/Cache.hs
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
module Reanimate.Cache
|
||||
( cacheMem
|
||||
, cacheDisk
|
||||
, cacheDiskSvg
|
||||
, cacheDiskLines
|
||||
) where
|
||||
|
||||
import Control.Exception
|
||||
import Data.Hashable
|
||||
import Data.IORef
|
||||
import Data.Map (Map)
|
||||
import qualified Data.Map as Map
|
||||
import Data.Text (Text)
|
||||
import qualified Data.Text as T
|
||||
import qualified Data.Text.Encoding as T
|
||||
import qualified Data.Text.IO as T
|
||||
import Graphics.SvgTree (Tree (..), parseSvgFile, unparse)
|
||||
import Reanimate.Monad (renderTree)
|
||||
import Reanimate.Svg (unbox)
|
||||
import Text.XML.Light ( Content(..), parseXML )
|
||||
import System.Directory
|
||||
import System.FilePath
|
||||
import System.IO.Unsafe
|
||||
|
||||
-- Memory cache and disk cache
|
||||
|
||||
cacheDisk :: (T.Text -> Maybe a) -> (a -> T.Text) -> (Text -> IO a) -> (Text -> IO a)
|
||||
cacheDisk parse render gen key = do
|
||||
root <- getXdgDirectory XdgCache "reanimate"
|
||||
createDirectoryIfMissing True root
|
||||
let path = root </> show (hash key)
|
||||
hit <- doesFileExist path
|
||||
if hit
|
||||
then do
|
||||
inp <- T.readFile path
|
||||
case parse inp of
|
||||
Nothing -> do
|
||||
let tmp = path <.> "tmp"
|
||||
new <- gen key
|
||||
T.writeFile tmp (render new)
|
||||
renameFile tmp path
|
||||
return new
|
||||
Just val -> pure val
|
||||
else do
|
||||
let tmp = path <.> "tmp"
|
||||
new <- gen key
|
||||
T.writeFile tmp (render new)
|
||||
renameFile tmp path
|
||||
return new
|
||||
|
||||
cacheDiskSvg :: (Text -> IO Tree) -> (Text -> IO Tree)
|
||||
cacheDiskSvg = cacheDisk parse render
|
||||
where
|
||||
parse txt = case parseXML txt of
|
||||
[Elem t] -> Just (unparse t)
|
||||
_ -> Nothing
|
||||
render = T.pack . renderTree
|
||||
|
||||
cacheDiskLines :: (Text -> IO [Text]) -> (Text -> IO [Text])
|
||||
cacheDiskLines = cacheDisk parse render
|
||||
where
|
||||
parse = Just . T.lines
|
||||
render = T.unlines
|
||||
|
||||
|
||||
{-# NOINLINE cache #-}
|
||||
cache :: IORef (Map Text Tree)
|
||||
cache = unsafePerformIO (newIORef Map.empty)
|
||||
|
||||
cacheMem :: (Text -> IO Tree) -> (Text -> IO Tree)
|
||||
cacheMem gen key = do
|
||||
store <- readIORef cache
|
||||
case Map.lookup key store of
|
||||
Just svg -> return svg
|
||||
Nothing -> do
|
||||
svg <- gen key
|
||||
case svg of
|
||||
-- None usually indicates that latex or another tool was misconfigured. In this case,
|
||||
-- don't store the result.
|
||||
None -> pure None
|
||||
_ -> atomicModifyIORef cache (\store -> (Map.insert key svg store, svg))
|
||||
|
|
@ -11,10 +11,12 @@ import System.INotify (EventVariety (..), addWatch, withINotify)
|
|||
import System.IO (BufferMode (..), hPutStrLn, hSetBuffering,
|
||||
stderr, stdin)
|
||||
|
||||
import Reanimate.Misc (runCmdLazy, runCmd_, withTempFile)
|
||||
import Reanimate.Misc (runCmdLazy, runCmd, runCmd_, withTempDir, withTempFile)
|
||||
import Reanimate.Monad (Animation)
|
||||
import Reanimate.Render (renderSvgs)
|
||||
|
||||
import Paths_reanimate
|
||||
|
||||
opts = defaultConnectionOptions
|
||||
{ connectionCompressionOptions = PermessageDeflateCompression defaultPermessageDeflate }
|
||||
|
||||
|
|
@ -24,50 +26,55 @@ reanimate animation = do
|
|||
hSetBuffering stdin NoBuffering
|
||||
case args of
|
||||
["once"] -> renderSvgs animation
|
||||
_ -> runServerWith "127.0.0.1" 9161 opts $ \pending -> do
|
||||
putStrLn "Server pending."
|
||||
prog <- getProgName
|
||||
lst <- listDirectory "."
|
||||
mbSelf <- findFile ("." : lst) prog
|
||||
blocker <- newEmptyMVar :: IO (MVar ())
|
||||
case mbSelf of
|
||||
Nothing -> do
|
||||
hPutStrLn stderr "Failed to find own source code."
|
||||
Just self -> withINotify $ \notify -> do
|
||||
conn <- acceptRequest pending
|
||||
slave <- newEmptyMVar
|
||||
let handler = modifyMVar_ slave $ \tid -> do
|
||||
sendTextData conn (T.pack "Compiling")
|
||||
putStrLn "Kill and respawn."
|
||||
killThread tid
|
||||
tid <- forkIO $ withTempFile ".exe" $ \tmpExecutable -> do
|
||||
ret <- runCmd_ "stack" $ ["ghc", "--"] ++ ghcOptions ++ [self, "-o", tmpExecutable]
|
||||
case ret of
|
||||
Left err ->
|
||||
sendTextData conn $ T.pack $ "Error" ++ unlines (drop 3 (lines err))
|
||||
Right{} -> do
|
||||
getFrame <- runCmdLazy tmpExecutable ["once", "+RTS", "-N", "-M200M", "-RTS"]
|
||||
flip fix [] $ \loop acc -> do
|
||||
frame <- getFrame
|
||||
case frame of
|
||||
Left "" -> do
|
||||
sendTextData conn (T.pack "Done")
|
||||
-- insertCache msg (reverse acc)
|
||||
Left err -> do
|
||||
-- _ <- getChanContents queue
|
||||
sendTextData conn $ T.pack $ "Error" ++ err
|
||||
Right frame -> do
|
||||
sendTextData conn frame
|
||||
loop (frame : acc)
|
||||
return tid
|
||||
putStrLn "Found self. Listening."
|
||||
addWatch notify [Modify] self (const handler)
|
||||
putMVar slave =<< forkIO (return ())
|
||||
let loop = do
|
||||
fps <- receiveData conn :: IO T.Text
|
||||
handler
|
||||
loop
|
||||
loop
|
||||
_ -> withTempDir $ \tmpDir -> do
|
||||
url <- getDataFileName "viewer/build/index.html"
|
||||
runCmd "xdg-open" [url]
|
||||
runServerWith "127.0.0.1" 9161 opts $ \pending -> do
|
||||
putStrLn "Server pending."
|
||||
prog <- getProgName
|
||||
lst <- listDirectory "."
|
||||
mbSelf <- findFile ("." : lst) prog
|
||||
blocker <- newEmptyMVar :: IO (MVar ())
|
||||
case mbSelf of
|
||||
Nothing -> do
|
||||
hPutStrLn stderr "Failed to find own source code."
|
||||
Just self -> withINotify $ \notify -> do
|
||||
conn <- acceptRequest pending
|
||||
slave <- newEmptyMVar
|
||||
let handler = modifyMVar_ slave $ \tid -> do
|
||||
sendTextData conn (T.pack "Compiling")
|
||||
putStrLn "Kill and respawn."
|
||||
killThread tid
|
||||
tid <- forkIO $ withTempFile ".exe" $ \tmpExecutable -> do
|
||||
ret <- runCmd_ "stack" $ ["ghc", "--"] ++ ghcOptions tmpDir ++ [self, "-o", tmpExecutable]
|
||||
case ret of
|
||||
Left err ->
|
||||
sendTextData conn $ T.pack $ "Error" ++ unlines (drop 3 (lines err))
|
||||
Right{} -> do
|
||||
getFrame <- runCmdLazy tmpExecutable ["once", "+RTS", "-N", "-M200M", "-RTS"]
|
||||
flip fix [] $ \loop acc -> do
|
||||
frame <- getFrame
|
||||
case frame of
|
||||
Left "" -> do
|
||||
sendTextData conn (T.pack "Done")
|
||||
-- insertCache msg (reverse acc)
|
||||
Left err -> do
|
||||
-- _ <- getChanContents queue
|
||||
sendTextData conn $ T.pack $ "Error" ++ err
|
||||
Right frame -> do
|
||||
sendTextData conn frame
|
||||
loop (frame : acc)
|
||||
return tid
|
||||
putStrLn "Found self. Listening."
|
||||
addWatch notify [Modify] self (const handler)
|
||||
putMVar slave =<< forkIO (return ())
|
||||
let loop = do
|
||||
fps <- receiveData conn :: IO T.Text
|
||||
handler
|
||||
loop
|
||||
loop
|
||||
|
||||
ghcOptions :: [String]
|
||||
ghcOptions = ["-rtsopts", "--make", "-threaded", "-O2"]
|
||||
ghcOptions :: FilePath -> [String]
|
||||
ghcOptions tmpDir =
|
||||
["-rtsopts", "--make", "-threaded", "-O2"] ++
|
||||
["-odir", tmpDir, "-hidir", tmpDir]
|
||||
|
|
|
|||
|
|
@ -7,74 +7,36 @@ import qualified Data.ByteString as B
|
|||
import Data.IORef
|
||||
import Data.Map (Map)
|
||||
import qualified Data.Map as Map
|
||||
import Data.Monoid
|
||||
import Reanimate.Cache
|
||||
import Reanimate.Misc
|
||||
import Reanimate.Svg
|
||||
import System.FilePath (replaceExtension, takeFileName, (</>))
|
||||
import System.IO.Unsafe (unsafePerformIO)
|
||||
|
||||
import Control.Lens (over, set, (%~), (&), (.~), (^.))
|
||||
import Data.Text (Text)
|
||||
import qualified Data.Text as T
|
||||
import qualified Data.Text.IO as T
|
||||
import Graphics.SvgTree (Document (..), Tree (..), defaultSvg,
|
||||
elements, loadSvgFile, parseSvgFile,
|
||||
xmlOfDocument)
|
||||
import Text.XML.Light (elContent)
|
||||
import Text.XML.Light.Output (ppcContent, ppcElement, prettyConfigPP)
|
||||
|
||||
-- instance ToHtml Document where
|
||||
-- toHtml = toHtmlRaw
|
||||
-- toHtmlRaw = toHtmlRaw . ppcElement prettyConfigPP . xmlOfDocument
|
||||
latex :: T.Text -> Tree
|
||||
latex tex = (unsafePerformIO . (cacheMem . cacheDiskSvg) latexToSVG)
|
||||
("% plain latex\n" <> tex)
|
||||
|
||||
-- instance ToHtml Document where
|
||||
-- toHtml = toHtmlRaw
|
||||
-- toHtmlRaw doc = toHtmlRaw $ unlines $ map (ppcContent prettyConfigPP) (elContent elt)
|
||||
-- where
|
||||
-- elt = xmlOfDocument doc
|
||||
--
|
||||
-- instance ToHtml Tree where
|
||||
-- toHtml = toHtmlRaw
|
||||
-- toHtmlRaw tree = toHtmlRaw doc
|
||||
-- where
|
||||
-- doc = Document
|
||||
-- { _viewBox = Nothing
|
||||
-- , _width = Nothing
|
||||
-- , _height = Nothing
|
||||
-- , _elements = [tree]
|
||||
-- , _definitions = Map.empty
|
||||
-- , _description = ""
|
||||
-- , _styleRules = []
|
||||
-- , _documentLocation = ""
|
||||
-- }
|
||||
xelatex :: Text -> Tree
|
||||
xelatex tex = (unsafePerformIO . (cacheMem . cacheDiskSvg) latexToSVG)
|
||||
("% xelatex\n" <> tex)
|
||||
|
||||
{-# NOINLINE cache #-}
|
||||
cache :: IORef (Map String Tree)
|
||||
cache = unsafePerformIO (newIORef Map.empty)
|
||||
|
||||
latex :: String -> Tree
|
||||
latex tex = unsafePerformIO $ do
|
||||
store <- readIORef cache
|
||||
case Map.lookup tex store of
|
||||
Just svg -> return svg
|
||||
Nothing -> do
|
||||
svg <- latexToSVG tex
|
||||
case svg of
|
||||
None -> pure None
|
||||
_ -> atomicModifyIORef cache (\store -> (Map.insert tex svg store, svg))
|
||||
|
||||
xelatex :: String -> Tree
|
||||
xelatex tex = unsafePerformIO $ do
|
||||
store <- readIORef cache
|
||||
case Map.lookup tex store of
|
||||
Just svg -> return svg
|
||||
Nothing -> do
|
||||
svg <- xelatexToSVG tex
|
||||
case svg of
|
||||
None -> pure None
|
||||
_ -> atomicModifyIORef cache (\store -> (Map.insert tex svg store, svg))
|
||||
|
||||
latexAlign :: String -> Tree
|
||||
latexAlign tex = latex $ unlines ["\\begin{align*}", tex, "\\end{align*}"]
|
||||
latexAlign :: Text -> Tree
|
||||
latexAlign tex = latex $ T.unlines ["\\begin{align*}", tex, "\\end{align*}"]
|
||||
|
||||
|
||||
latexToSVG :: String -> IO Tree
|
||||
latexToSVG :: Text -> IO Tree
|
||||
latexToSVG tex = handle (\(e::SomeException) -> return (failedSvg tex)) $ do
|
||||
latex <- requireExecutable "latex"
|
||||
dvisvgm <- requireExecutable "dvisvgm"
|
||||
|
|
@ -82,7 +44,7 @@ latexToSVG tex = handle (\(e::SomeException) -> return (failedSvg tex)) $ do
|
|||
let dvi_file = tmp_dir </> replaceExtension (takeFileName tex_file) "dvi"
|
||||
writeFile tex_file tex_document
|
||||
appendFile tex_file tex_prologue
|
||||
appendFile tex_file tex
|
||||
T.appendFile tex_file tex
|
||||
appendFile tex_file tex_epilogue
|
||||
runCmd latex ["-interaction=batchmode", "-halt-on-error", "-output-directory="++tmp_dir, tex_file]
|
||||
runCmd dvisvgm [ dvi_file
|
||||
|
|
@ -95,7 +57,7 @@ latexToSVG tex = handle (\(e::SomeException) -> return (failedSvg tex)) $ do
|
|||
Nothing -> error "Malformed svg"
|
||||
Just svg -> return $ unbox $ replaceUses svg
|
||||
|
||||
xelatexToSVG :: String -> IO Tree
|
||||
xelatexToSVG :: Text -> IO Tree
|
||||
xelatexToSVG tex = handle (\(e::SomeException) -> return (failedSvg tex)) $ do
|
||||
latex <- requireExecutable "xelatex"
|
||||
dvisvgm <- requireExecutable "dvisvgm"
|
||||
|
|
@ -104,7 +66,7 @@ xelatexToSVG tex = handle (\(e::SomeException) -> return (failedSvg tex)) $ do
|
|||
writeFile tex_file tex_document
|
||||
appendFile tex_file tex_xelatex
|
||||
appendFile tex_file tex_prologue
|
||||
appendFile tex_file tex
|
||||
T.appendFile tex_file tex
|
||||
appendFile tex_file tex_epilogue
|
||||
runCmd latex ["-no-pdf", "-interaction=batchmode", "-halt-on-error", "-output-directory="++tmp_dir, tex_file]
|
||||
runCmd dvisvgm [ dvi_file
|
||||
|
|
@ -117,7 +79,7 @@ xelatexToSVG tex = handle (\(e::SomeException) -> return (failedSvg tex)) $ do
|
|||
Nothing -> error "Malformed svg"
|
||||
Just svg -> return $ unbox $ replaceUses svg
|
||||
|
||||
failedSvg :: String -> Tree
|
||||
failedSvg :: Text -> Tree
|
||||
failedSvg tex = defaultSvg
|
||||
-- text_ [ font_size_ "20"
|
||||
-- , fill_ "white"] (toHtml $ "bad latex: "++tex)
|
||||
|
|
|
|||
|
|
@ -10,8 +10,7 @@ import Data.Monoid ((<>))
|
|||
import Data.Text (Text, pack)
|
||||
import Graphics.SvgTree (Document (..), Number (..), Text (..),
|
||||
TextSpan (..), TextSpanContent (..),
|
||||
Tree, Tree (..), xmlOfDocument)
|
||||
import Reanimate.LaTeX
|
||||
Tree, Tree (..), xmlOfDocument, xmlOfTree)
|
||||
import Reanimate.Svg
|
||||
import Text.XML.Light (elContent)
|
||||
import Text.XML.Light.Output
|
||||
|
|
@ -101,10 +100,10 @@ frameAt :: Double -> Animation -> Tree
|
|||
frameAt t (Animation d (Frame f)) = mkGroup $ execState (f d (min d t)) id []
|
||||
|
||||
renderTree :: Tree -> String
|
||||
renderTree = renderSizedTree Nothing Nothing
|
||||
renderTree t = maybe "" ppElement $ xmlOfTree t
|
||||
|
||||
renderSizedTree :: Maybe Number -> Maybe Number -> Tree -> String
|
||||
renderSizedTree w h t = ppElement $ xmlOfDocument doc
|
||||
renderSvg :: Maybe Number -> Maybe Number -> Tree -> String
|
||||
renderSvg w h t = ppElement $ xmlOfDocument doc
|
||||
where
|
||||
width = 320
|
||||
height = width / (16/9)
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ renderSvgs ani = do
|
|||
where
|
||||
frames = [0..frameCount-1]
|
||||
rate = 60
|
||||
nthFrame nth = renderTree $ frameAt (recip (fromIntegral rate) * fromIntegral nth) ani
|
||||
nthFrame nth = renderSvg Nothing Nothing $ frameAt (recip (fromIntegral rate) * fromIntegral nth) ani
|
||||
frameCount = round (duration ani * fromIntegral rate) :: Int
|
||||
nameTemplate :: String
|
||||
nameTemplate = "render-%05d.svg"
|
||||
|
|
@ -88,7 +88,7 @@ renderFormat format ani target = do
|
|||
-- XXX: Use threads
|
||||
generateFrames ani width_ rate action = withTempDir $ \tmp -> do
|
||||
let frameName nth = tmp </> printf nameTemplate nth
|
||||
rendered = [ renderSizedTree width height $ nthFrame n | n <- frames]
|
||||
rendered = [ renderSvg width height $ nthFrame n | n <- frames]
|
||||
`using` parBuffer 16 rdeepseq
|
||||
forM_ (zip [0::Int ..] rendered) $ \(n, frame) -> do
|
||||
writeFile (frameName n) frame
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ resolver: lts-11.22
|
|||
allow-newer: false
|
||||
|
||||
extra-deps:
|
||||
- reanimate-svg-0.8.1.0
|
||||
- reanimate-svg-0.8.2.0
|
||||
- diagrams-1.4@sha256:3e36369e84115b900fd9dcb570672a188339a470eb19ca62170775cd835cf8ca
|
||||
- diagrams-contrib-1.4.3@sha256:bcfa6c85f8c33b8c48c3a61b7216afdebd51cd793c50da3a2dd358827d25fc76
|
||||
- diagrams-core-1.4.1.1@sha256:6ef6b17785d77997c481eb085570e21b6a00cc91d086fbf49490504130ebc7d1
|
||||
|
|
|
|||
23
viewer/.gitignore
vendored
Executable file
23
viewer/.gitignore
vendored
Executable file
|
|
@ -0,0 +1,23 @@
|
|||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
# /build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
13
viewer/build/asset-manifest.json
Normal file
13
viewer/build/asset-manifest.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"main.css": "./static/css/main.6efe09fd.chunk.css",
|
||||
"main.js": "./static/js/main.db22f45d.chunk.js",
|
||||
"main.js.map": "./static/js/main.db22f45d.chunk.js.map",
|
||||
"runtime~main.js": "./static/js/runtime~main.9eb600ee.js",
|
||||
"runtime~main.js.map": "./static/js/runtime~main.9eb600ee.js.map",
|
||||
"static/js/2.772a56e7.chunk.js": "./static/js/2.772a56e7.chunk.js",
|
||||
"static/js/2.772a56e7.chunk.js.map": "./static/js/2.772a56e7.chunk.js.map",
|
||||
"index.html": "./index.html",
|
||||
"precache-manifest.15b8d497ab10704d87b84878b92d08cf.js": "./precache-manifest.15b8d497ab10704d87b84878b92d08cf.js",
|
||||
"service-worker.js": "./service-worker.js",
|
||||
"static/css/main.6efe09fd.chunk.css.map": "./static/css/main.6efe09fd.chunk.css.map"
|
||||
}
|
||||
BIN
viewer/build/favicon.ico
Executable file
BIN
viewer/build/favicon.ico
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
1
viewer/build/index.html
Normal file
1
viewer/build/index.html
Normal file
|
|
@ -0,0 +1 @@
|
|||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"/><meta name="theme-color" content="#000000"/><link rel="manifest" href="./manifest.json"/><title>Reanimate Playground</title><link href="./static/css/main.6efe09fd.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(l){function e(e){for(var r,t,n=e[0],o=e[1],u=e[2],f=0,i=[];f<n.length;f++)t=n[f],p[t]&&i.push(p[t][0]),p[t]=0;for(r in o)Object.prototype.hasOwnProperty.call(o,r)&&(l[r]=o[r]);for(s&&s(e);i.length;)i.shift()();return c.push.apply(c,u||[]),a()}function a(){for(var e,r=0;r<c.length;r++){for(var t=c[r],n=!0,o=1;o<t.length;o++){var u=t[o];0!==p[u]&&(n=!1)}n&&(c.splice(r--,1),e=f(f.s=t[0]))}return e}var t={},p={1:0},c=[];function f(e){if(t[e])return t[e].exports;var r=t[e]={i:e,l:!1,exports:{}};return l[e].call(r.exports,r,r.exports,f),r.l=!0,r.exports}f.m=l,f.c=t,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(r,e){if(1&e&&(r=f(r)),8&e)return r;if(4&e&&"object"==typeof r&&r&&r.__esModule)return r;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:r}),2&e&&"string"!=typeof r)for(var n in r)f.d(t,n,function(e){return r[e]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="./";var r=window.webpackJsonp=window.webpackJsonp||[],n=r.push.bind(r);r.push=e,r=r.slice();for(var o=0;o<r.length;o++)e(r[o]);var s=n;a()}([])</script><script src="./static/js/2.772a56e7.chunk.js"></script><script src="./static/js/main.db22f45d.chunk.js"></script></body></html>
|
||||
15
viewer/build/manifest.json
Executable file
15
viewer/build/manifest.json
Executable file
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
self.__precacheManifest = [
|
||||
{
|
||||
"revision": "9eb600ee07a27cdad64f",
|
||||
"url": "./static/js/runtime~main.9eb600ee.js"
|
||||
},
|
||||
{
|
||||
"revision": "db22f45d36ec0c1e28c1",
|
||||
"url": "./static/js/main.db22f45d.chunk.js"
|
||||
},
|
||||
{
|
||||
"revision": "772a56e764e5091f7538",
|
||||
"url": "./static/js/2.772a56e7.chunk.js"
|
||||
},
|
||||
{
|
||||
"revision": "db22f45d36ec0c1e28c1",
|
||||
"url": "./static/css/main.6efe09fd.chunk.css"
|
||||
},
|
||||
{
|
||||
"revision": "8912fe90ce5c625516525c7cd80ee665",
|
||||
"url": "./index.html"
|
||||
}
|
||||
];
|
||||
34
viewer/build/service-worker.js
Normal file
34
viewer/build/service-worker.js
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* Welcome to your Workbox-powered service worker!
|
||||
*
|
||||
* You'll need to register this file in your web app and you should
|
||||
* disable HTTP caching for this file too.
|
||||
* See https://goo.gl/nhQhGp
|
||||
*
|
||||
* The rest of the code is auto-generated. Please don't update this file
|
||||
* directly; instead, make changes to your Workbox build configuration
|
||||
* and re-run your build process.
|
||||
* See https://goo.gl/2aRDsh
|
||||
*/
|
||||
|
||||
importScripts("https://storage.googleapis.com/workbox-cdn/releases/3.6.3/workbox-sw.js");
|
||||
|
||||
importScripts(
|
||||
"./precache-manifest.15b8d497ab10704d87b84878b92d08cf.js"
|
||||
);
|
||||
|
||||
workbox.clientsClaim();
|
||||
|
||||
/**
|
||||
* The workboxSW.precacheAndRoute() method efficiently caches and responds to
|
||||
* requests for URLs in the manifest.
|
||||
* See https://goo.gl/S9QRab
|
||||
*/
|
||||
self.__precacheManifest = [].concat(self.__precacheManifest || []);
|
||||
workbox.precaching.suppressWarnings();
|
||||
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
|
||||
|
||||
workbox.routing.registerNavigationRoute("./index.html", {
|
||||
|
||||
blacklist: [/^\/_/,/\/[^\/]+\.[^\/]+$/],
|
||||
});
|
||||
2
viewer/build/static/css/main.6efe09fd.chunk.css
Normal file
2
viewer/build/static/css/main.6efe09fd.chunk.css
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
body{margin:0;padding:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}.App{height:100vh;background-color:#282c34;color:#fff;display:grid;grid-template-columns:1fr;grid-template-rows:auto;grid-auto-flow:column;-webkit-align-items:center;align-items:center;overflow:hidden}.controls{text-align:center}.controls svg{padding-left:5px;padding-right:5px}.viewer{position:static}.viewer svg{max-width:100vw;max-height:100vh;margin-top:auto;margin-bottom:auto}div.messages{position:fixed;top:0;width:100%;background:#282c34}div.messages pre{margin:0}.home{color:#fff;text-align:center;float:right;margin-right:2em}
|
||||
/*# sourceMappingURL=main.6efe09fd.chunk.css.map */
|
||||
1
viewer/build/static/css/main.6efe09fd.chunk.css.map
Normal file
1
viewer/build/static/css/main.6efe09fd.chunk.css.map
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["/home/lemmih/Coding/Haskell/reanimate/viewer/src/index.css","main.6efe09fd.chunk.css","/home/lemmih/Coding/Haskell/reanimate/viewer/src/App.css"],"names":[],"mappings":"AAAA,KACE,QAAA,CACA,SAAA,CACA,mICEY,CDCZ,kCAAA,CACA,iCCCF,CDEA,KACE,uECEF,CCbA,KAGE,YAAA,CACA,wBAAA,CACA,UAAA,CACA,YAAA,CACA,yBAAA,CACA,uBAAA,CAEA,qBAAA,CACA,0BAAA,CAAA,kBAAA,CACA,eDiBF,CCZA,UACE,iBDiBF,CCfA,cACE,gBAAA,CACA,iBDiBF,CCfA,QACE,eDiBF,CCfA,YACE,eAAA,CACA,gBAAA,CACA,eAAA,CACA,kBDiBF,CCfA,aACE,cAAA,CACA,KAAA,CACA,UAAA,CACA,kBDiBF,CCfA,iBACE,QDiBF,CCfA,MACE,UAAA,CACA,iBAAA,CACA,WAAA,CACA,gBDiBF","file":"main.6efe09fd.chunk.css","sourcesContent":["body {\n margin: 0;\n padding: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\",\n \"Ubuntu\", \"Cantarell\", \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\",\n sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\ncode {\n font-family: source-code-pro, Menlo, Monaco, Consolas, \"Courier New\",\n monospace;\n}\n","body {\n margin: 0;\n padding: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\",\n \"Ubuntu\", \"Cantarell\", \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\",\n sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\ncode {\n font-family: source-code-pro, Menlo, Monaco, Consolas, \"Courier New\",\n monospace;\n}\n\n.App {\n /* text-align: center; */\n /* min-height: 100vh; */\n height: 100vh;\n background-color: #282c34;\n color: white;\n display: grid;\n grid-template-columns: 1fr;\n grid-template-rows: auto;\n /* grid-gap: 1em; */\n grid-auto-flow: column;\n -webkit-align-items: center;\n align-items: center;\n overflow: hidden;\n}\n.editor {\n /* min-width: 35em; */\n}\n.controls {\n text-align:center;\n}\n.controls svg {\n padding-left: 5px;\n padding-right: 5px;\n}\n.viewer {\n position: static;\n}\n.viewer svg {\n max-width: 100vw;\n max-height: 100vh;\n margin-top: auto;\n margin-bottom: auto;\n}\ndiv.messages {\n position: fixed;\n top: 0;\n width: 100%;\n background: #282c34;\n}\ndiv.messages pre {\n margin: 0;\n}\n.home {\n color: white;\n text-align: center;\n float:right;\n margin-right: 2em;\n}\n\n\n#editor {\n\n}\n\n",".App {\n /* text-align: center; */\n /* min-height: 100vh; */\n height: 100vh;\n background-color: #282c34;\n color: white;\n display: grid;\n grid-template-columns: 1fr;\n grid-template-rows: auto;\n /* grid-gap: 1em; */\n grid-auto-flow: column;\n align-items: center;\n overflow: hidden;\n}\n.editor {\n /* min-width: 35em; */\n}\n.controls {\n text-align:center;\n}\n.controls svg {\n padding-left: 5px;\n padding-right: 5px;\n}\n.viewer {\n position: static;\n}\n.viewer svg {\n max-width: 100vw;\n max-height: 100vh;\n margin-top: auto;\n margin-bottom: auto;\n}\ndiv.messages {\n position: fixed;\n top: 0;\n width: 100%;\n background: #282c34;\n}\ndiv.messages pre {\n margin: 0;\n}\n.home {\n color: white;\n text-align: center;\n float:right;\n margin-right: 2em;\n}\n\n\n#editor {\n\n}\n"]}
|
||||
2
viewer/build/static/js/2.772a56e7.chunk.js
Normal file
2
viewer/build/static/js/2.772a56e7.chunk.js
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1 @@
|
|||
4078f6ee1053e716763e575de7ea78c199ad4bbd
|
||||
2
viewer/build/static/js/main.db22f45d.chunk.js
Normal file
2
viewer/build/static/js/main.db22f45d.chunk.js
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
(window.webpackJsonp=window.webpackJsonp||[]).push([[0],{10:function(e,n,t){e.exports=t(18)},16:function(e,n,t){},17:function(e,n,t){},18:function(e,n,t){"use strict";t.r(n);var s=t(0),a=t.n(s),o=t(4),r=t.n(o),i=(t(16),t(2)),c=t(5),l=t(6),m=t(9),g=t(7),u=t(1),d=t(8),v=(t(17),function(e){function n(e){var t;Object(c.a)(this,n),(t=Object(m.a)(this,Object(g.a)(n).call(this,e))).connect=function(){var e=new WebSocket("ws://localhost:9161");e.onopen=function(n){t.setState(function(e){return Object(i.a)({},e,{message:"Connected."})}),e.send("60")},e.onclose=function(e){t.setState(function(e){return Object(i.a)({},e,{message:"Disconnected."})}),setTimeout(t.connect,1e3)},e.onmessage=function(e){if("Success!"===e.data)console.log("Success");else if("Compiling"===e.data)t.setState({message:"Compiling..."});else if("Rendering"===e.data)t.setState({message:"Rendering..."}),t.nFrames_new=0,t.svgs_new=[];else if("Done"===e.data)t.setState({message:""}),console.log("Done"),t.nFrames=t.nFrames_new,t.svgs=t.svgs_new,t.nFrames_new=0,t.svgs_new=[],t.start=Date.now();else if(e.data.startsWith("Error"))console.log("Error"),t.setState({message:e.data.substring(5)});else{t.setState({message:"Rendering: ".concat(t.nFrames_new)}),t.nFrames_new++;var n=document.createElement("div");n.innerHTML=e.data,t.svgs_new.push(n)}},t.setState(function(n){return Object(i.a)({},n,{socket:e,message:"Connecting..."})})},t.onLoad=function(e){setTimeout(function(){e.resize()},0)},t.state={},setTimeout(t.connect,0),t.nFrames_new=0,t.svgs_new=[],t.nFrames=0,t.svgs=[],t.start=Date.now();var s=Object(u.a)(t);return requestAnimationFrame(function e(){var n=Date.now(),a=s.nFrames,o=Math.round((n-t.start)/1e3*60)%a;if(s.svgs_new.length){for(;s.svg.firstChild;)s.svg.removeChild(s.svg.firstChild);s.svg.appendChild(s.svgs_new[s.svgs_new.length-1])}else if(a){for(;s.svg.firstChild;)s.svg.removeChild(s.svg.firstChild);s.svg.appendChild(s.svgs[o])}else s.svg.innerText="";requestAnimationFrame(e)}),t}return Object(d.a)(n,e),Object(l.a)(n,[{key:"render",value:function(){var e=this,n=this.state.message;return a.a.createElement("div",{className:"App"},a.a.createElement("div",{className:"viewer"},a.a.createElement("div",{ref:function(n){return e.svg=n}}),a.a.createElement("div",{className:"messages"},a.a.createElement("pre",null,n))))}}]),n}(s.Component));Boolean("localhost"===window.location.hostname||"[::1]"===window.location.hostname||window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/));r.a.render(a.a.createElement(v,null),document.getElementById("root")),"serviceWorker"in navigator&&navigator.serviceWorker.ready.then(function(e){e.unregister()})}},[[10,1,2]]]);
|
||||
//# sourceMappingURL=main.db22f45d.chunk.js.map
|
||||
1
viewer/build/static/js/main.db22f45d.chunk.js.map
Normal file
1
viewer/build/static/js/main.db22f45d.chunk.js.map
Normal file
File diff suppressed because one or more lines are too long
2
viewer/build/static/js/runtime~main.9eb600ee.js
Normal file
2
viewer/build/static/js/runtime~main.9eb600ee.js
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
!function(e){function r(r){for(var n,f,i=r[0],l=r[1],a=r[2],c=0,s=[];c<i.length;c++)f=i[c],o[f]&&s.push(o[f][0]),o[f]=0;for(n in l)Object.prototype.hasOwnProperty.call(l,n)&&(e[n]=l[n]);for(p&&p(r);s.length;)s.shift()();return u.push.apply(u,a||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++){var l=t[i];0!==o[l]&&(n=!1)}n&&(u.splice(r--,1),e=f(f.s=t[0]))}return e}var n={},o={1:0},u=[];function f(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,f),t.l=!0,t.exports}f.m=e,f.c=n,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(e,r){if(1&r&&(e=f(e)),8&r)return e;if(4&r&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)f.d(t,n,function(r){return e[r]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="./";var i=window.webpackJsonp=window.webpackJsonp||[],l=i.push.bind(i);i.push=r,i=i.slice();for(var a=0;a<i.length;a++)r(i[a]);var p=l;t()}([]);
|
||||
//# sourceMappingURL=runtime~main.9eb600ee.js.map
|
||||
1
viewer/build/static/js/runtime~main.9eb600ee.js.map
Normal file
1
viewer/build/static/js/runtime~main.9eb600ee.js.map
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
|||
c3337c953a23ef5eb5f30ed154dd591fe5a8645e
|
||||
6c7a4342c0b1d2c4a2a8b83b5ecc53ee6d1b6622
|
||||
|
|
@ -2,10 +2,9 @@
|
|||
"name": "viewer",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"homepage": "https://lemmih.github.io/reanimate",
|
||||
"homepage": ".",
|
||||
"dependencies": {
|
||||
"react": "^16.8.2",
|
||||
"react-ace": "^6.4.0",
|
||||
"react-dom": "^16.8.2",
|
||||
"react-scripts": "2.1.5"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,11 +1,5 @@
|
|||
import React, {Component} from 'react';
|
||||
import './App.css';
|
||||
import AceEditor from 'react-ace';
|
||||
import 'brace/mode/haskell';
|
||||
import 'brace/theme/github';
|
||||
import 'brace/theme/monokai';
|
||||
|
||||
import preset from './Presets';
|
||||
|
||||
class App extends Component {
|
||||
connect = () => {
|
||||
|
|
@ -64,7 +58,6 @@ class App extends Component {
|
|||
super(props);
|
||||
|
||||
this.state = {
|
||||
program: preset[0].programs[0].code
|
||||
};
|
||||
setTimeout(this.connect, 0);
|
||||
this.nFrames_new = 0;
|
||||
|
|
@ -101,27 +94,8 @@ class App extends Component {
|
|||
ace.resize();
|
||||
}, 0);
|
||||
}
|
||||
onChange = text => {
|
||||
this.setState({program: text});
|
||||
if (this.timeout)
|
||||
clearTimeout(this.timeout);
|
||||
const socket = this.state.socket
|
||||
const self = this;
|
||||
|
||||
this.timeout = setTimeout(function() {
|
||||
console.log('change', text);
|
||||
self.setState(state => ({
|
||||
...state,
|
||||
message: "Compiling..."
|
||||
}));
|
||||
socket.send(text);
|
||||
}, 500);
|
||||
};
|
||||
selectPreset = evt => {
|
||||
this.onChange(evt.target.value);
|
||||
};
|
||||
render() {
|
||||
const {message, program} = this.state;
|
||||
const {message} = this.state;
|
||||
return (
|
||||
<div className="App">
|
||||
<div className="viewer">
|
||||
|
|
|
|||
|
|
@ -1,217 +0,0 @@
|
|||
const latex_draw =
|
||||
`animation :: Animation
|
||||
animation =
|
||||
bg \`sim\` (autoReverse $ drawText \`andThen\` fillText)
|
||||
where
|
||||
bg = mkAnimation 0 $ emit (mkBackground "black")
|
||||
msg = "\\\\sum_{k=1}^\\\\infty {1 \\\\over k^2} = {\\\\pi^2 \\\\over 6}"
|
||||
glyphs = center $ latexAlign msg
|
||||
fillText = mkAnimation 1 $ do
|
||||
s <- signal 0 1
|
||||
emit $ scale 5 $ withFillColor "white" $ withFillOpacity s glyphs
|
||||
drawText = mkAnimation 2 $ do
|
||||
s <- signal 0 1
|
||||
emit $ scale 5 $
|
||||
withStrokeColor "white" $ withFillOpacity 0 $ withStrokeWidth (Num 0.1) $
|
||||
partialSvg s glyphs`;
|
||||
|
||||
const bbox =
|
||||
`animation :: Animation
|
||||
animation = bg \`sim\`
|
||||
mapA (translate (-50) 0) bbox1 \`sim\`
|
||||
mapA (translate 50 0) bbox2
|
||||
where
|
||||
bg = mkAnimation 0 $ emit $ mkBackground "black"
|
||||
|
||||
bbox1 :: Animation
|
||||
bbox1 = mkAnimation 5 $ do
|
||||
s <- signal 0 1
|
||||
emit $ mkGroup
|
||||
[ mkBoundingBox $ rotate (360*s) svg
|
||||
, withFillColor "white" $ rotate (360*s) svg ]
|
||||
where
|
||||
svg = scale 3 $ center $ latexAlign "\\\\sum_{k=1}^\\\\infty"
|
||||
|
||||
bbox2 :: Animation
|
||||
bbox2 = autoReverse $ mkAnimation 2.5 $ do
|
||||
s <- signal 0 1
|
||||
emit $ mkGroup
|
||||
[ mkBoundingBox $ partialSvg s heartShape
|
||||
, withStrokeColor "white" $ withFillOpacity 0 $ partialSvg s heartShape ]
|
||||
|
||||
mkBoundingBox :: Tree -> Tree
|
||||
mkBoundingBox svg = withStrokeColor "red" $ withFillOpacity 0 $
|
||||
mkRect (S.Num x, S.Num y) (S.Num w) (S.Num h)
|
||||
where
|
||||
(x, y, w, h) = boundingBox svg
|
||||
|
||||
heartShape =
|
||||
center $ rotateAroundCenter 225 $ mkPathString
|
||||
"M0.0,40.0 v-40.0 h40.0a20.0 20.0 90.0 0 1 0.0,40.0a20.0 20.0 90.0 0 1 -40.0,0.0 Z"`;
|
||||
|
||||
const sinewave =
|
||||
`animation :: Ani ()
|
||||
animation = proc () -> do
|
||||
duration 10 -< ()
|
||||
emit -< toHtml $ mkBackground "black"
|
||||
idx <- signalOscillate 0 1 -< ()
|
||||
emit -< do
|
||||
defs_ $ clipPath_ [id_ "clip"] $ toHtml $
|
||||
mkRect (Num 0, Num (-height)) (Num $ idx*width) (Num 320)
|
||||
toHtml $ translate margin height $ withStrokeColor "white" $
|
||||
withClipPathRef (Ref "clip") $ mkPathText $ renderPathText $ approxFnData 100 wave
|
||||
toHtml $ withStrokeColor "white" $
|
||||
mkLine (Num margin, Num 10) (Num margin, Num 170)
|
||||
toHtml $ withStrokeColor "white" $
|
||||
mkLine (Num margin, Num height) (Num (margin+width), Num height)
|
||||
let (circX, circY) = wave idx
|
||||
emit -< g_ [transform_ $ Lucid.translate margin height] $
|
||||
circle_ [num_ cx_ circX, num_ cy_ circY, r_ "3", fill_ "red"]
|
||||
where
|
||||
freq = 3; margin = 30; width = 260; height = 90
|
||||
wave idx = (idx*width, sin (idx*pi*2*freq) * 50)`;
|
||||
|
||||
const morph_wave =
|
||||
`animation :: Animation
|
||||
animation = autoReverse $ mkAnimation 2.5 $ do
|
||||
morph <- signal 0 1
|
||||
emit $ mkBackground "black"
|
||||
emit $ withStrokeColor "white" $ translate (-320/2) (-180/2) $ mkGroup
|
||||
[ translate 30 50 $ mkLinePath wave1
|
||||
, translate 30 130 $ mkLinePath wave2
|
||||
, translate 30 90 $ mkLinePath $ morphPath wave1 wave2 morph
|
||||
, mkLine (Num 30, Num 10) (Num 30, Num 170)
|
||||
, mkLine (Num 30, Num 90) (Num 290, Num 90) ]
|
||||
where
|
||||
freq = 3; width = 260
|
||||
wave1 = approxFnData 100 $ \\idx -> (idx*width, sin (idx*pi*2*freq) * 20)
|
||||
wave2 = approxFnData 100 $ \\idx -> (idx*width, sin (idx*pi*2*(freq*3)) * 20)`;
|
||||
|
||||
const morph_wave_circle =
|
||||
`animation :: Animation
|
||||
animation = autoReverse $ mkAnimation 2.5 $ do
|
||||
idx <- signal 0 1
|
||||
emit $ mkBackground "black"
|
||||
emit $ withStrokeColor "white" $ translate (-320/2) (-180/2) $ mkGroup
|
||||
[ translate 30 90 $ mkLinePath $ morphPath circle wave1 idx
|
||||
, mkLine (Num 30, Num 10) (Num 30, Num 170)
|
||||
, mkLine (Num 30, Num 90) (Num 290, Num 90) ]
|
||||
where
|
||||
freq = 5; width = 260; radius = 50
|
||||
wave1 = approxFnData 100 $ \\idx -> (idx*width, sin (idx*pi*2*freq) * 20)
|
||||
circle = approxFnData 100 $ \\idx ->
|
||||
(cos (idx*pi*2+pi/2)*radius + width/2, sin (idx*pi*2+pi/2)*radius)`;
|
||||
|
||||
const progressMeters =
|
||||
`animation :: Animation
|
||||
animation =
|
||||
bg \`sim\` labels \`sim\`
|
||||
mapA (translate (-100) 0) (adjustSpeed 1.0 progressMeter) \`simLoop\`
|
||||
mapA (translate 0 0) (adjustSpeed 2.0 progressMeter) \`simLoop\`
|
||||
mapA (translate 100 0) (adjustSpeed 0.5 progressMeter)
|
||||
where
|
||||
bg = mkAnimation 0 $ emit $ mkBackground "black"
|
||||
labels = mkAnimation 0 $ emit $ translate 0 70 $ withFillColor "white" $ mkGroup
|
||||
[ translate (-100) 0 $ scale 2 $ center $ latex "1x"
|
||||
, translate 0 0 $ scale 2 $ center $ latex "2x"
|
||||
, translate 100 0 $ scale 2 $ center $ latex "0.5x"
|
||||
]
|
||||
|
||||
progressMeter :: Animation
|
||||
progressMeter = mkAnimation 3 $ do
|
||||
h <- signal 0 100
|
||||
emit $ center $ mkGroup
|
||||
[ withStrokeColor "white" $ withStrokeWidth (Num 2) $ withFillOpacity 0 $
|
||||
mkRect (Num 0, Num 0) (Num 30) (Num 100)
|
||||
, withFillColor "white" $
|
||||
mkRect (Num 0, Num 0) (Num 30) (Num h) ]`
|
||||
|
||||
const latex_basic =
|
||||
`animation :: Animation
|
||||
animation = autoReverse $ mkAnimation 2 $ do
|
||||
s <- signal 0 1
|
||||
emit $ mkGroup
|
||||
[ mkBackground "black"
|
||||
, withStrokeColor "white" $ withFillOpacity 0 $ withStrokeWidth (Num 0.1) text
|
||||
, withFillColor "white" $ withFillOpacity s text ]
|
||||
where
|
||||
text = scale 4 $ center $ latexAlign
|
||||
"\\\\sum_{k=1}^\\\\infty {1 \\\\over k^2} = {\\\\pi^2 \\\\over 6}"`
|
||||
|
||||
const latex_color =
|
||||
`animation :: Animation
|
||||
animation = mkAnimation 1 $ do
|
||||
emit $ mkBackground "black"
|
||||
emit $ withStrokeWidth (Num 0.2) $
|
||||
withStrokeColor "white" $
|
||||
withSubglyphs [0] (withFillColor "blue") $
|
||||
withSubglyphs [1] (withFillColor "yellow") $
|
||||
withSubglyphs [2] (withFillColor "green") $
|
||||
withSubglyphs [3] (withFillColor "red") $
|
||||
withSubglyphs [4] (withFillColor "darkslategrey") $
|
||||
svg
|
||||
where
|
||||
svg = scale 10 $ center $ latex "\\\\LaTeX"`;
|
||||
|
||||
const valentine =
|
||||
`animation :: Animation
|
||||
animation =
|
||||
all_red \`before\`
|
||||
( background \`sim\`
|
||||
(backgroundDelay \`before\`
|
||||
foldr1 sim [ pause p \`before\` fallingLove p x | (p, x) <- falling ]
|
||||
) \`sim\`
|
||||
(heart_ani \`before\` heart_disappear) \`sim\`
|
||||
(pause 1 \`before\` repeatAnimation 10 (message ai))
|
||||
)
|
||||
where
|
||||
falling = [(6.4, 0.09), (4.9, 0.12), (4.5, 0.88), (0.3, 0.43), (5.3, 0.93)
|
||||
,(0.1, 0.80), (1.1, 0.39), (2.3, 0.21), (2.9, 0.77), (3.4, 0.46)
|
||||
,(6.2, 0.88), (5.9, 0.80), (3.2, 0.14), (7.7, 0.99), (3.4, 0.35)
|
||||
,(0.4, 0.51), (7.1, 0.60), (7.7, 0.65)]
|
||||
ai = center $ xelatex "爱"
|
||||
all_red = mkAnimation 1 $ emit $ mkBackground "red"
|
||||
background = mkAnimation 2 $ do
|
||||
n <- round <$> signal 0 0xFF
|
||||
emit $ mkBackgroundPixel $ PixelRGBA8 0xFF n n 0xFF
|
||||
backgroundDelay = pause (duration background-1)
|
||||
heart_ani = repeatAnimation 10 $ mkAnimation 1 $ do
|
||||
n <- oscillate $ signalSCurve 2 0.9 1.1
|
||||
mapF (scale n) $ drawHeart
|
||||
heart_disappear = mkAnimation 3 $ do
|
||||
n <- signal 0.9 10
|
||||
o <- oscillate $ signal 0 1.5
|
||||
mapF (scale n) drawHeart
|
||||
mapF (scale (n*4)) $ emit $ withFillOpacity o $ withFillColor "white" ai
|
||||
fallingLove rand xPos = mkAnimation 2 $ do
|
||||
n <- signal (-100) 90
|
||||
o <- signal 0 5
|
||||
emit $ withStrokeColor "black" $ withFillColor "red" $
|
||||
translate ((xPos*2-1)*(320/2)) n $ scale 0.3 $ rotate (60*(o+rand+xPos)) heartShape
|
||||
message txt = mkAnimation 1 $ do
|
||||
o <- oscillate $ signal 0 1
|
||||
n <- oscillate $ signalSCurve 2 0.9 1.1
|
||||
emit $ scale n $ scale 2 $ withFillColor "white" $ withFillOpacity o txt
|
||||
|
||||
drawHeart = emit $ withFillColor "red" $ heartShape
|
||||
|
||||
heartShape =
|
||||
center $ rotateAroundCenter 225 $ mkPathString
|
||||
"M0.0,40.0 v-40.0 h40.0a20.0 20.0 90.0 0 1 0.0,40.0a20.0 20.0 90.0 0 1 -40.0,0.0 Z"`;
|
||||
|
||||
export default [
|
||||
{ name: "Examples"
|
||||
, programs:
|
||||
[ {name: "LaTeX Draw", code: latex_draw }
|
||||
, {name: "LaTeX Color", code: latex_color }
|
||||
, {name: "LaTeX Basic", code: latex_basic }
|
||||
, {name: "Bounding boxes", code: bbox }
|
||||
// , {name: "Sinewave", code: sinewave }
|
||||
, {name: "Morphwave", code: morph_wave }
|
||||
, {name: "Morphwave Circle", code: morph_wave_circle }
|
||||
, {name: "Progress meters", code: progressMeters }
|
||||
, {name: "Valentine", code: valentine }
|
||||
// , {name: "Highlight", code: highlight }
|
||||
]
|
||||
},
|
||||
];
|
||||
Loading…
Reference in a new issue