diff --git a/reanimate.cabal b/reanimate.cabal index 1f0afd7..f09c4c2 100644 --- a/reanimate.cabal +++ b/reanimate.cabal @@ -24,9 +24,9 @@ description: 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/main.2d848b69.chunk.js viewer/build/static/js/runtime~main.9eb600ee.js - viewer/build/static/css/main.6efe09fd.chunk.css + viewer/build/static/css/main.f7ad3e9b.chunk.css data/CIExyz.csv data/cone_sensitivity_lms.csv diff --git a/src/Reanimate/Driver.hs b/src/Reanimate/Driver.hs index d14c53e..01f118b 100644 --- a/src/Reanimate/Driver.hs +++ b/src/Reanimate/Driver.hs @@ -1,15 +1,17 @@ module Reanimate.Driver ( reanimate ) where import Control.Concurrent (MVar, forkIO, killThread, modifyMVar_, - newEmptyMVar, putMVar) + newEmptyMVar, putMVar, takeMVar) import Control.Exception (finally) import Control.Monad.Fix (fix) import qualified Data.Text as T +import qualified Data.Text.Read as T import Network.WebSockets import System.Directory (findFile, listDirectory) import System.Environment (getArgs, getProgName) import System.FilePath import System.FSNotify +import System.Exit import System.IO (BufferMode (..), hPutStrLn, hSetBuffering, stderr, stdin) @@ -18,7 +20,7 @@ import Paths_reanimate import Reanimate.Misc (runCmdLazy, runCmd_, withTempDir, withTempFile) import Reanimate.Monad (Animation) -import Reanimate.Render (renderSvgs, render) +import Reanimate.Render (render, renderSvgs) import Web.Browser (openBrowser) opts = defaultConnectionOptions @@ -56,26 +58,11 @@ reanimate animation = do sendTextData conn (T.pack "Compiling") putStrLn "Killing and respawning..." 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) + tid <- forkIO $ slaveHandler conn self tmpDir return tid + killSlave = do + tid <- takeMVar slave + killThread tid putStrLn "Found self. Listening..." stop <- watchFile watch self handler putMVar slave =<< forkIO (return ()) @@ -83,7 +70,38 @@ reanimate animation = do fps <- receiveData conn :: IO T.Text handler loop - loop `finally` stop + loop `finally` (killSlave >> stop) + +slaveHandler conn self tmpDir = 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", "-M1G", "-RTS"] + (frameCount,_) <- expectFrame =<< getFrame + -- sendTextData conn (T.pack "Compiled") + sendTextData conn (T.pack $ show frameCount) + fix $ \loop -> do + (frameIdx, frame) <- expectFrame =<< getFrame + sendTextData conn (T.pack $ show frameIdx) + sendTextData conn frame + loop + where + expectFrame (Left "") = do + sendTextData conn (T.pack "Done") + exitWith ExitSuccess + expectFrame (Left err) = do + sendTextData conn $ T.pack $ "Error" ++ err + exitWith (ExitFailure 1) + expectFrame (Right frame) = + case T.decimal frame of + Left err -> do + hPutStrLn stderr (T.unpack frame) + hPutStrLn stderr $ "expectFrame: " ++ err + sendTextData conn $ T.pack $ "Error" ++ err + exitWith (ExitFailure 1) + Right (frameNumber, rest) -> pure (frameNumber, rest) watchFile watch file action = watchDir watch (takeDirectory file) check (const action) where diff --git a/src/Reanimate/Render.hs b/src/Reanimate/Render.hs index ab48344..b11be26 100644 --- a/src/Reanimate/Render.hs +++ b/src/Reanimate/Render.hs @@ -23,16 +23,38 @@ import Text.Printf (printf) renderSvgs :: Animation -> IO () renderSvgs ani = do - let renderedFrames = map (T.concat . T.lines . T.pack . nthFrame) frames - mapM_ T.putStrLn (renderedFrames `using` parBuffer 16 rdeepseq) + print frameCount + lock <- newMVar () + -- let renderedFrames = map (T.concat . T.lines . T.pack . nthFrame) frames + -- mapM_ T.putStrLn (renderedFrames `using` parBuffer 16 rdeepseq) + + concurrentForM_ (frameOrder rate frameCount) $ \nth -> do + let -- frame = frameAt (recip (fromIntegral rate-1) * fromIntegral nth) ani + now = (duration ani / (fromIntegral frameCount-1)) * fromIntegral nth + frame = frameAt (if frameCount<=1 then 0 else now) ani + svg = renderSvg Nothing Nothing frame + evaluate (length svg) + withMVar lock $ \_ -> do + putStr (show nth) + T.putStrLn $ T.concat . T.lines . T.pack $ svg + hFlush stdout where frames = [0..frameCount-1] rate = 60 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" +frameOrder :: Int -> Int -> [Int] +frameOrder fps nFrames = worker [] fps + where + worker seen 0 = [] + worker seen nthFrame = + filterFrameList seen nthFrame nFrames ++ + worker (nthFrame : seen) (nthFrame `div` 2) +filterFrameList seen nthFrame nFrames = + filter (not.isSeen) $ [0, nthFrame .. nFrames-1] + where + isSeen x = any (\y -> x `mod` y == 0) seen data Format = RenderMp4 | RenderGif | RenderWebm | RenderBlank diff --git a/viewer/build/asset-manifest.json b/viewer/build/asset-manifest.json index 720fb8b..011e920 100644 --- a/viewer/build/asset-manifest.json +++ b/viewer/build/asset-manifest.json @@ -1,13 +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", + "main.css": "./static/css/main.f7ad3e9b.chunk.css", + "main.js": "./static/js/main.2d848b69.chunk.js", + "main.js.map": "./static/js/main.2d848b69.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", + "precache-manifest.ecac8766a6ef020d2fc520bbf4682e03.js": "./precache-manifest.ecac8766a6ef020d2fc520bbf4682e03.js", "service-worker.js": "./service-worker.js", - "static/css/main.6efe09fd.chunk.css.map": "./static/css/main.6efe09fd.chunk.css.map" + "static/css/main.f7ad3e9b.chunk.css.map": "./static/css/main.f7ad3e9b.chunk.css.map" } \ No newline at end of file diff --git a/viewer/build/index.html b/viewer/build/index.html index 602bc31..788d452 100644 --- a/viewer/build/index.html +++ b/viewer/build/index.html @@ -1 +1 @@ -Reanimate Playground
\ No newline at end of file +Reanimate Playground
\ No newline at end of file diff --git a/viewer/build/precache-manifest.15b8d497ab10704d87b84878b92d08cf.js b/viewer/build/precache-manifest.ecac8766a6ef020d2fc520bbf4682e03.js similarity index 54% rename from viewer/build/precache-manifest.15b8d497ab10704d87b84878b92d08cf.js rename to viewer/build/precache-manifest.ecac8766a6ef020d2fc520bbf4682e03.js index 598c317..bab2ff5 100644 --- a/viewer/build/precache-manifest.15b8d497ab10704d87b84878b92d08cf.js +++ b/viewer/build/precache-manifest.ecac8766a6ef020d2fc520bbf4682e03.js @@ -4,19 +4,19 @@ self.__precacheManifest = [ "url": "./static/js/runtime~main.9eb600ee.js" }, { - "revision": "db22f45d36ec0c1e28c1", - "url": "./static/js/main.db22f45d.chunk.js" + "revision": "2d848b69f66deb18ce2a", + "url": "./static/js/main.2d848b69.chunk.js" }, { "revision": "772a56e764e5091f7538", "url": "./static/js/2.772a56e7.chunk.js" }, { - "revision": "db22f45d36ec0c1e28c1", - "url": "./static/css/main.6efe09fd.chunk.css" + "revision": "2d848b69f66deb18ce2a", + "url": "./static/css/main.f7ad3e9b.chunk.css" }, { - "revision": "8912fe90ce5c625516525c7cd80ee665", + "revision": "c119ec4d7ef35b6c6c46fd0e9dcca5ce", "url": "./index.html" } ]; \ No newline at end of file diff --git a/viewer/build/service-worker.js b/viewer/build/service-worker.js index 50254d2..a3227ba 100644 --- a/viewer/build/service-worker.js +++ b/viewer/build/service-worker.js @@ -14,7 +14,7 @@ importScripts("https://storage.googleapis.com/workbox-cdn/releases/3.6.3/workbox-sw.js"); importScripts( - "./precache-manifest.15b8d497ab10704d87b84878b92d08cf.js" + "./precache-manifest.ecac8766a6ef020d2fc520bbf4682e03.js" ); workbox.clientsClaim(); diff --git a/viewer/build/static/css/main.6efe09fd.chunk.css b/viewer/build/static/css/main.f7ad3e9b.chunk.css similarity index 94% rename from viewer/build/static/css/main.6efe09fd.chunk.css rename to viewer/build/static/css/main.f7ad3e9b.chunk.css index f72534c..389fab4 100644 --- a/viewer/build/static/css/main.6efe09fd.chunk.css +++ b/viewer/build/static/css/main.f7ad3e9b.chunk.css @@ -1,2 +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 */ \ No newline at end of file +/*# sourceMappingURL=main.f7ad3e9b.chunk.css.map */ \ No newline at end of file diff --git a/viewer/build/static/css/main.6efe09fd.chunk.css.map b/viewer/build/static/css/main.f7ad3e9b.chunk.css.map similarity index 68% rename from viewer/build/static/css/main.6efe09fd.chunk.css.map rename to viewer/build/static/css/main.f7ad3e9b.chunk.css.map index cb65b85..5954ec9 100644 --- a/viewer/build/static/css/main.6efe09fd.chunk.css.map +++ b/viewer/build/static/css/main.f7ad3e9b.chunk.css.map @@ -1 +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"]} \ No newline at end of file +{"version":3,"sources":["/home/lemmih/Coding/Haskell/reanimate/viewer/src/index.css","main.f7ad3e9b.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.f7ad3e9b.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#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#editor {\n\n}\n"]} \ No newline at end of file diff --git a/viewer/build/static/js/main.2d848b69.chunk.js b/viewer/build/static/js/main.2d848b69.chunk.js new file mode 100644 index 0000000..1b6a3d0 --- /dev/null +++ b/viewer/build/static/js/main.2d848b69.chunk.js @@ -0,0 +1,2 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[0],{10:function(e,t,n){e.exports=n(18)},16:function(e,t,n){},17:function(e,t,n){},18:function(e,t,n){"use strict";n.r(t);var a=n(0),s=n.n(a),o=n(4),r=n.n(o),c=(n(16),n(2)),i=n(5),u=n(6),m=n(9),l=n(7),f=n(1),g=n(8),d=(n(17),function(e){function t(e){var n;Object(i.a)(this,t),(n=Object(m.a)(this,Object(l.a)(t).call(this,e))).connect=function(){var e=new WebSocket("ws://localhost:9161");e.onopen=function(t){n.setState(function(e){return Object(c.a)({},e,{message:"Connected."})}),e.send("60")},e.onclose=function(e){n.setState(function(e){return Object(c.a)({},e,{message:"Disconnected."})}),setTimeout(n.connect,1e3)},e.onmessage=function(e){if("Success!"===e.data)console.log("Success");else if("Compiling"===e.data)n.setState({message:"Compiling..."}),n.status="compiling",n.svgs=[],n.frame_count=0,n.next_frame=0;else if("Done"===e.data)n.setState({message:""}),console.log("Done"),n.start=Date.now();else if(e.data.startsWith("Error"))console.log("Error",e.data.substring(5)),n.setState({message:e.data.substring(5)});else{var t=parseInt(e.data);if(isNaN(t)){var a=document.createElement("div");a.innerHTML=e.data,n.svgs[n.next_frame]=a;var s=0;n.svgs.forEach(function(e){return s++}),console.log("Received",n.next_frame,n.frame_count,s)}else"compiling"===n.status?(n.setState({message:"Rendering..."}),n.frame_count=t,n.status="rendering",n.start=Date.now(),n.svgs=[],n.svgs[n.frame_count-1]=void 0):"rendering"===n.status?n.next_frame=t:console.log("Bad state change: received number")}},n.setState(function(t){return Object(c.a)({},t,{socket:e,message:"Connecting..."})})},n.onLoad=function(e){setTimeout(function(){e.resize()},0)},n.state={},setTimeout(n.connect,0),n.svgs=[],n.start=Date.now(),n.status="",n.frame_count=0,n.next_frame=0;var a=Object(f.a)(n);return requestAnimationFrame(function e(){var t=Date.now(),s=a.frame_count,o=a.frame_count/60,r=Math.round((t-n.start)/1e3*60)%s,c=0;if(n.svgs.forEach(function(e){return c++}),s){if(a.svgs[r]){for(n.setState({message:r+"/"+a.frame_count+" "+Math.round(c/o)+" fps"});a.svg.firstChild;)a.svg.removeChild(a.svg.firstChild);a.svg.appendChild(a.svgs[r])}}else a.svg.innerText="";requestAnimationFrame(e)}),n}return Object(g.a)(t,e),Object(u.a)(t,[{key:"render",value:function(){var e=this,t=this.state.message;return s.a.createElement("div",{className:"App"},s.a.createElement("div",{className:"viewer"},s.a.createElement("div",{ref:function(t){return e.svg=t}}),s.a.createElement("div",{className:"messages"},s.a.createElement("pre",null,t))))}}]),t}(a.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(s.a.createElement(d,null),document.getElementById("root")),"serviceWorker"in navigator&&navigator.serviceWorker.ready.then(function(e){e.unregister()})}},[[10,1,2]]]); +//# sourceMappingURL=main.2d848b69.chunk.js.map \ No newline at end of file diff --git a/viewer/build/static/js/main.2d848b69.chunk.js.map b/viewer/build/static/js/main.2d848b69.chunk.js.map new file mode 100644 index 0000000..c97ae31 --- /dev/null +++ b/viewer/build/static/js/main.2d848b69.chunk.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["App.jsx","serviceWorker.js","index.js"],"names":["App","props","_this","Object","classCallCheck","this","possibleConstructorReturn","getPrototypeOf","call","connect","ws","WebSocket","onopen","event","setState","state","objectSpread","message","send","onclose","setTimeout","onmessage","data","console","log","status","svgs","frame_count","next_frame","start","Date","now","startsWith","substring","num","parseInt","isNaN","div","document","createElement","innerHTML","count","forEach","_","undefined","socket","onLoad","ace","resize","self","assertThisInitialized","requestAnimationFrame","animate","nFrames","aniDuration","thisFrame","Math","round","svg","firstChild","removeChild","appendChild","innerText","_this2","react_default","a","className","ref","node","Component","Boolean","window","location","hostname","match","ReactDOM","render","src_App_0","getElementById","navigator","serviceWorker","ready","then","registration","unregister"],"mappings":"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","file":"static/js/main.2d848b69.chunk.js","sourcesContent":["import React, {Component} from 'react';\nimport './App.css';\n\nclass App extends Component {\n connect = () => {\n // const ws = new WebSocket(\"wss://reanimate.clozecards.com:9160\");\n const ws = new WebSocket(\"ws://localhost:9161\");\n\n ws.onopen = event => {\n this.setState(state => ({\n ...state,\n message: \"Connected.\"\n }));\n ws.send('60');\n }\n ws.onclose = event => {\n this.setState(state => ({\n ...state,\n message: \"Disconnected.\"\n }));\n setTimeout(this.connect, 1000);\n }\n ws.onmessage = event => {\n if (event.data === \"Success!\") {\n console.log(\"Success\");\n } else if (event.data === \"Compiling\") {\n this.setState({message: \"Compiling...\"});\n this.status = 'compiling';\n this.svgs = [];\n this.frame_count = 0;\n this.next_frame = 0;\n } else if (event.data === \"Done\") {\n this.setState({message: \"\"});\n console.log(\"Done\");\n this.start = Date.now();\n } else if (event.data.startsWith(\"Error\")) {\n console.log(\"Error\", event.data.substring(5));\n this.setState({message: event.data.substring(5)});\n } else {\n const num = parseInt(event.data);\n if(isNaN(num)) {\n // this.setState({message: `Rendering: ${this.nFrames_new}`});\n // this.nFrames_new++;\n // const div = document.createElement('div');\n // div.innerHTML = event.data;\n // this.svgs_new.push(div);\n const div = document.createElement('div');\n div.innerHTML = event.data;\n this.svgs[this.next_frame] = div;\n var count = 0;\n this.svgs.forEach(_ => count++);\n console.log('Received', this.next_frame, this.frame_count, count);\n } else {\n\n if( this.status === 'compiling' ) {\n this.setState({message: `Rendering...`});\n this.frame_count = num;\n this.status = 'rendering';\n this.start = Date.now();\n this.svgs = [];\n this.svgs[this.frame_count-1] = undefined;\n } else if( this.status === 'rendering' ) {\n this.next_frame = num;\n } else {\n console.log(\"Bad state change: received number\");\n }\n }\n }\n }\n this.setState(state => ({\n ...state,\n socket: ws,\n message: \"Connecting...\"\n }));\n }\n constructor(props) {\n super(props);\n\n this.state = {\n };\n setTimeout(this.connect, 0);\n this.svgs = [];\n this.start = Date.now();\n\n this.status = '';\n this.frame_count = 0;\n this.next_frame = 0;\n\n const self = this;\n const animate = () => {\n const now = Date.now();\n const nFrames = self.frame_count;\n const aniDuration = self.frame_count/60;\n const thisFrame = (Math.round((now - this.start) / 1000 * 60)) % nFrames\n // const thisFrame = 0; console.log('Animation frame:', thisFrame, nFrames);\n var count = 0;\n this.svgs.forEach(_ => count++);\n if (nFrames) {\n // self.svg.innerHTML = self.svgs[thisFrame];\n if(self.svgs[thisFrame]) {\n // self.hud.innerText = '' + thisFrame + '/' + self.frame_count;\n this.setState({message: '' + thisFrame + '/' + self.frame_count + ' ' + Math.round(count/aniDuration) + ' fps'});\n while (self.svg.firstChild)\n self.svg.removeChild(self.svg.firstChild);\n self.svg.appendChild(self.svgs[thisFrame]);\n }\n } else {\n self.svg.innerText = \"\";\n }\n requestAnimationFrame(animate);\n };\n requestAnimationFrame(animate);\n }\n onLoad = ace => {\n setTimeout(function() {\n ace.resize();\n }, 0);\n }\n render() {\n const {message} = this.state;\n return (\n
\n
\n
this.svg = node}/>\n
\n
{message}
\n
\n
\n
\n );\n }\n}\n\nexport default App;\n","// This optional code is used to register a service worker.\n// register() is not called by default.\n\n// This lets the app load faster on subsequent visits in production, and gives\n// it offline capabilities. However, it also means that developers (and users)\n// will only see deployed updates on subsequent visits to a page, after all the\n// existing tabs open on the page have been closed, since previously cached\n// resources are updated in the background.\n\n// To learn more about the benefits of this model and instructions on how to\n// opt-in, read http://bit.ly/CRA-PWA\n\nconst isLocalhost = Boolean(\n window.location.hostname === 'localhost' ||\n // [::1] is the IPv6 localhost address.\n window.location.hostname === '[::1]' ||\n // 127.0.0.1/8 is considered localhost for IPv4.\n window.location.hostname.match(\n /^127(?:\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/\n )\n);\n\nexport function register(config) {\n if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {\n // The URL constructor is available in all browsers that support SW.\n const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);\n if (publicUrl.origin !== window.location.origin) {\n // Our service worker won't work if PUBLIC_URL is on a different origin\n // from what our page is served on. This might happen if a CDN is used to\n // serve assets; see https://github.com/facebook/create-react-app/issues/2374\n return;\n }\n\n window.addEventListener('load', () => {\n const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;\n\n if (isLocalhost) {\n // This is running on localhost. Let's check if a service worker still exists or not.\n checkValidServiceWorker(swUrl, config);\n\n // Add some additional logging to localhost, pointing developers to the\n // service worker/PWA documentation.\n navigator.serviceWorker.ready.then(() => {\n console.log(\n 'This web app is being served cache-first by a service ' +\n 'worker. To learn more, visit http://bit.ly/CRA-PWA'\n );\n });\n } else {\n // Is not localhost. Just register service worker\n registerValidSW(swUrl, config);\n }\n });\n }\n}\n\nfunction registerValidSW(swUrl, config) {\n navigator.serviceWorker\n .register(swUrl)\n .then(registration => {\n registration.onupdatefound = () => {\n const installingWorker = registration.installing;\n if (installingWorker == null) {\n return;\n }\n installingWorker.onstatechange = () => {\n if (installingWorker.state === 'installed') {\n if (navigator.serviceWorker.controller) {\n // At this point, the updated precached content has been fetched,\n // but the previous service worker will still serve the older\n // content until all client tabs are closed.\n console.log(\n 'New content is available and will be used when all ' +\n 'tabs for this page are closed. See http://bit.ly/CRA-PWA.'\n );\n\n // Execute callback\n if (config && config.onUpdate) {\n config.onUpdate(registration);\n }\n } else {\n // At this point, everything has been precached.\n // It's the perfect time to display a\n // \"Content is cached for offline use.\" message.\n console.log('Content is cached for offline use.');\n\n // Execute callback\n if (config && config.onSuccess) {\n config.onSuccess(registration);\n }\n }\n }\n };\n };\n })\n .catch(error => {\n console.error('Error during service worker registration:', error);\n });\n}\n\nfunction checkValidServiceWorker(swUrl, config) {\n // Check if the service worker can be found. If it can't reload the page.\n fetch(swUrl)\n .then(response => {\n // Ensure service worker exists, and that we really are getting a JS file.\n const contentType = response.headers.get('content-type');\n if (\n response.status === 404 ||\n (contentType != null && contentType.indexOf('javascript') === -1)\n ) {\n // No service worker found. Probably a different app. Reload the page.\n navigator.serviceWorker.ready.then(registration => {\n registration.unregister().then(() => {\n window.location.reload();\n });\n });\n } else {\n // Service worker found. Proceed as normal.\n registerValidSW(swUrl, config);\n }\n })\n .catch(() => {\n console.log(\n 'No internet connection found. App is running in offline mode.'\n );\n });\n}\n\nexport function unregister() {\n if ('serviceWorker' in navigator) {\n navigator.serviceWorker.ready.then(registration => {\n registration.unregister();\n });\n }\n}\n","import React from 'react';\nimport ReactDOM from 'react-dom';\nimport './index.css';\nimport App from './App';\nimport * as serviceWorker from './serviceWorker';\n\nReactDOM.render(, document.getElementById('root'));\n\n// If you want your app to work offline and load faster, you can change\n// unregister() to register() below. Note this comes with some pitfalls.\n// Learn more about service workers: http://bit.ly/CRA-PWA\nserviceWorker.unregister();\n"],"sourceRoot":""} \ No newline at end of file diff --git a/viewer/build/static/js/main.db22f45d.chunk.js b/viewer/build/static/js/main.db22f45d.chunk.js deleted file mode 100644 index 917516f..0000000 --- a/viewer/build/static/js/main.db22f45d.chunk.js +++ /dev/null @@ -1,2 +0,0 @@ -(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 \ No newline at end of file diff --git a/viewer/build/static/js/main.db22f45d.chunk.js.map b/viewer/build/static/js/main.db22f45d.chunk.js.map deleted file mode 100644 index 2a6b617..0000000 --- a/viewer/build/static/js/main.db22f45d.chunk.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["App.jsx","serviceWorker.js","index.js"],"names":["App","props","_this","Object","classCallCheck","this","possibleConstructorReturn","getPrototypeOf","call","connect","ws","WebSocket","onopen","event","setState","state","objectSpread","message","send","onclose","setTimeout","onmessage","data","console","log","nFrames_new","svgs_new","nFrames","svgs","start","Date","now","startsWith","substring","concat","div","document","createElement","innerHTML","push","socket","onLoad","ace","resize","self","assertThisInitialized","requestAnimationFrame","animate","thisFrame","Math","round","length","svg","firstChild","removeChild","appendChild","innerText","_this2","react_default","a","className","ref","node","Component","Boolean","window","location","hostname","match","ReactDOM","render","src_App_0","getElementById","navigator","serviceWorker","ready","then","registration","unregister"],"mappings":"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","file":"static/js/main.db22f45d.chunk.js","sourcesContent":["import React, {Component} from 'react';\nimport './App.css';\n\nclass App extends Component {\n connect = () => {\n // const ws = new WebSocket(\"wss://reanimate.clozecards.com:9160\");\n const ws = new WebSocket(\"ws://localhost:9161\");\n\n ws.onopen = event => {\n this.setState(state => ({\n ...state,\n message: \"Connected.\"\n }));\n ws.send('60');\n }\n ws.onclose = event => {\n this.setState(state => ({\n ...state,\n message: \"Disconnected.\"\n }));\n setTimeout(this.connect, 1000);\n }\n ws.onmessage = event => {\n if (event.data === \"Success!\") {\n console.log(\"Success\");\n } else if (event.data === \"Compiling\") {\n this.setState({message: \"Compiling...\"});\n } else if (event.data === \"Rendering\") {\n this.setState({message: \"Rendering...\"});\n this.nFrames_new = 0;\n this.svgs_new = [];\n } else if (event.data === \"Done\") {\n this.setState({message: \"\"});\n console.log(\"Done\");\n this.nFrames = this.nFrames_new;\n this.svgs = this.svgs_new;\n this.nFrames_new = 0;\n this.svgs_new = [];\n this.start = Date.now();\n } else if (event.data.startsWith(\"Error\")) {\n console.log(\"Error\");\n this.setState({message: event.data.substring(5)});\n } else {\n this.setState({message: `Rendering: ${this.nFrames_new}`});\n this.nFrames_new++;\n const div = document.createElement('div');\n div.innerHTML = event.data;\n this.svgs_new.push(div);\n }\n }\n this.setState(state => ({\n ...state,\n socket: ws,\n message: \"Connecting...\"\n }));\n }\n constructor(props) {\n super(props);\n\n this.state = {\n };\n setTimeout(this.connect, 0);\n this.nFrames_new = 0;\n this.svgs_new = [];\n this.nFrames = 0;\n this.svgs = [];\n this.start = Date.now();\n const self = this;\n const animate = () => {\n const now = Date.now();\n const nFrames = self.nFrames;\n const thisFrame = (Math.round((now - this.start) / 1000 * 60)) % nFrames\n // const thisFrame = 0; console.log('Animation frame:', thisFrame, nFrames);\n if (self.svgs_new.length) {\n while (self.svg.firstChild)\n self.svg.removeChild(self.svg.firstChild);\n self.svg.appendChild(self.svgs_new[self.svgs_new.length-1]);\n } else {\n if (nFrames) {\n // self.svg.innerHTML = self.svgs[thisFrame];\n while (self.svg.firstChild)\n self.svg.removeChild(self.svg.firstChild);\n self.svg.appendChild(self.svgs[thisFrame]);\n } else {\n self.svg.innerText = \"\";\n }\n }\n requestAnimationFrame(animate);\n };\n requestAnimationFrame(animate);\n }\n onLoad = ace => {\n setTimeout(function() {\n ace.resize();\n }, 0);\n }\n render() {\n const {message} = this.state;\n return (\n
\n
\n
this.svg = node}/>\n
\n
{message}
\n
\n
\n
\n );\n }\n}\n\nexport default App;\n","// This optional code is used to register a service worker.\n// register() is not called by default.\n\n// This lets the app load faster on subsequent visits in production, and gives\n// it offline capabilities. However, it also means that developers (and users)\n// will only see deployed updates on subsequent visits to a page, after all the\n// existing tabs open on the page have been closed, since previously cached\n// resources are updated in the background.\n\n// To learn more about the benefits of this model and instructions on how to\n// opt-in, read http://bit.ly/CRA-PWA\n\nconst isLocalhost = Boolean(\n window.location.hostname === 'localhost' ||\n // [::1] is the IPv6 localhost address.\n window.location.hostname === '[::1]' ||\n // 127.0.0.1/8 is considered localhost for IPv4.\n window.location.hostname.match(\n /^127(?:\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/\n )\n);\n\nexport function register(config) {\n if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {\n // The URL constructor is available in all browsers that support SW.\n const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);\n if (publicUrl.origin !== window.location.origin) {\n // Our service worker won't work if PUBLIC_URL is on a different origin\n // from what our page is served on. This might happen if a CDN is used to\n // serve assets; see https://github.com/facebook/create-react-app/issues/2374\n return;\n }\n\n window.addEventListener('load', () => {\n const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;\n\n if (isLocalhost) {\n // This is running on localhost. Let's check if a service worker still exists or not.\n checkValidServiceWorker(swUrl, config);\n\n // Add some additional logging to localhost, pointing developers to the\n // service worker/PWA documentation.\n navigator.serviceWorker.ready.then(() => {\n console.log(\n 'This web app is being served cache-first by a service ' +\n 'worker. To learn more, visit http://bit.ly/CRA-PWA'\n );\n });\n } else {\n // Is not localhost. Just register service worker\n registerValidSW(swUrl, config);\n }\n });\n }\n}\n\nfunction registerValidSW(swUrl, config) {\n navigator.serviceWorker\n .register(swUrl)\n .then(registration => {\n registration.onupdatefound = () => {\n const installingWorker = registration.installing;\n if (installingWorker == null) {\n return;\n }\n installingWorker.onstatechange = () => {\n if (installingWorker.state === 'installed') {\n if (navigator.serviceWorker.controller) {\n // At this point, the updated precached content has been fetched,\n // but the previous service worker will still serve the older\n // content until all client tabs are closed.\n console.log(\n 'New content is available and will be used when all ' +\n 'tabs for this page are closed. See http://bit.ly/CRA-PWA.'\n );\n\n // Execute callback\n if (config && config.onUpdate) {\n config.onUpdate(registration);\n }\n } else {\n // At this point, everything has been precached.\n // It's the perfect time to display a\n // \"Content is cached for offline use.\" message.\n console.log('Content is cached for offline use.');\n\n // Execute callback\n if (config && config.onSuccess) {\n config.onSuccess(registration);\n }\n }\n }\n };\n };\n })\n .catch(error => {\n console.error('Error during service worker registration:', error);\n });\n}\n\nfunction checkValidServiceWorker(swUrl, config) {\n // Check if the service worker can be found. If it can't reload the page.\n fetch(swUrl)\n .then(response => {\n // Ensure service worker exists, and that we really are getting a JS file.\n const contentType = response.headers.get('content-type');\n if (\n response.status === 404 ||\n (contentType != null && contentType.indexOf('javascript') === -1)\n ) {\n // No service worker found. Probably a different app. Reload the page.\n navigator.serviceWorker.ready.then(registration => {\n registration.unregister().then(() => {\n window.location.reload();\n });\n });\n } else {\n // Service worker found. Proceed as normal.\n registerValidSW(swUrl, config);\n }\n })\n .catch(() => {\n console.log(\n 'No internet connection found. App is running in offline mode.'\n );\n });\n}\n\nexport function unregister() {\n if ('serviceWorker' in navigator) {\n navigator.serviceWorker.ready.then(registration => {\n registration.unregister();\n });\n }\n}\n","import React from 'react';\nimport ReactDOM from 'react-dom';\nimport './index.css';\nimport App from './App';\nimport * as serviceWorker from './serviceWorker';\n\nReactDOM.render(, document.getElementById('root'));\n\n// If you want your app to work offline and load faster, you can change\n// unregister() to register() below. Note this comes with some pitfalls.\n// Learn more about service workers: http://bit.ly/CRA-PWA\nserviceWorker.unregister();\n"],"sourceRoot":""} \ No newline at end of file diff --git a/viewer/src/App.css b/viewer/src/App.css index 79dde98..32577c6 100755 --- a/viewer/src/App.css +++ b/viewer/src/App.css @@ -47,7 +47,6 @@ div.messages pre { margin-right: 2em; } - #editor { } diff --git a/viewer/src/App.jsx b/viewer/src/App.jsx index c2fe0a7..5cd1a42 100755 --- a/viewer/src/App.jsx +++ b/viewer/src/App.jsx @@ -25,27 +25,46 @@ class App extends Component { console.log("Success"); } else if (event.data === "Compiling") { this.setState({message: "Compiling..."}); - } else if (event.data === "Rendering") { - this.setState({message: "Rendering..."}); - this.nFrames_new = 0; - this.svgs_new = []; + this.status = 'compiling'; + this.svgs = []; + this.frame_count = 0; + this.next_frame = 0; } else if (event.data === "Done") { this.setState({message: ""}); console.log("Done"); - this.nFrames = this.nFrames_new; - this.svgs = this.svgs_new; - this.nFrames_new = 0; - this.svgs_new = []; this.start = Date.now(); } else if (event.data.startsWith("Error")) { - console.log("Error"); + console.log("Error", event.data.substring(5)); this.setState({message: event.data.substring(5)}); } else { - this.setState({message: `Rendering: ${this.nFrames_new}`}); - this.nFrames_new++; - const div = document.createElement('div'); - div.innerHTML = event.data; - this.svgs_new.push(div); + const num = parseInt(event.data); + if(isNaN(num)) { + // this.setState({message: `Rendering: ${this.nFrames_new}`}); + // this.nFrames_new++; + // const div = document.createElement('div'); + // div.innerHTML = event.data; + // this.svgs_new.push(div); + const div = document.createElement('div'); + div.innerHTML = event.data; + this.svgs[this.next_frame] = div; + var count = 0; + this.svgs.forEach(_ => count++); + console.log('Received', this.next_frame, this.frame_count, count); + } else { + + if( this.status === 'compiling' ) { + this.setState({message: `Rendering...`}); + this.frame_count = num; + this.status = 'rendering'; + this.start = Date.now(); + this.svgs = []; + this.svgs[this.frame_count-1] = undefined; + } else if( this.status === 'rendering' ) { + this.next_frame = num; + } else { + console.log("Bad state change: received number"); + } + } } } this.setState(state => ({ @@ -60,30 +79,33 @@ class App extends Component { this.state = { }; setTimeout(this.connect, 0); - this.nFrames_new = 0; - this.svgs_new = []; - this.nFrames = 0; this.svgs = []; this.start = Date.now(); + + this.status = ''; + this.frame_count = 0; + this.next_frame = 0; + const self = this; const animate = () => { const now = Date.now(); - const nFrames = self.nFrames; + const nFrames = self.frame_count; + const aniDuration = self.frame_count/60; const thisFrame = (Math.round((now - this.start) / 1000 * 60)) % nFrames // const thisFrame = 0; console.log('Animation frame:', thisFrame, nFrames); - if (self.svgs_new.length) { - while (self.svg.firstChild) - self.svg.removeChild(self.svg.firstChild); - self.svg.appendChild(self.svgs_new[self.svgs_new.length-1]); - } else { - if (nFrames) { - // self.svg.innerHTML = self.svgs[thisFrame]; + var count = 0; + this.svgs.forEach(_ => count++); + if (nFrames) { + // self.svg.innerHTML = self.svgs[thisFrame]; + if(self.svgs[thisFrame]) { + // self.hud.innerText = '' + thisFrame + '/' + self.frame_count; + this.setState({message: '' + thisFrame + '/' + self.frame_count + ' ' + Math.round(count/aniDuration) + ' fps'}); while (self.svg.firstChild) self.svg.removeChild(self.svg.firstChild); self.svg.appendChild(self.svgs[thisFrame]); - } else { - self.svg.innerText = ""; } + } else { + self.svg.innerText = ""; } requestAnimationFrame(animate); };