reanimate 0.1.5.0 → 0.1.6.0
raw patch · 30 files changed
+2414/−529 lines, 30 filesdep +QuickCheckdep +ansi-wl-pprintdep +base64-bytestringdep ~basedep ~reanimate-svgPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: QuickCheck, ansi-wl-pprint, base64-bytestring, cassava, colour, here, random-shuffle, reanimate, tasty, tasty-golden, tasty-hunit, temporary, vector
Dependency ranges changed: base, reanimate-svg
API changes (from Hackage documentation)
- Reanimate.Examples: bbox :: Animation
- Reanimate.Examples: bbox1 :: Animation
- Reanimate.Examples: bbox2 :: Animation
- Reanimate.Examples: drawSunflower :: Animation
- Reanimate.Examples: gooEffect :: Animation
- Reanimate.Examples: heartShape :: Tree
- Reanimate.Examples: latex_basic :: Animation
- Reanimate.Examples: latex_color :: Animation
- Reanimate.Examples: latex_draw :: Animation
- Reanimate.Examples: mkBoundingBox :: Tree -> Tree
- Reanimate.Examples: mkFilter :: String -> [FilterElement] -> Filter
- Reanimate.Examples: tangentAndNormal :: Animation
- Reanimate.Examples: valentine :: Animation
- Reanimate.Monad: signal :: Double -> Double -> Frame Double
- Reanimate.Monad: signalSCurve :: Double -> Double -> Double -> Frame Double
+ Reanimate.ColorMap: cividis :: Double -> PixelRGB8
+ Reanimate.ColorMap: greyscale :: Double -> PixelRGB8
+ Reanimate.ColorMap: hsv :: Double -> PixelRGB8
+ Reanimate.ColorMap: inferno :: Double -> PixelRGB8
+ Reanimate.ColorMap: jet :: Double -> PixelRGB8
+ Reanimate.ColorMap: magma :: Double -> PixelRGB8
+ Reanimate.ColorMap: parula :: Double -> PixelRGB8
+ Reanimate.ColorMap: plasma :: Double -> PixelRGB8
+ Reanimate.ColorMap: sinebow :: Double -> PixelRGB8
+ Reanimate.ColorMap: turbo :: Double -> PixelRGB8
+ Reanimate.ColorMap: viridis :: Double -> PixelRGB8
+ Reanimate.ColorSpace: coneSensitivity :: Map Nanometer (Double, Double, Double)
+ Reanimate.ColorSpace: lightLABCoordinates :: Map Nanometer (Double, Double, Double)
+ Reanimate.ColorSpace: lightXYZCoordinates :: Map Nanometer (Double, Double, Double)
+ Reanimate.ColorSpace: renderLABCoordinates :: Tree
+ Reanimate.ColorSpace: renderSensitivity :: Tree
+ Reanimate.ColorSpace: renderXYZCoordinates :: Tree
+ Reanimate.ColorSpace: type Nanometer = Integer
+ Reanimate.Interpolate: ColorComponents :: (Colour Double -> (Double, Double, Double)) -> (Double -> Double -> Double -> Colour Double) -> ColorComponents
+ Reanimate.Interpolate: [colorPack] :: ColorComponents -> Double -> Double -> Double -> Colour Double
+ Reanimate.Interpolate: [colorUnpack] :: ColorComponents -> Colour Double -> (Double, Double, Double)
+ Reanimate.Interpolate: data ColorComponents
+ Reanimate.Interpolate: fromRGB8 :: PixelRGB8 -> Colour Double
+ Reanimate.Interpolate: hsvComponents :: ColorComponents
+ Reanimate.Interpolate: interpolate :: ColorComponents -> Colour Double -> Colour Double -> Double -> Colour Double
+ Reanimate.Interpolate: interpolateRGB8 :: ColorComponents -> PixelRGB8 -> PixelRGB8 -> Double -> PixelRGB8
+ Reanimate.Interpolate: labComponents :: ColorComponents
+ Reanimate.Interpolate: lchComponents :: ColorComponents
+ Reanimate.Interpolate: rgbComponents :: ColorComponents
+ Reanimate.Interpolate: toRGB8 :: Colour Double -> PixelRGB8
+ Reanimate.Interpolate: xyzComponents :: ColorComponents
+ Reanimate.Memo: Key :: !a -> Key
+ Reanimate.Memo: KeyPrim :: !a -> Key
+ Reanimate.Memo: data Key
+ Reanimate.Memo: instance GHC.Classes.Eq Reanimate.Memo.DynamicName
+ Reanimate.Memo: instance GHC.Classes.Ord Reanimate.Memo.DynamicName
+ Reanimate.Memo: memo :: Typeable a => [Key] -> a -> a
+ Reanimate.Monad: freezeFrame :: Double -> Animation -> Frame ()
+ Reanimate.Monad: getSignal :: Signal -> Frame Double
+ Reanimate.Monad: pauseAround :: Double -> Double -> Animation -> Animation
+ Reanimate.Monad: pauseAtBeginning :: Double -> Animation -> Animation
+ Reanimate.Monad: setDuration :: Double -> Animation -> Animation
+ Reanimate.Povray: povray :: [String] -> Text -> Tree
+ Reanimate.Povray: povrayQuick :: [String] -> Text -> Tree
+ Reanimate.Povray: povraySlow :: [String] -> Text -> Tree
+ Reanimate.Raster: embedDynamicImage :: DynamicImage -> Tree
+ Reanimate.Raster: embedImage :: PngSavable a => Image a -> Tree
+ Reanimate.Render: renderSnippets :: Animation -> IO ()
+ Reanimate.Scene: M :: (Time -> ST s (a, Duration, Duration, Timeline)) -> Scene s a
+ Reanimate.Scene: [unM] :: Scene s a -> Time -> ST s (a, Duration, Duration, Timeline)
+ Reanimate.Scene: adjustZ :: (ZIndex -> ZIndex) -> Scene s a -> Scene s a
+ Reanimate.Scene: data World
+ Reanimate.Scene: debug :: String -> Scene s ()
+ Reanimate.Scene: emptyTimeline :: Timeline
+ Reanimate.Scene: fork :: Scene s a -> Scene s a
+ Reanimate.Scene: instance GHC.Base.Applicative (Reanimate.Scene.Scene s)
+ Reanimate.Scene: instance GHC.Base.Functor (Reanimate.Scene.Scene s)
+ Reanimate.Scene: instance GHC.Base.Monad (Reanimate.Scene.Scene s)
+ Reanimate.Scene: newtype Scene s a
+ Reanimate.Scene: play :: Animation -> Scene s ()
+ Reanimate.Scene: playZ :: ZIndex -> Animation -> Scene s ()
+ Reanimate.Scene: queryNow :: Scene s Time
+ Reanimate.Scene: sceneAnimation :: (forall s. Scene s a) -> Animation
+ Reanimate.Scene: someaction :: Scene s ()
+ Reanimate.Scene: type Timeline = [(Time, Animation, ZIndex)]
+ Reanimate.Scene: type ZIndex = Int
+ Reanimate.Scene: unionTimeline :: Timeline -> Timeline -> Timeline
+ Reanimate.Scene: wait :: Duration -> Scene s ()
+ Reanimate.Scene: waitAll :: Scene s a -> Scene s a
+ Reanimate.Scene: waitUntil :: Time -> Scene s ()
+ Reanimate.Scene: withSceneDuration :: Scene s a -> Scene s Duration
+ Reanimate.Signal: signalBell :: Double -> Signal
+ Reanimate.Signal: signalCurve :: Double -> Signal
+ Reanimate.Signal: signalFlat :: Double -> Signal
+ Reanimate.Signal: signalFromList :: [(Double, Signal)] -> Signal
+ Reanimate.Signal: signalFromTo :: Double -> Double -> Signal -> Signal
+ Reanimate.Signal: signalLinear :: Signal
+ Reanimate.Signal: signalReverse :: Signal -> Signal
+ Reanimate.Signal: type Signal = Double -> Double
+ Reanimate.Svg: centerX :: Tree -> Tree
+ Reanimate.Svg: centerY :: Tree -> Tree
+ Reanimate.Svg: mkCircle :: Number -> Tree
+ Reanimate.Svg: mkClipPath :: String -> [Tree] -> Tree
+ Reanimate.Svg: mkDefinitions :: [Tree] -> Tree
+ Reanimate.Svg: mkUse :: String -> Tree
+ Reanimate.Svg: scaleToSize :: Double -> Double -> Tree -> Tree
+ Reanimate.Svg: withGroupOpacity :: Double -> Tree -> Tree
+ Reanimate.Svg: withId :: String -> Tree -> Tree
- Reanimate.Svg: mkRect :: Point -> Number -> Number -> Tree
+ Reanimate.Svg: mkRect :: Number -> Number -> Tree
Files
- ChangeLog.md +19/−2
- data/CIExyz.csv +441/−0
- data/cone_sensitivity_lms.csv +441/−0
- reanimate.cabal +31/−20
- server/Cache.hs +0/−30
- server/Main.hs +0/−117
- src/Reanimate/Cache.hs +15/−12
- src/Reanimate/ColorMap.hs +475/−0
- src/Reanimate/ColorSpace.hs +72/−0
- src/Reanimate/Driver.hs +240/−61
- src/Reanimate/Examples.hs +7/−242
- src/Reanimate/Interpolate.hs +73/−0
- src/Reanimate/LaTeX.hs +1/−1
- src/Reanimate/Memo.hs +73/−0
- src/Reanimate/Misc.hs +2/−2
- src/Reanimate/Monad.hs +17/−11
- src/Reanimate/Povray.hs +67/−0
- src/Reanimate/Raster.hs +37/−0
- src/Reanimate/Render.hs +57/−13
- src/Reanimate/Scene.hs +134/−0
- src/Reanimate/Signal.hs +35/−0
- src/Reanimate/Svg.hs +58/−12
- test/Spec.hs +12/−0
- test/UnitTests.hs +101/−0
- viewer/build/index.html +1/−1
- viewer/build/service-worker.js +1/−1
- viewer/build/static/css/main.6efe09fd.chunk.css +0/−2
- viewer/build/static/css/main.f7ad3e9b.chunk.css +2/−0
- viewer/build/static/js/main.c36ecb4a.chunk.js +2/−0
- viewer/build/static/js/main.db22f45d.chunk.js +0/−2
ChangeLog.md view
@@ -1,5 +1,22 @@-# Revision history for reani+# Revision history for reanimate +## 0.1.6.0 -- 2019-09-14++* Test suite.+* Improved Windows support.+* Automated builds on Linux, Mac, and Windows.+* Automated builds with lts-13, lts-12, and lts-11.+* Monadic language for composing animations (Reanimate.Scene).+* Support for Povray.+* Viewer: Incrementally generate videos with increasing fps.+* 'check' command to verify external dependencies.+* Built-in data for color theory (cone sensitivity etc).+* Built-in colormaps.+* Raster image support.+* Reorganized signals.+* Color interpolation (rgb, hsv, lab, xyz).+* Improved performance of multi-threaded renderer.+ ## 0.1.5.0 -- 2019-07-07 * Basic driver for live previewing and rendering.@@ -7,6 +24,6 @@ transformations, etc). * Automated CI builds (azure). -## 0.1.0.0 -- YYYY-mm-dd+## 0.1.0.0 -- 2019-03-08 * First version. Released on an unsuspecting world.
+ data/CIExyz.csv view
@@ -0,0 +1,441 @@+390,0.16638,0.01830,0.81532 +391,0.16635,0.01846,0.81519 +392,0.16629,0.01858,0.81513 +393,0.16620,0.01867,0.81513 +394,0.16609,0.01872,0.81519 +395,0.16595,0.01874,0.81531 +396,0.16579,0.01872,0.81548 +397,0.16561,0.01867,0.81572 +398,0.16542,0.01857,0.81601 +399,0.16521,0.01844,0.81635 +400,0.16499,0.01827,0.81673 +401,0.16477,0.01807,0.81716 +402,0.16455,0.01784,0.81761 +403,0.16433,0.01761,0.81806 +404,0.16412,0.01738,0.81849 +405,0.16393,0.01718,0.81888 +406,0.16376,0.01702,0.81922 +407,0.16359,0.01688,0.81953 +408,0.16341,0.01676,0.81983 +409,0.16320,0.01664,0.82015 +410,0.16296,0.01653,0.82052 +411,0.16266,0.01640,0.82094 +412,0.16233,0.01627,0.82140 +413,0.16198,0.01614,0.82188 +414,0.16162,0.01603,0.82235 +415,0.16126,0.01594,0.82280 +416,0.16093,0.01587,0.82320 +417,0.16060,0.01583,0.82357 +418,0.16028,0.01582,0.82390 +419,0.15994,0.01584,0.82422 +420,0.15958,0.01589,0.82453 +421,0.15920,0.01597,0.82483 +422,0.15879,0.01608,0.82513 +423,0.15836,0.01622,0.82542 +424,0.15793,0.01637,0.82570 +425,0.15750,0.01653,0.82596 +426,0.15709,0.01671,0.82620 +427,0.15669,0.01690,0.82641 +428,0.15628,0.01712,0.82660 +429,0.15586,0.01738,0.82677 +430,0.15540,0.01767,0.82692 +431,0.15491,0.01802,0.82706 +432,0.15439,0.01841,0.82720 +433,0.15384,0.01883,0.82733 +434,0.15329,0.01926,0.82745 +435,0.15276,0.01968,0.82756 +436,0.15225,0.02008,0.82767 +437,0.15178,0.02047,0.82775 +438,0.15131,0.02086,0.82782 +439,0.15085,0.02128,0.82787 +440,0.15036,0.02173,0.82790 +441,0.14985,0.02225,0.82791 +442,0.14929,0.02282,0.82789 +443,0.14871,0.02343,0.82785 +444,0.14811,0.02409,0.82780 +445,0.14749,0.02478,0.82773 +446,0.14687,0.02550,0.82763 +447,0.14624,0.02625,0.82751 +448,0.14560,0.02706,0.82734 +449,0.14493,0.02795,0.82712 +450,0.14423,0.02895,0.82682 +451,0.14349,0.03007,0.82643 +452,0.14271,0.03133,0.82597 +453,0.14186,0.03271,0.82543 +454,0.14095,0.03422,0.82483 +455,0.13997,0.03584,0.82419 +456,0.13890,0.03759,0.82351 +457,0.13775,0.03945,0.82279 +458,0.13653,0.04145,0.82202 +459,0.13525,0.04359,0.82116 +460,0.13392,0.04588,0.82020 +461,0.13254,0.04834,0.81912 +462,0.13112,0.05098,0.81790 +463,0.12964,0.05384,0.81652 +464,0.12807,0.05695,0.81498 +465,0.12638,0.06036,0.81326 +466,0.12456,0.06410,0.81134 +467,0.12259,0.06822,0.80919 +468,0.12046,0.07275,0.80678 +469,0.11818,0.07773,0.80409 +470,0.11574,0.08320,0.80106 +471,0.11313,0.08922,0.79766 +472,0.11034,0.09582,0.79384 +473,0.10736,0.10307,0.78956 +474,0.10418,0.11103,0.78479 +475,0.10078,0.11975,0.77947 +476,0.09715,0.12930,0.77354 +477,0.09329,0.13971,0.76700 +478,0.08923,0.15097,0.75980 +479,0.08497,0.16308,0.75195 +480,0.08055,0.17601,0.74344 +481,0.07599,0.18973,0.73427 +482,0.07130,0.20429,0.72441 +483,0.06649,0.21974,0.71378 +484,0.06155,0.23615,0.70230 +485,0.05651,0.25359,0.68990 +486,0.05138,0.27212,0.67650 +487,0.04622,0.29169,0.66209 +488,0.04108,0.31224,0.64668 +489,0.03604,0.33365,0.63031 +490,0.03117,0.35580,0.61304 +491,0.02654,0.37853,0.59493 +492,0.02222,0.40175,0.57604 +493,0.01827,0.42531,0.55642 +494,0.01477,0.44909,0.53614 +495,0.01178,0.47294,0.51529 +496,0.00933,0.49673,0.49393 +497,0.00741,0.52049,0.47210 +498,0.00596,0.54424,0.44980 +499,0.00490,0.56805,0.42705 +500,0.00418,0.59194,0.40387 +501,0.00374,0.61593,0.38033 +502,0.00364,0.63984,0.35652 +503,0.00393,0.66346,0.33260 +504,0.00471,0.68656,0.30872 +505,0.00605,0.70890,0.28505 +506,0.00802,0.73018,0.26180 +507,0.01073,0.74993,0.23934 +508,0.01424,0.76771,0.21805 +509,0.01860,0.78319,0.19821 +510,0.02382,0.79618,0.18001 +511,0.02983,0.80664,0.16352 +512,0.03645,0.81498,0.14857 +513,0.04345,0.82161,0.13495 +514,0.05062,0.82692,0.12246 +515,0.05780,0.83125,0.11095 +516,0.06485,0.83484,0.10031 +517,0.07183,0.83765,0.09052 +518,0.07882,0.83959,0.08158 +519,0.08593,0.84060,0.07347 +520,0.09322,0.84063,0.06615 +521,0.10076,0.83968,0.05957 +522,0.10849,0.83786,0.05365 +523,0.11636,0.83533,0.04831 +524,0.12431,0.83221,0.04349 +525,0.13227,0.82861,0.03912 +526,0.14020,0.82463,0.03517 +527,0.14806,0.82034,0.03160 +528,0.15582,0.81580,0.02838 +529,0.16344,0.81106,0.02550 +530,0.17090,0.80617,0.02292 +531,0.17820,0.80119,0.02062 +532,0.18536,0.79609,0.01855 +533,0.19247,0.79084,0.01668 +534,0.19958,0.78542,0.01500 +535,0.20674,0.77980,0.01346 +536,0.21399,0.77394,0.01207 +537,0.22130,0.76790,0.01080 +538,0.22862,0.76172,0.00966 +539,0.23592,0.75544,0.00864 +540,0.24316,0.74912,0.00773 +541,0.25030,0.74278,0.00692 +542,0.25736,0.73644,0.00620 +543,0.26434,0.73010,0.00556 +544,0.27125,0.72376,0.00499 +545,0.27809,0.71742,0.00448 +546,0.28489,0.71108,0.00403 +547,0.29164,0.70474,0.00362 +548,0.29834,0.69841,0.00326 +549,0.30499,0.69208,0.00293 +550,0.31161,0.68576,0.00263 +551,0.31821,0.67944,0.00236 +552,0.32481,0.67307,0.00211 +553,0.33148,0.66663,0.00189 +554,0.33825,0.66006,0.00170 +555,0.34516,0.65332,0.00152 +556,0.35222,0.64642,0.00136 +557,0.35937,0.63941,0.00123 +558,0.36653,0.63237,0.00110 +559,0.37363,0.62538,0.00099 +560,0.38061,0.61851,0.00089 +561,0.38743,0.61177,0.00080 +562,0.39416,0.60512,0.00072 +563,0.40087,0.59849,0.00064 +564,0.40763,0.59179,0.00058 +565,0.41450,0.58498,0.00052 +566,0.42152,0.57801,0.00047 +567,0.42864,0.57094,0.00042 +568,0.43579,0.56383,0.00038 +569,0.44293,0.55672,0.00034 +570,0.45001,0.54968,0.00031 +571,0.45698,0.54274,0.00028 +572,0.46388,0.53587,0.00025 +573,0.47071,0.52906,0.00023 +574,0.47751,0.52229,0.00020 +575,0.48429,0.51553,0.00019 +576,0.49105,0.50878,0.00017 +577,0.49782,0.50203,0.00015 +578,0.50458,0.49529,0.00014 +579,0.51133,0.48854,0.00013 +580,0.51808,0.48181,0.00011 +581,0.52481,0.47509,0.00010 +582,0.53145,0.46846,0.00009 +583,0.53795,0.46197,0.00008 +584,0.54425,0.45567,0.00008 +585,0.55031,0.44962,0.00007 +586,0.55610,0.44384,0.00006 +587,0.56167,0.43828,0.00006 +588,0.56707,0.43288,0.00005 +589,0.57236,0.42759,0.00005 +590,0.57757,0.42238,0.00004 +591,0.58273,0.41723,0.00004 +592,0.58783,0.41214,0.00004 +593,0.59283,0.40714,0.00003 +594,0.59772,0.40225,0.00003 +595,0.60249,0.39748,0.00003 +596,0.60712,0.39285,0.00003 +597,0.61163,0.38834,0.00002 +598,0.61604,0.38394,0.00002 +599,0.62034,0.37963,0.00002 +600,0.62457,0.37541,0.00002 +601,0.62871,0.37127,0.00002 +602,0.63276,0.36723,0.00002 +603,0.63668,0.36330,0.00002 +604,0.64046,0.35952,0.00001 +605,0.64409,0.35589,0.00001 +606,0.64756,0.35243,0.00001 +607,0.65088,0.34911,0.00001 +608,0.65405,0.34594,0.00001 +609,0.65708,0.34291,0.00001 +610,0.66000,0.33999,0.00001 +611,0.66280,0.33719,0.00001 +612,0.66548,0.33451,0.00001 +613,0.66807,0.33193,0.00001 +614,0.67055,0.32945,0.00001 +615,0.67293,0.32706,0.00001 +616,0.67523,0.32477,0.00000 +617,0.67744,0.32256,0.00000 +618,0.67958,0.32042,0.00000 +619,0.68166,0.31834,0.00000 +620,0.68369,0.31631,0.00000 +621,0.68567,0.31433,0.00000 +622,0.68758,0.31242,0.00000 +623,0.68940,0.31060,0.00000 +624,0.69111,0.30889,0.00000 +625,0.69269,0.30731,0.00000 +626,0.69415,0.30585,0.00000 +627,0.69549,0.30451,0.00000 +628,0.69675,0.30325,0.00000 +629,0.69793,0.30207,0.00000 +630,0.69907,0.30093,0.00000 +631,0.70017,0.29983,0.00000 +632,0.70124,0.29876,0.00000 +633,0.70228,0.29772,0.00000 +634,0.70329,0.29671,0.00000 +635,0.70426,0.29574,0.00000 +636,0.70520,0.29480,0.00000 +637,0.70612,0.29388,0.00000 +638,0.70703,0.29297,0.00000 +639,0.70795,0.29205,0.00000 +640,0.70887,0.29113,0.00000 +641,0.70980,0.29020,0.00000 +642,0.71071,0.28929,0.00000 +643,0.71157,0.28843,0.00000 +644,0.71236,0.28764,0.00000 +645,0.71304,0.28696,0.00000 +646,0.71361,0.28639,0.00000 +647,0.71410,0.28590,0.00000 +648,0.71452,0.28548,0.00000 +649,0.71490,0.28510,0.00000 +650,0.71528,0.28472,0.00000 +651,0.71566,0.28434,0.00000 +652,0.71605,0.28395,0.00000 +653,0.71645,0.28355,0.00000 +654,0.71685,0.28315,0.00000 +655,0.71725,0.28275,0.00000 +656,0.71764,0.28236,0.00000 +657,0.71802,0.28198,0.00000 +658,0.71840,0.28160,0.00000 +659,0.71876,0.28124,0.00000 +660,0.71912,0.28088,0.00000 +661,0.71946,0.28054,0.00000 +662,0.71978,0.28022,0.00000 +663,0.72009,0.27991,0.00000 +664,0.72037,0.27963,0.00000 +665,0.72062,0.27938,0.00000 +666,0.72084,0.27916,0.00000 +667,0.72104,0.27896,0.00000 +668,0.72122,0.27878,0.00000 +669,0.72139,0.27861,0.00000 +670,0.72154,0.27846,0.00000 +671,0.72169,0.27831,0.00000 +672,0.72182,0.27818,0.00000 +673,0.72195,0.27805,0.00000 +674,0.72208,0.27792,0.00000 +675,0.72219,0.27781,0.00000 +676,0.72230,0.27770,0.00000 +677,0.72239,0.27761,0.00000 +678,0.72248,0.27752,0.00000 +679,0.72257,0.27743,0.00000 +680,0.72265,0.27735,0.00000 +681,0.72272,0.27728,0.00000 +682,0.72279,0.27721,0.00000 +683,0.72285,0.27715,0.00000 +684,0.72291,0.27709,0.00000 +685,0.72296,0.27704,0.00000 +686,0.72300,0.27700,0.00000 +687,0.72304,0.27696,0.00000 +688,0.72308,0.27692,0.00000 +689,0.72311,0.27689,0.00000 +690,0.72314,0.27686,0.00000 +691,0.72317,0.27683,0.00000 +692,0.72320,0.27680,0.00000 +693,0.72323,0.27677,0.00000 +694,0.72325,0.27675,0.00000 +695,0.72327,0.27673,0.00000 +696,0.72328,0.27672,0.00000 +697,0.72329,0.27671,0.00000 +698,0.72329,0.27671,0.00000 +699,0.72329,0.27671,0.00000 +700,0.72329,0.27671,0.00000 +701,0.72329,0.27671,0.00000 +702,0.72329,0.27671,0.00000 +703,0.72329,0.27671,0.00000 +704,0.72329,0.27671,0.00000 +705,0.72329,0.27671,0.00000 +706,0.72329,0.27671,0.00000 +707,0.72328,0.27672,0.00000 +708,0.72327,0.27673,0.00000 +709,0.72325,0.27675,0.00000 +710,0.72323,0.27677,0.00000 +711,0.72320,0.27680,0.00000 +712,0.72318,0.27682,0.00000 +713,0.72314,0.27686,0.00000 +714,0.72311,0.27689,0.00000 +715,0.72308,0.27692,0.00000 +716,0.72305,0.27695,0.00000 +717,0.72301,0.27699,0.00000 +718,0.72298,0.27702,0.00000 +719,0.72295,0.27705,0.00000 +720,0.72292,0.27708,0.00000 +721,0.72288,0.27712,0.00000 +722,0.72284,0.27716,0.00000 +723,0.72280,0.27720,0.00000 +724,0.72276,0.27724,0.00000 +725,0.72272,0.27728,0.00000 +726,0.72268,0.27732,0.00000 +727,0.72264,0.27736,0.00000 +728,0.72259,0.27741,0.00000 +729,0.72255,0.27745,0.00000 +730,0.72251,0.27749,0.00000 +731,0.72246,0.27754,0.00000 +732,0.72242,0.27758,0.00000 +733,0.72237,0.27763,0.00000 +734,0.72233,0.27767,0.00000 +735,0.72228,0.27772,0.00000 +736,0.72222,0.27778,0.00000 +737,0.72217,0.27783,0.00000 +738,0.72211,0.27789,0.00000 +739,0.72204,0.27796,0.00000 +740,0.72198,0.27802,0.00000 +741,0.72191,0.27809,0.00000 +742,0.72184,0.27816,0.00000 +743,0.72177,0.27823,0.00000 +744,0.72169,0.27831,0.00000 +745,0.72162,0.27838,0.00000 +746,0.72155,0.27845,0.00000 +747,0.72148,0.27852,0.00000 +748,0.72141,0.27859,0.00000 +749,0.72134,0.27866,0.00000 +750,0.72127,0.27873,0.00000 +751,0.72120,0.27880,0.00000 +752,0.72112,0.27888,0.00000 +753,0.72105,0.27895,0.00000 +754,0.72098,0.27902,0.00000 +755,0.72091,0.27909,0.00000 +756,0.72083,0.27917,0.00000 +757,0.72075,0.27925,0.00000 +758,0.72068,0.27932,0.00000 +759,0.72060,0.27940,0.00000 +760,0.72052,0.27948,0.00000 +761,0.72044,0.27956,0.00000 +762,0.72037,0.27963,0.00000 +763,0.72030,0.27970,0.00000 +764,0.72022,0.27978,0.00000 +765,0.72015,0.27985,0.00000 +766,0.72008,0.27992,0.00000 +767,0.72001,0.27999,0.00000 +768,0.71994,0.28006,0.00000 +769,0.71987,0.28013,0.00000 +770,0.71980,0.28020,0.00000 +771,0.71973,0.28027,0.00000 +772,0.71966,0.28034,0.00000 +773,0.71958,0.28042,0.00000 +774,0.71951,0.28049,0.00000 +775,0.71943,0.28057,0.00000 +776,0.71936,0.28064,0.00000 +777,0.71928,0.28072,0.00000 +778,0.71920,0.28080,0.00000 +779,0.71912,0.28088,0.00000 +780,0.71904,0.28096,0.00000 +781,0.71895,0.28105,0.00000 +782,0.71887,0.28113,0.00000 +783,0.71879,0.28121,0.00000 +784,0.71870,0.28130,0.00000 +785,0.71862,0.28138,0.00000 +786,0.71853,0.28147,0.00000 +787,0.71845,0.28155,0.00000 +788,0.71836,0.28164,0.00000 +789,0.71828,0.28172,0.00000 +790,0.71819,0.28181,0.00000 +791,0.71810,0.28190,0.00000 +792,0.71802,0.28198,0.00000 +793,0.71793,0.28207,0.00000 +794,0.71783,0.28217,0.00000 +795,0.71774,0.28226,0.00000 +796,0.71764,0.28236,0.00000 +797,0.71753,0.28247,0.00000 +798,0.71743,0.28257,0.00000 +799,0.71732,0.28268,0.00000 +800,0.71721,0.28279,0.00000 +801,0.71710,0.28290,0.00000 +802,0.71699,0.28301,0.00000 +803,0.71688,0.28312,0.00000 +804,0.71677,0.28323,0.00000 +805,0.71666,0.28334,0.00000 +806,0.71655,0.28345,0.00000 +807,0.71644,0.28356,0.00000 +808,0.71633,0.28367,0.00000 +809,0.71622,0.28378,0.00000 +810,0.71611,0.28389,0.00000 +811,0.71599,0.28401,0.00000 +812,0.71588,0.28412,0.00000 +813,0.71577,0.28423,0.00000 +814,0.71566,0.28434,0.00000 +815,0.71555,0.28445,0.00000 +816,0.71544,0.28456,0.00000 +817,0.71533,0.28467,0.00000 +818,0.71522,0.28478,0.00000 +819,0.71512,0.28488,0.00000 +820,0.71502,0.28498,0.00000 +821,0.71492,0.28508,0.00000 +822,0.71482,0.28518,0.00000 +823,0.71473,0.28527,0.00000 +824,0.71464,0.28536,0.00000 +825,0.71455,0.28545,0.00000 +826,0.71447,0.28553,0.00000 +827,0.71439,0.28561,0.00000 +828,0.71431,0.28569,0.00000 +829,0.71424,0.28576,0.00000 +830,0.71417,0.28583,0.00000
+ data/cone_sensitivity_lms.csv view
@@ -0,0 +1,441 @@+390, 4.15003E-04, 3.68349E-04, 9.54729E-03 +391, 5.02650E-04, 4.48015E-04, 1.14794E-02 +392, 6.07367E-04, 5.43965E-04, 1.37986E-02 +393, 7.31850E-04, 6.58983E-04, 1.65746E-02 +394, 8.79012E-04, 7.96121E-04, 1.98869E-02 +395, 1.05192E-03, 9.58658E-04, 2.38250E-02 +396, 1.25373E-03, 1.15002E-03, 2.84877E-02 +397, 1.48756E-03, 1.37367E-03, 3.39832E-02 +398, 1.75633E-03, 1.63296E-03, 4.04274E-02 +399, 2.06261E-03, 1.93089E-03, 4.79417E-02 +400, 2.40836E-03, 2.26991E-03, 5.66498E-02 +401, 2.79522E-03, 2.65210E-03, 6.66757E-02 +402, 3.22640E-03, 3.08110E-03, 7.81479E-02 +403, 3.70617E-03, 3.56156E-03, 9.11925E-02 +404, 4.23972E-03, 4.09900E-03, 1.05926E-01 +405, 4.83339E-03, 4.70010E-03, 1.22451E-01 +406, 5.49335E-03, 5.37186E-03, 1.40844E-01 +407, 6.21933E-03, 6.11757E-03, 1.61140E-01 +408, 7.00631E-03, 6.93795E-03, 1.83325E-01 +409, 7.84503E-03, 7.83144E-03, 2.07327E-01 +410, 8.72127E-03, 8.79369E-03, 2.33008E-01 +411, 9.61879E-03, 9.81865E-03, 2.60183E-01 +412, 1.05324E-02, 1.09044E-02, 2.88723E-01 +413, 1.14620E-02, 1.20509E-02, 3.18512E-01 +414, 1.24105E-02, 1.32582E-02, 3.49431E-01 +415, 1.33837E-02, 1.45277E-02, 3.81363E-01 +416, 1.43870E-02, 1.58600E-02, 4.14141E-01 +417, 1.54116E-02, 1.72496E-02, 4.47350E-01 +418, 1.64424E-02, 1.86878E-02, 4.80439E-01 +419, 1.74614E-02, 2.01638E-02, 5.12767E-01 +420, 1.84480E-02, 2.16649E-02, 5.43618E-01 +421, 1.93852E-02, 2.31801E-02, 5.72399E-01 +422, 2.02811E-02, 2.47139E-02, 5.99284E-01 +423, 2.11545E-02, 2.62785E-02, 6.24786E-01 +424, 2.20286E-02, 2.78905E-02, 6.49576E-01 +425, 2.29317E-02, 2.95714E-02, 6.74474E-01 +426, 2.38896E-02, 3.13438E-02, 7.00186E-01 +427, 2.49026E-02, 3.32151E-02, 7.26460E-01 +428, 2.59631E-02, 3.51889E-02, 7.52726E-01 +429, 2.70619E-02, 3.72683E-02, 7.78333E-01 +430, 2.81877E-02, 3.94566E-02, 8.02555E-01 +431, 2.93303E-02, 4.17546E-02, 8.24818E-01 +432, 3.04898E-02, 4.41544E-02, 8.45422E-01 +433, 3.16694E-02, 4.66432E-02, 8.64961E-01 +434, 3.28731E-02, 4.92050E-02, 8.84100E-01 +435, 3.41054E-02, 5.18199E-02, 9.03573E-01 +436, 3.53670E-02, 5.44645E-02, 9.23844E-01 +437, 3.66400E-02, 5.71131E-02, 9.44055E-01 +438, 3.78988E-02, 5.97369E-02, 9.62920E-01 +439, 3.91148E-02, 6.23038E-02, 9.79057E-01 +440, 4.02563E-02, 6.47782E-02, 9.91020E-01 +441, 4.12989E-02, 6.71329E-02, 9.97765E-01 +442, 4.22582E-02, 6.93844E-02, 9.99982E-01 +443, 4.31627E-02, 7.15658E-02, 9.98861E-01 +444, 4.40444E-02, 7.37164E-02, 9.95628E-01 +445, 4.49380E-02, 7.58812E-02, 9.91515E-01 +446, 4.58729E-02, 7.80980E-02, 9.87377E-01 +447, 4.68459E-02, 8.03538E-02, 9.82619E-01 +448, 4.78446E-02, 8.26198E-02, 9.76301E-01 +449, 4.88555E-02, 8.48644E-02, 9.67513E-01 +450, 4.98639E-02, 8.70524E-02, 9.55393E-01 +451, 5.08618E-02, 8.91640E-02, 9.39499E-01 +452, 5.18731E-02, 9.12523E-02, 9.20807E-01 +453, 5.29317E-02, 9.33950E-02, 9.00592E-01 +454, 5.40746E-02, 9.56775E-02, 8.80040E-01 +455, 5.53418E-02, 9.81934E-02, 8.60240E-01 +456, 5.67734E-02, 1.01031E-01, 8.42031E-01 +457, 5.83973E-02, 1.04229E-01, 8.25572E-01 +458, 6.02409E-02, 1.07814E-01, 8.10860E-01 +459, 6.23353E-02, 1.11817E-01, 7.97902E-01 +460, 6.47164E-02, 1.16272E-01, 7.86704E-01 +461, 6.74131E-02, 1.21204E-01, 7.77119E-01 +462, 7.04040E-02, 1.26573E-01, 7.68365E-01 +463, 7.36489E-02, 1.32311E-01, 7.59538E-01 +464, 7.70978E-02, 1.38334E-01, 7.49777E-01 +465, 8.06894E-02, 1.44541E-01, 7.38268E-01 +466, 8.43613E-02, 1.50828E-01, 7.24389E-01 +467, 8.80904E-02, 1.57146E-01, 7.08113E-01 +468, 9.18630E-02, 1.63457E-01, 6.89572E-01 +469, 9.56637E-02, 1.69721E-01, 6.68927E-01 +470, 9.94755E-02, 1.75893E-01, 6.46359E-01 +471, 1.03286E-01, 1.81938E-01, 6.22112E-01 +472, 1.07103E-01, 1.87872E-01, 5.96591E-01 +473, 1.10947E-01, 1.93731E-01, 5.70216E-01 +474, 1.14838E-01, 1.99557E-01, 5.43373E-01 +475, 1.18802E-01, 2.05398E-01, 5.16411E-01 +476, 1.22863E-01, 2.11302E-01, 4.89647E-01 +477, 1.27026E-01, 2.17283E-01, 4.63406E-01 +478, 1.31293E-01, 2.23347E-01, 4.37965E-01 +479, 1.35666E-01, 2.29501E-01, 4.13549E-01 +480, 1.40145E-01, 2.35754E-01, 3.90333E-01 +481, 1.44731E-01, 2.42108E-01, 3.68394E-01 +482, 1.49415E-01, 2.48545E-01, 3.47582E-01 +483, 1.54189E-01, 2.55037E-01, 3.27724E-01 +484, 1.59038E-01, 2.61554E-01, 3.08676E-01 +485, 1.63952E-01, 2.68063E-01, 2.90322E-01 +486, 1.68932E-01, 2.74561E-01, 2.72626E-01 +487, 1.74063E-01, 2.81180E-01, 2.55772E-01 +488, 1.79457E-01, 2.88097E-01, 2.39945E-01 +489, 1.85241E-01, 2.95508E-01, 2.25280E-01 +490, 1.91556E-01, 3.03630E-01, 2.11867E-01 +491, 1.98532E-01, 3.12649E-01, 1.99719E-01 +492, 2.06182E-01, 3.22565E-01, 1.88674E-01 +493, 2.14485E-01, 3.33319E-01, 1.78557E-01 +494, 2.23411E-01, 3.44844E-01, 1.69217E-01 +495, 2.32926E-01, 3.57061E-01, 1.60526E-01 +496, 2.42992E-01, 3.69895E-01, 1.52368E-01 +497, 2.53616E-01, 3.83355E-01, 1.44620E-01 +498, 2.64810E-01, 3.97467E-01, 1.37173E-01 +499, 2.76587E-01, 4.12260E-01, 1.29937E-01 +500, 2.88959E-01, 4.27764E-01, 1.22839E-01 +501, 3.01934E-01, 4.44001E-01, 1.15834E-01 +502, 3.15508E-01, 4.60947E-01, 1.08922E-01 +503, 3.29673E-01, 4.78562E-01, 1.02118E-01 +504, 3.44416E-01, 4.96795E-01, 9.54374E-02 +505, 3.59716E-01, 5.15587E-01, 8.88965E-02 +506, 3.75550E-01, 5.34868E-01, 8.25343E-02 +507, 3.91895E-01, 5.54575E-01, 7.64613E-02 +508, 4.08722E-01, 5.74640E-01, 7.07769E-02 +509, 4.25998E-01, 5.94984E-01, 6.55491E-02 +510, 4.43683E-01, 6.15520E-01, 6.08210E-02 +511, 4.61731E-01, 6.36162E-01, 5.65919E-02 +512, 4.80094E-01, 6.56872E-01, 5.27659E-02 +513, 4.98717E-01, 6.77624E-01, 4.92440E-02 +514, 5.17539E-01, 6.98393E-01, 4.59470E-02 +515, 5.36494E-01, 7.19154E-01, 4.28123E-02 +516, 5.55493E-01, 7.39844E-01, 3.98014E-02 +517, 5.74386E-01, 7.60235E-01, 3.69227E-02 +518, 5.92995E-01, 7.80039E-01, 3.41909E-02 +519, 6.11124E-01, 7.98941E-01, 3.16158E-02 +520, 6.28561E-01, 8.16610E-01, 2.92033E-02 +521, 6.45139E-01, 8.32783E-01, 2.69536E-02 +522, 6.60907E-01, 8.47550E-01, 2.48575E-02 +523, 6.75993E-01, 8.61113E-01, 2.29047E-02 +524, 6.90542E-01, 8.73698E-01, 2.10855E-02 +525, 7.04720E-01, 8.85550E-01, 1.93912E-02 +526, 7.18662E-01, 8.96873E-01, 1.78145E-02 +527, 7.32323E-01, 9.07638E-01, 1.63514E-02 +528, 7.45606E-01, 9.17757E-01, 1.49980E-02 +529, 7.58410E-01, 9.27137E-01, 1.37497E-02 +530, 7.70630E-01, 9.35687E-01, 1.26013E-02 +531, 7.82208E-01, 9.43362E-01, 1.15466E-02 +532, 7.93290E-01, 9.50309E-01, 1.05766E-02 +533, 8.04084E-01, 9.56733E-01, 9.68268E-03 +534, 8.14813E-01, 9.62844E-01, 8.85746E-03 +535, 8.25711E-01, 9.68858E-01, 8.09453E-03 +536, 8.36943E-01, 9.74919E-01, 7.38890E-03 +537, 8.48349E-01, 9.80850E-01, 6.73799E-03 +538, 8.59676E-01, 9.86388E-01, 6.13947E-03 +539, 8.70656E-01, 9.91267E-01, 5.59073E-03 +540, 8.81011E-01, 9.95217E-01, 5.08900E-03 +541, 8.90496E-01, 9.98007E-01, 4.63120E-03 +542, 8.99052E-01, 9.99595E-01, 4.21366E-03 +543, 9.06669E-01, 9.99982E-01, 3.83286E-03 +544, 9.13341E-01, 9.99177E-01, 3.48559E-03 +545, 9.19067E-01, 9.97193E-01, 3.16893E-03 +546, 9.23898E-01, 9.94102E-01, 2.88021E-03 +547, 9.28099E-01, 9.90204E-01, 2.61698E-03 +548, 9.31995E-01, 9.85855E-01, 2.37701E-03 +549, 9.35916E-01, 9.81404E-01, 2.15829E-03 +550, 9.40198E-01, 9.77193E-01, 1.95896E-03 +551, 9.45076E-01, 9.73441E-01, 1.77737E-03 +552, 9.50367E-01, 9.69881E-01, 1.61218E-03 +553, 9.55775E-01, 9.66133E-01, 1.46215E-03 +554, 9.61000E-01, 9.61823E-01, 1.32606E-03 +555, 9.65733E-01, 9.56583E-01, 1.20277E-03 +556, 9.69744E-01, 9.50167E-01, 1.09118E-03 +557, 9.73133E-01, 9.42773E-01, 9.90135E-04 +558, 9.76086E-01, 9.34709E-01, 8.98564E-04 +559, 9.78793E-01, 9.26271E-01, 8.15524E-04 +560, 9.81445E-01, 9.17750E-01, 7.40174E-04 +561, 9.84186E-01, 9.09339E-01, 6.71772E-04 +562, 9.86965E-01, 9.00895E-01, 6.09693E-04 +563, 9.89678E-01, 8.92197E-01, 5.53372E-04 +564, 9.92220E-01, 8.83035E-01, 5.02292E-04 +565, 9.94486E-01, 8.73205E-01, 4.55979E-04 +566, 9.96386E-01, 8.62565E-01, 4.13997E-04 +567, 9.97895E-01, 8.51173E-01, 3.75940E-04 +568, 9.99004E-01, 8.39132E-01, 3.41439E-04 +569, 9.99706E-01, 8.26545E-01, 3.10160E-04 +570, 9.99993E-01, 8.13509E-01, 2.81800E-04 +571, 9.99837E-01, 8.00082E-01, 2.56084E-04 +572, 9.99123E-01, 7.86166E-01, 2.32764E-04 +573, 9.97719E-01, 7.71635E-01, 2.11616E-04 +574, 9.95491E-01, 7.56376E-01, 1.92436E-04 +575, 9.92310E-01, 7.40291E-01, 1.75039E-04 +576, 9.88146E-01, 7.23369E-01, 1.59257E-04 +577, 9.83345E-01, 7.05890E-01, 1.44940E-04 +578, 9.78342E-01, 6.88184E-01, 1.31948E-04 +579, 9.73564E-01, 6.70554E-01, 1.20157E-04 +580, 9.69429E-01, 6.53274E-01, 1.09454E-04 +581, 9.66211E-01, 6.36524E-01, 9.97367E-05 +582, 9.63630E-01, 6.20217E-01, 9.09125E-05 +583, 9.61273E-01, 6.04211E-01, 8.28976E-05 +584, 9.58732E-01, 5.88375E-01, 7.56160E-05 +585, 9.55602E-01, 5.72597E-01, 6.89991E-05 +586, 9.51569E-01, 5.56783E-01, 6.29846E-05 +587, 9.46654E-01, 5.40896E-01, 5.75163E-05 +588, 9.40962E-01, 5.24912E-01, 5.25432E-05 +589, 9.34600E-01, 5.08817E-01, 4.80192E-05 +590, 9.27673E-01, 4.92599E-01, 4.39024E-05 +591, 9.20264E-01, 4.76268E-01, 4.01551E-05 +592, 9.12391E-01, 4.59893E-01, 3.67431E-05 +593, 9.04050E-01, 4.43551E-01, 3.36353E-05 +594, 8.95243E-01, 4.27314E-01, 3.08037E-05 +595, 8.85969E-01, 4.11246E-01, 2.82228E-05 +596, 8.76242E-01, 3.95396E-01, 2.58697E-05 +597, 8.66117E-01, 3.79782E-01, 2.37235E-05 +598, 8.55658E-01, 3.64410E-01, 2.17653E-05 +599, 8.44926E-01, 3.49289E-01, 1.99779E-05 +600, 8.33982E-01, 3.34429E-01, 1.83459E-05 +601, 8.22859E-01, 3.19843E-01, 1.68551E-05 +602, 8.11491E-01, 3.05564E-01, 1.54929E-05 +603, 7.99794E-01, 2.91625E-01, 1.42476E-05 +604, 7.87689E-01, 2.78053E-01, 1.31087E-05 +605, 7.75103E-01, 2.64872E-01, 1.20667E-05 +606, 7.61996E-01, 2.52099E-01, 1.11131E-05 +607, 7.48425E-01, 2.39747E-01, 1.02399E-05 +608, 7.34470E-01, 2.27822E-01, 9.43999E-06 +609, 7.20208E-01, 2.16330E-01, 8.70695E-06 +610, 7.05713E-01, 2.05273E-01, 8.03488E-06 +611, 6.91044E-01, 1.94650E-01, 7.41844E-06 +612, 6.76212E-01, 1.84448E-01, 6.85279E-06 +613, 6.61220E-01, 1.74654E-01, 6.33352E-06 +614, 6.46072E-01, 1.65256E-01, 5.85662E-06 +615, 6.30773E-01, 1.56243E-01, 5.41843E-06 +616, 6.15349E-01, 1.47602E-01, +617, 5.99888E-01, 1.39329E-01, +618, 5.84489E-01, 1.31416E-01, +619, 5.69240E-01, 1.23856E-01, +620, 5.54224E-01, 1.16641E-01, +621, 5.39469E-01, 1.09766E-01, +622, 5.24827E-01, 1.03226E-01, +623, 5.10124E-01, 9.70205E-02, +624, 4.95206E-01, 9.11430E-02, +625, 4.79941E-01, 8.55872E-02, +626, 4.64270E-01, 8.03428E-02, +627, 4.48338E-01, 7.53913E-02, +628, 4.32329E-01, 7.07136E-02, +629, 4.16406E-01, 6.62924E-02, +630, 4.00711E-01, 6.21120E-02, +631, 3.85355E-01, 5.81595E-02, +632, 3.70377E-01, 5.44276E-02, +633, 3.55793E-01, 5.09098E-02, +634, 3.41618E-01, 4.75991E-02, +635, 3.27864E-01, 4.44879E-02, +636, 3.14541E-01, 4.15659E-02, +637, 3.01662E-01, 3.88152E-02, +638, 2.89239E-01, 3.62181E-02, +639, 2.77278E-01, 3.37599E-02, +640, 2.65784E-01, 3.14282E-02, +641, 2.54740E-01, 2.92176E-02, +642, 2.44054E-01, 2.71403E-02, +643, 2.33634E-01, 2.52084E-02, +644, 2.23399E-01, 2.34286E-02, +645, 2.13284E-01, 2.18037E-02, +646, 2.03257E-01, 2.03285E-02, +647, 1.93370E-01, 1.89779E-02, +648, 1.83688E-01, 1.77272E-02, +649, 1.74263E-01, 1.65560E-02, +650, 1.65141E-01, 1.54480E-02, +651, 1.56354E-01, 1.43924E-02, +652, 1.47916E-01, 1.33896E-02, +653, 1.39834E-01, 1.24414E-02, +654, 1.32111E-01, 1.15488E-02, +655, 1.24749E-01, 1.07120E-02, +656, 1.17744E-01, 9.92994E-03, +657, 1.11081E-01, 9.20057E-03, +658, 1.04747E-01, 8.52126E-03, +659, 9.87277E-02, 7.88945E-03, +660, 9.30085E-02, 7.30255E-03, +661, 8.75769E-02, 6.75820E-03, +662, 8.24219E-02, 6.25474E-03, +663, 7.75328E-02, 5.79045E-03, +664, 7.28989E-02, 5.36347E-03, +665, 6.85100E-02, 4.97179E-03, +666, 6.43554E-02, 4.61300E-03, +667, 6.04242E-02, 4.28339E-03, +668, 5.67057E-02, 3.97940E-03, +669, 5.31896E-02, 3.69803E-03, +670, 4.98661E-02, 3.43667E-03, +671, 4.67258E-02, 3.19326E-03, +672, 4.37598E-02, 2.96653E-03, +673, 4.09594E-02, 2.75543E-03, +674, 3.83165E-02, 2.55896E-03, +675, 3.58233E-02, 2.37617E-03, +676, 3.34725E-02, 2.20618E-03, +677, 3.12583E-02, 2.04809E-03, +678, 2.91751E-02, 1.90110E-03, +679, 2.72173E-02, 1.76442E-03, +680, 2.53790E-02, 1.63734E-03, +681, 2.36541E-02, 1.51916E-03, +682, 2.20336E-02, 1.40913E-03, +683, 2.05092E-02, 1.30655E-03, +684, 1.90735E-02, 1.21078E-03, +685, 1.77201E-02, 1.12128E-03, +686, 1.64451E-02, 1.03766E-03, +687, 1.52510E-02, 9.59892E-04, +688, 1.41404E-02, 8.87951E-04, +689, 1.31137E-02, 8.21729E-04, +690, 1.21701E-02, 7.61051E-04, +691, 1.13063E-02, 7.05623E-04, +692, 1.05131E-02, 6.54874E-04, +693, 9.78127E-03, 6.08240E-04, +694, 9.10300E-03, 5.65240E-04, +695, 8.47170E-03, 5.25457E-04, +696, 7.88227E-03, 4.88550E-04, +697, 7.33213E-03, 4.54277E-04, +698, 6.81928E-03, 4.22436E-04, +699, 6.34173E-03, 3.92839E-04, +700, 5.89749E-03, 3.65317E-04, +701, 5.48444E-03, 3.39710E-04, +702, 5.09978E-03, 3.15856E-04, +703, 4.74086E-03, 2.93607E-04, +704, 4.40538E-03, 2.72834E-04, +705, 4.09129E-03, 2.53417E-04, +706, 3.79703E-03, 2.35265E-04, +707, 3.52187E-03, 2.18331E-04, +708, 3.26520E-03, 2.02574E-04, +709, 3.02632E-03, 1.87948E-04, +710, 2.80447E-03, 1.74402E-04, +711, 2.59882E-03, 1.61878E-04, +712, 2.40849E-03, 1.50305E-04, +713, 2.23259E-03, 1.39612E-04, +714, 2.07024E-03, 1.29734E-04, +715, 1.92058E-03, 1.20608E-04, +716, 1.78269E-03, 1.12176E-04, +717, 1.65540E-03, 1.04373E-04, +718, 1.53762E-03, 9.71383E-05, +719, 1.42839E-03, 9.04202E-05, +720, 1.32687E-03, 8.41716E-05, +721, 1.23238E-03, 7.83538E-05, +722, 1.14456E-03, 7.29425E-05, +723, 1.06308E-03, 6.79164E-05, +724, 9.87592E-04, 6.32542E-05, +725, 9.17777E-04, 5.89349E-05, +726, 8.53264E-04, 5.49360E-05, +727, 7.93589E-04, 5.12291E-05, +728, 7.38306E-04, 4.77872E-05, +729, 6.87018E-04, 4.45862E-05, +730, 6.39373E-04, 4.16049E-05, +731, 5.95057E-04, 3.88245E-05, +732, 5.53797E-04, 3.62302E-05, +733, 5.15350E-04, 3.38086E-05, +734, 4.79496E-04, 3.15474E-05, +735, 4.46035E-04, 2.94354E-05, +736, 4.14809E-04, 2.74634E-05, +737, 3.85749E-04, 2.56268E-05, +738, 3.58792E-04, 2.39217E-05, +739, 3.33861E-04, 2.23432E-05, +740, 3.10869E-04, 2.08860E-05, +741, 2.89699E-04, 1.95424E-05, +742, 2.70145E-04, 1.82990E-05, +743, 2.52007E-04, 1.71423E-05, +744, 2.35117E-04, 1.60611E-05, +745, 2.19329E-04, 1.50458E-05, +746, 2.04535E-04, 1.40893E-05, +747, 1.90692E-04, 1.31899E-05, +748, 1.77771E-04, 1.23462E-05, +749, 1.65736E-04, 1.15569E-05, +750, 1.54549E-04, 1.08200E-05, +751, 1.44167E-04, 1.01334E-05, +752, 1.34528E-04, 9.49367E-06, +753, 1.25574E-04, 8.89736E-06, +754, 1.17251E-04, 8.34135E-06, +755, 1.09508E-04, 7.82271E-06, +756, 1.02300E-04, 7.33865E-06, +757, 9.55828E-05, 6.88612E-06, +758, 8.93161E-05, 6.46228E-06, +759, 8.34631E-05, 6.06462E-06, +760, 7.79912E-05, 5.69093E-06, +761, 7.28730E-05, 5.33942E-06, +762, 6.80921E-05, 5.00929E-06, +763, 6.36342E-05, 4.69984E-06, +764, 5.94841E-05, 4.41034E-06, +765, 5.56264E-05, 4.13998E-06, +766, 5.20430E-05, 3.88772E-06, +767, 4.87063E-05, 3.65186E-06, +768, 4.55900E-05, 3.43070E-06, +769, 4.26710E-05, 3.22278E-06, +770, 3.99295E-05, 3.02683E-06, +771, 3.73509E-05, 2.84192E-06, +772, 3.49326E-05, 2.66795E-06, +773, 3.26730E-05, 2.50491E-06, +774, 3.05690E-05, 2.35267E-06, +775, 2.86163E-05, 2.21100E-06, +776, 2.68077E-05, 2.07946E-06, +777, 2.51286E-05, 1.95700E-06, +778, 2.35645E-05, 1.84258E-06, +779, 2.21026E-05, 1.73528E-06, +780, 2.07321E-05, 1.63433E-06, +781, 1.94444E-05, 1.53910E-06, +782, 1.82351E-05, 1.44932E-06, +783, 1.71008E-05, 1.36478E-06, +784, 1.60380E-05, 1.28526E-06, +785, 1.50432E-05, 1.21054E-06, +786, 1.41127E-05, 1.14038E-06, +787, 1.32420E-05, 1.07446E-06, +788, 1.24263E-05, 1.01247E-06, +789, 1.16618E-05, 9.54130E-07, +790, 1.09446E-05, 8.99170E-07, +791, 1.02716E-05, 8.47379E-07, +792, 9.64036E-06, 7.98635E-07, +793, 9.04897E-06, 7.52832E-07, +794, 8.49535E-06, 7.09857E-07, +795, 7.97750E-06, 6.69594E-07, +796, 7.49341E-06, 6.31907E-07, +797, 7.04079E-06, 5.96604E-07, +798, 6.61744E-06, 5.63495E-07, +799, 6.22133E-06, 5.32408E-07, +800, 5.85057E-06, 5.03187E-07, +801, 5.50333E-06, 4.75686E-07, +802, 5.17756E-06, 4.49753E-07, +803, 4.87135E-06, 4.25249E-07, +804, 4.58300E-06, 4.02050E-07, +805, 4.31102E-06, 3.80046E-07, +806, 4.05422E-06, 3.59156E-07, +807, 3.81213E-06, 3.39356E-07, +808, 3.58438E-06, 3.20633E-07, +809, 3.37053E-06, 3.02966E-07, +810, 3.17009E-06, 2.86329E-07, +811, 2.98248E-06, 2.70687E-07, +812, 2.80691E-06, 2.55980E-07, +813, 2.64257E-06, 2.42147E-07, +814, 2.48873E-06, 2.29130E-07, +815, 2.34468E-06, 2.16878E-07, +816, 2.20974E-06, 2.05338E-07, +817, 2.08315E-06, 1.94449E-07, +818, 1.96419E-06, 1.84155E-07, +819, 1.85222E-06, 1.74407E-07, +820, 1.74666E-06, 1.65158E-07, +821, 1.64705E-06, 1.56373E-07, +822, 1.55307E-06, 1.48031E-07, +823, 1.46448E-06, 1.40117E-07, +824, 1.38100E-06, 1.32615E-07, +825, 1.30241E-06, 1.25508E-07, +826, 1.22844E-06, 1.18781E-07, +827, 1.15888E-06, 1.12416E-07, +828, 1.09348E-06, 1.06398E-07, +829, 1.03203E-06, 1.00711E-07, +830, 9.74306E-07, 9.53411E-08,
reanimate.cabal view
@@ -2,7 +2,7 @@ -- see http://haskell.org/cabal/users-guide/ name: reanimate-version: 0.1.5.0+version: 0.1.6.0 -- synopsis: -- description: license: PublicDomain@@ -24,9 +24,11 @@ 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.c36ecb4a.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 Source-Repository head Type: git@@ -37,6 +39,7 @@ default-language: Haskell2010 default-extensions: PackageImports exposed-modules: Reanimate.Monad+ Reanimate.Signal Reanimate.Render Reanimate.Examples Reanimate.Combinators@@ -47,30 +50,38 @@ Reanimate.Driver Reanimate.Misc Reanimate.Morph+ Reanimate.Raster+ Reanimate.ColorMap+ Reanimate.ColorSpace+ Reanimate.Interpolate+ Reanimate.Memo+ Reanimate.Scene+ Reanimate.Povray other-modules: Reanimate.Svg.NamedColors Reanimate.Cache Paths_reanimate build-depends: base >=4.10 && <4.13, time, text, filepath, process, directory,- containers, reanimate-svg >= 0.7.0.0, xml, bytestring, lens, linear, mtl, matrix,+ containers, reanimate-svg >= 0.9.1.1, xml, bytestring, lens, linear, mtl, matrix, JuicyPixels, attoparsec, parallel, diagrams, diagrams-svg, diagrams-core, diagrams-lib, diagrams-contrib, svg-builder, matrices, cubicbezier, palette, websockets,- hashable, fsnotify, open-browser--Flag server- Description: Enable rendering server- Default: False+ hashable, fsnotify, open-browser, random-shuffle, base64-bytestring,+ vector, colour, cassava, ansi-wl-pprint, here, temporary -executable reanimate-server- if flag(server)- buildable: True- else- buildable: False+test-suite spec+ type: exitcode-stdio-1.0+ main-is: Spec.hs default-language: Haskell2010- hs-source-dirs: server, src- main-is: Main.hs- other-modules: Reanimate.Misc- Cache- build-depends: base >=4.10 && <4.13, text, websockets, process, filepath, directory,- containers, hashable, time+ other-modules:+ UnitTests+ hs-source-dirs: test+ build-depends:+ base,+ directory,+ filepath,+ bytestring, process,+ reanimate,+ QuickCheck >= 2.1.0,+ tasty, tasty-golden, tasty-hunit,+ ansi-wl-pprint
− server/Cache.hs
@@ -1,30 +0,0 @@-module Cache (lookupCache, insertCache) where--import Control.Concurrent-import Control.Exception-import qualified Data.IntMap as M-import System.Directory-import System.FilePath-import qualified Data.Text as T-import qualified Data.Text.IO as T-import Data.Text (Text)-import Data.Hashable--valueFilePath :: Text -> IO FilePath-valueFilePath key = do- tmp <- getTemporaryDirectory- return $ tmp </> "reanimate" ++ show (hash key) <.> "svgs"---insertCache :: Text -> [Text] -> IO ()-insertCache key value = do- cacheFile <- valueFilePath key- T.writeFile cacheFile (T.unlines value)--lookupCache :: Text -> IO (Maybe [Text])-lookupCache key = do- cacheFile <- valueFilePath key- do svgs <- T.lines <$> T.readFile cacheFile- evaluate (svgs)- return (Just svgs)- `catch` \SomeException{} -> return Nothing
− server/Main.hs
@@ -1,117 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module Main where--import Control.Concurrent-import Control.Exception-import Control.Monad-import Data.Monoid-import Data.Time-import Network.WebSockets-import Control.Monad.Fix-import qualified Data.Text as T-import qualified Data.Text.IO as T-import qualified Data.Map as Map--import Cache-import Reanimate.Misc--main :: IO ()-main = do- runServerWith "127.0.0.1" 9161 opts $ \pending -> do- conn <- acceptRequest pending- thread <- newEmptyMVar- forkPingThread conn 30- forever $ do- msg <- receiveData conn :: IO T.Text- stopWorker conn thread- putMVar thread =<< forkIO (generateResponse conn msg >> tryTakeMVar thread >> return ())- where- opts = defaultConnectionOptions- { connectionCompressionOptions = PermessageDeflateCompression defaultPermessageDeflate }--stopWorker conn mvar = do- mbTid <- tryTakeMVar mvar- case mbTid of- Nothing -> return ()- Just tid -> do- putStrLn "Interrupt"- killThread tid- sendTextData conn $ T.pack $ "Error" ++ "Reset"--generateResponse conn msg = do- mbCached <- lookupCache msg- case mbCached of- Just svgs -> do- putStrLn "Returning cached svg."- sendTextDatas conn (T.pack "Success!" : svgs)- sendTextData conn (T.pack "Done")- Nothing ->- withTempFile ".exe" $ \tmpExecutable ->- withTempFile ".hs" $ \tmpSource ->- withTempDir $ \tmpDir -> do- T.writeFile tmpSource $ T.unlines- ["{-# LANGUAGE Arrows, OverloadedStrings, CPP #-}"- ,"module Main where"- ,"import Reanimate.Monad"- ,"import Reanimate.Combinators"- ,"import Reanimate.LaTeX"- ,"import Codec.Picture.Types"- ,"import Reanimate.Svg"- ,"import Reanimate.Render"- ,"import Data.Monoid"- ,"import Graphics.Svg as S"- ,"main = renderSvgs animation " <> T.pack (show tmpDir)- ,"#line 1 \"animation.hs\""- ] <> msg- putStrLn $ "Compiling program:\n" ++ T.unpack msg- sendTextData conn (T.pack "Compiling")- ret <- timeIt "compile" $- runCmd_ "stack" $ ["ghc", "--"] ++ ghcOptions ++ [tmpSource, "-o", tmpExecutable]- case ret of- Left err -> do- sendTextData conn $ T.pack $ "Error" ++ unlines (drop 3 (lines err))- Right{} -> do- queue <- newChan- tid <- forkIO $ forever $ sendTextData conn =<< readChan queue- sendTextData conn (T.pack "Rendering")- flip onException (killThread tid) $- timeIt "render" $ withTimeout queue conn 60 $ do- getFrame <- runCmdLazy tmpExecutable ["+RTS", "-N", "-M50M", "-RTS"]- flip fix [] $ \loop acc -> do- frame <- getFrame- case frame of- Left "" -> do- writeChan queue (T.pack "Done")- insertCache msg (reverse acc)- Left err -> do- _ <- getChanContents queue- writeChan queue $ T.pack $ "Error" ++ err- Right frame -> do- writeChan queue frame- loop (frame : acc)--ghcOptions :: [String]-ghcOptions = ["-rtsopts", "--make", "-threaded", "-O"]--withTimeout queue conn t action = do- finished <- newEmptyMVar- worker <- forkIO (action >> putMVar finished ())- timer <- forkIO $ do- threadDelay (10^6 * t)- putMVar finished ()- putStrLn "Timeout"- killThread worker- _ <- getChanContents queue- writeChan queue $ T.pack $ "Error" ++ "Timeout"-- takeMVar finished `onException` do- killThread worker- killThread timer--timeIt :: String -> IO a -> IO a-timeIt label fn = do- t1 <- getCurrentTime- a <- fn- t2 <- getCurrentTime- putStrLn $ label ++ ": " ++ show (diffUTCTime t2 t1)- return a
src/Reanimate/Cache.hs view
@@ -2,6 +2,7 @@ ( cacheMem , cacheDisk , cacheDiskSvg+ , cacheDiskKey , cacheDiskLines ) where @@ -21,6 +22,7 @@ import System.Directory import System.FilePath import System.IO.Unsafe+import System.IO -- Memory cache and disk cache @@ -34,19 +36,20 @@ 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+ Nothing -> genCache root path Just val -> pure val- else do- let tmp = path <.> "tmp"- new <- gen key- T.writeFile tmp (render new)- renameFile tmp path- return new+ else genCache root path+ where+ genCache root path = do+ (tmpPath, tmpHandle) <- openTempFile root (show (hash key))+ new <- gen key+ T.hPutStr tmpHandle (render new)+ hClose tmpHandle+ renameFile tmpPath path+ return new++cacheDiskKey :: Text -> IO Tree -> IO Tree+cacheDiskKey key gen = cacheDiskSvg (const gen) key cacheDiskSvg :: (Text -> IO Tree) -> (Text -> IO Tree) cacheDiskSvg = cacheDisk parse render
+ src/Reanimate/ColorMap.hs view
@@ -0,0 +1,475 @@+{-# LANGUAGE OverloadedStrings #-}+module Reanimate.ColorMap+ ( turbo+ , viridis+ , magma+ , inferno+ , plasma+ , sinebow+ , parula+ , cividis+ , jet+ , hsv+ , greyscale+ ) where++import Data.Text (Text)+import Data.Vector (Vector)+import qualified Data.Text as T+import qualified Data.Vector as V+import Codec.Picture+import Data.Char+import Data.Bits+import qualified Data.Colour.RGBSpace.HSV as HSV+import Data.Colour.RGBSpace++-- | Given a number t in the range [0,1], returns the corresponding color from+-- the “turbo” color scheme by Anton Mikhailov.+turbo :: Double -> PixelRGB8+turbo t = PixelRGB8 red green blue+ where+ red = trunc (round (34.61 + t * (1172.33 - t * (10793.56 - t * (33300.12 - t * (38394.49 - t * 14825.05))))))+ green = trunc (round (23.31 + t * (557.33 + t * (1225.33 - t * (3574.96 - t * (1073.77 + t * 707.56))))))+ blue = trunc (round (27.2 + t * (3211.1 - t * (15327.97 - t * (27814 - t * (22569.18 - t * 6838.66))))))+ trunc :: Integer -> Pixel8+ trunc = fromIntegral . min 255 . max 0++-- | Given a number t in the range [0,1], returns the corresponding color from+-- the “viridis” perceptually-uniform color scheme designed by van der Walt,+-- Smith and Firing for matplotlib, represented as an RGB string.+viridis :: Double -> PixelRGB8+viridis = ramp (colors+ "44015444025645045745055946075a46085c460a5d460b5e470d60470e614710634711644713\+ \6548146748166848176948186a481a6c481b6d481c6e481d6f481f7048207148217348237448\+ \2475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f\+ \463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142\+ \874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b\+ \518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d\+ \355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b\+ \8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a\+ \778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e\+ \25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f\+ \8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e\+ \9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a685\+ \22a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db2\+ \7d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340\+ \bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c765\+ \5ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d0\+ \5477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195\+ \d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2b\+ \b8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e2\+ \19dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8\+ \e621fbe723fde725")++-- | Given a number t in the range [0,1], returns the corresponding color from+-- the “magma” perceptually-uniform color scheme designed by van der Walt and+-- Smith for matplotlib, represented as an RGB string.+magma :: Double -> PixelRGB8+magma = ramp (colors+ "00000401000501010601010802010902020b02020d03030f0303120404140504160605180605\+ \1a07061c08071e0907200a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d3414\+ \0e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e221150241253\+ \25125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f\+ \6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f\+ \127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980\+ \641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621\+ \817822817922827b23827c23827e24828025828125818326818426818627818827818928818b\+ \29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7f\+ \a02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb336\+ \7ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c8\+ \3e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476a\+ \dc4869de4968df4a68e04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb57\+ \60ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695cf56b5cf6\+ \6c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835f\+ \fb8560fb8761fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b\+ \6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb27afe\+ \b47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8d\+ \fecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1fde2\+ \a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fc\+ \f9bbfcfbbdfcfdbf")++-- | Given a number t in the range [0,1], returns the corresponding color from+-- the “inferno” perceptually-uniform color scheme designed by van der Walt+-- and Smith for matplotlib, represented as an RGB string.+inferno :: Double -> PixelRGB8+inferno = ramp (colors+ "00000401000501010601010802010a02020c02020e0302100403120403140504170604190705\+ \1b08051d09061f0a07220b07240c08260d08290e092b10092d110a30120a32140b34150b3716\+ \0b39180c3c190c3e1b0c411c0c431e0c451f0c48210c4a230c4c240c4f260c51280b53290b55\+ \2b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b09643d09653e0966400a\+ \67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d55\+ \0f6d57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e\+ \6a176e6c186e6d186e6f196e71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e\+ \6d7f1e6c801f6c82206c84206b85216b87216b88226a8a226a8c23698d23698f246990256892\+ \25689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62a32c61a52c60\+ \a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b935\+ \56ba3655bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb\+ \4149cc4248ce4347cf4446d04545d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3c\+ \db503bdd513ade5238df5337e05536e15635e25734e35933e45a31e55c30e65d2fe75e2ee860\+ \2de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711ff1731df2\+ \741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890c\+ \f98b0bf98c0af98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca1\+ \08fca309fca50afca60cfca80dfcaa0ffcac11fcae12fcb014fcb216fcb418fbb61afbb81dfb\+ \ba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932f9cb35f8cd37f8cf3af7d13d\+ \f7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865f2ea\+ \69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9\+ \fc9dfafda1fcffa4")++-- | Given a number t in the range [0,1], returns the corresponding color from+-- the “plasma” perceptually-uniform color scheme designed by van der Walt and+-- Smith for matplotlib, represented as an RGB string.+plasma :: Double -> PixelRGB8+plasma = ramp (colors+ "0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05\+ \932c05942e05952f059631059733059735049837049938049a3a049a3c049b3e049c3f049c41\+ \049d43039e44039e46039f48039f4903a04b03a14c02a14e02a25002a25102a35302a35502a4\+ \5601a45801a45901a55b01a55c01a65e01a66001a66100a76300a76400a76600a76700a86900\+ \a86a00a86c00a86e00a86f00a87100a87201a87401a87501a87701a87801a87a02a87b02a87d\+ \03a87e03a88004a88104a78305a78405a78606a68707a68808a68a09a58b0aa58d0ba58e0ca4\+ \8f0da4910ea3920fa39410a29511a19613a19814a099159f9a169f9c179e9d189d9e199da01a\+ \9ca11b9ba21d9aa31e9aa51f99a62098a72197a82296aa2395ab2494ac2694ad2793ae2892b0\+ \2991b12a90b22b8fb32c8eb42e8db52f8cb6308bb7318ab83289ba3388bb3488bc3587bd3786\+ \be3885bf3984c03a83c13b82c23c81c33d80c43e7fc5407ec6417dc7427cc8437bc9447aca45\+ \7acb4679cc4778cc4977cd4a76ce4b75cf4c74d04d73d14e72d24f71d35171d45270d5536fd5\+ \546ed6556dd7566cd8576bd9586ada5a6ada5b69db5c68dc5d67dd5e66de5f65de6164df6263\+ \e06363e16462e26561e26660e3685fe4695ee56a5de56b5de66c5ce76e5be76f5ae87059e971\+ \58e97257ea7457eb7556eb7655ec7754ed7953ed7a52ee7b51ef7c51ef7e50f07f4ff0804ef1\+ \814df1834cf2844bf3854bf3874af48849f48948f58b47f58c46f68d45f68f44f79044f79143\+ \f79342f89441f89540f9973ff9983ef99a3efa9b3dfa9c3cfa9e3bfb9f3afba139fba238fca3\+ \38fca537fca636fca835fca934fdab33fdac33fdae32fdaf31fdb130fdb22ffdb42ffdb52efe\+ \b72dfeb82cfeba2cfebb2bfebd2afebe2afec029fdc229fdc328fdc527fdc627fdc827fdca26\+ \fdcb26fccd25fcce25fcd025fcd225fbd324fbd524fbd724fad824fada24f9dc24f9dd25f8df\+ \25f8e125f7e225f7e425f6e626f6e826f5e926f5eb27f4ed27f3ee27f3f027f2f227f1f426f1\+ \f525f0f724f0f921")++-- | Given a number t in the range [0,1], returns the corresponding color from+-- the “sinebow” color scheme by Jim Bumgardner and Charlie Loyd.+sinebow :: Double -> PixelRGB8+sinebow t = PixelRGB8 r g b+ where+ pi_1_3 = pi / 3+ pi_2_3 = pi * 2 / 3+ x = (0.5 - t) * pi+ r = round $ 255 * sin(x)^2+ g = round $ 255 * sin(x+pi_1_3)^2+ b = round $ 255 * sin(x+pi_2_3)^2++-- | Given a number t in the range [0,1], returns the corresponding color from+-- the “cividis” color vision deficiency-optimized color scheme designed by+-- Nuñez, Anderton, and Renslow, represented as an RGB string.+cividis :: Double -> PixelRGB8+cividis t = PixelRGB8 red green blue+ where+ red = trunc $ round(-4.54 - t * (35.34 - t * (2381.73 - t * (6402.7 - t * (7024.72 - t * 2710.57)))))+ green = trunc $ round(32.49 + t * (170.73 + t * (52.82 - t * (131.46 - t * (176.58 - t * 67.37)))))+ blue = trunc $ round(81.24 + t * (442.36 - t * (2482.43 - t * (6167.24 - t * (6614.94 - t * 2475.67)))))+ trunc :: Integer -> Pixel8+ trunc = fromIntegral . min 255 . max 0++-- | Jet colormap. Used to be the default in matlab. Obsolete.+jet :: Double -> PixelRGB8+jet t = PixelRGB8 red green blue+ where+ red = trunc $ min (4*t - 1.5) (-4*t + 4.5)+ green = trunc $ min (4*t - 0.5) (-4*t + 3.5)+ blue = trunc $ min (4*t + 0.5) (-4*t + 2.5)+ trunc :: Double -> Pixel8+ trunc = fromIntegral . min 255 . max 0 . round . (*) 255++-- | hsv colormap. Goes from 0 degrees to 360 degrees.+hsv :: Double -> PixelRGB8+hsv t = PixelRGB8 (round $ r*255) (round $ g*255) (round $ b*255)+ where+ RGB r g b = HSV.hsv (t * 360) 1 1++-- | Matlab hsv colormap. Goes from 0 degrees to 330 degrees.+hsvMatlab :: Double -> PixelRGB8+hsvMatlab t = PixelRGB8 (round $ r*255) (round $ g*255) (round $ b*255)+ where+ RGB r g b = HSV.hsv (t * 330) 1 1++greyscale :: Double -> PixelRGB8+greyscale t = PixelRGB8 v v v+ where+ v = round $ t * 255++parula :: Double -> PixelRGB8+parula = ramp vec+ where+ vec = V.fromList $ pixels colors+ pixels [] = []+ pixels (r:g:b:xs) =+ PixelRGB8 (round $ r*255) (round $ g*255) (round $ b*255) :+ pixels xs+ colors =+ [0.2081, 0.1663, 0.5292+ ,0.2091, 0.1721, 0.5411+ ,0.2101, 0.1779, 0.5530+ ,0.2109, 0.1837, 0.5650+ ,0.2116, 0.1895, 0.5771+ ,0.2121, 0.1954, 0.5892+ ,0.2124, 0.2013, 0.6013+ ,0.2125, 0.2072, 0.6135+ ,0.2123, 0.2132, 0.6258+ ,0.2118, 0.2192, 0.6381+ ,0.2111, 0.2253, 0.6505+ ,0.2099, 0.2315, 0.6629+ ,0.2084, 0.2377, 0.6753+ ,0.2063, 0.2440, 0.6878+ ,0.2038, 0.2503, 0.7003+ ,0.2006, 0.2568, 0.7129+ ,0.1968, 0.2632, 0.7255+ ,0.1921, 0.2698, 0.7381+ ,0.1867, 0.2764, 0.7507+ ,0.1802, 0.2832, 0.7634+ ,0.1728, 0.2902, 0.7762+ ,0.1641, 0.2975, 0.7890+ ,0.1541, 0.3052, 0.8017+ ,0.1427, 0.3132, 0.8145+ ,0.1295, 0.3217, 0.8269+ ,0.1147, 0.3306, 0.8387+ ,0.0986, 0.3397, 0.8495+ ,0.0816, 0.3486, 0.8588+ ,0.0646, 0.3572, 0.8664+ ,0.0482, 0.3651, 0.8722+ ,0.0329, 0.3724, 0.8765+ ,0.0213, 0.3792, 0.8796+ ,0.0136, 0.3853, 0.8815+ ,0.0086, 0.3911, 0.8827+ ,0.0060, 0.3965, 0.8833+ ,0.0051, 0.4017, 0.8834+ ,0.0054, 0.4066, 0.8831+ ,0.0067, 0.4113, 0.8825+ ,0.0089, 0.4159, 0.8816+ ,0.0116, 0.4203, 0.8805+ ,0.0148, 0.4246, 0.8793+ ,0.0184, 0.4288, 0.8779+ ,0.0223, 0.4329, 0.8763+ ,0.0264, 0.4370, 0.8747+ ,0.0306, 0.4410, 0.8729+ ,0.0349, 0.4449, 0.8711+ ,0.0394, 0.4488, 0.8692+ ,0.0437, 0.4526, 0.8672+ ,0.0477, 0.4564, 0.8652+ ,0.0514, 0.4602, 0.8632+ ,0.0549, 0.4640, 0.8611+ ,0.0582, 0.4677, 0.8589+ ,0.0612, 0.4714, 0.8568+ ,0.0640, 0.4751, 0.8546+ ,0.0666, 0.4788, 0.8525+ ,0.0689, 0.4825, 0.8503+ ,0.0710, 0.4862, 0.8481+ ,0.0729, 0.4899, 0.8460+ ,0.0746, 0.4937, 0.8439+ ,0.0761, 0.4974, 0.8418+ ,0.0773, 0.5012, 0.8398+ ,0.0782, 0.5051, 0.8378+ ,0.0789, 0.5089, 0.8359+ ,0.0794, 0.5129, 0.8341+ ,0.0795, 0.5169, 0.8324+ ,0.0793, 0.5210, 0.8308+ ,0.0788, 0.5251, 0.8293+ ,0.0778, 0.5295, 0.8280+ ,0.0764, 0.5339, 0.8270+ ,0.0746, 0.5384, 0.8261+ ,0.0724, 0.5431, 0.8253+ ,0.0698, 0.5479, 0.8247+ ,0.0668, 0.5527, 0.8243+ ,0.0636, 0.5577, 0.8239+ ,0.0600, 0.5627, 0.8237+ ,0.0562, 0.5677, 0.8234+ ,0.0523, 0.5727, 0.8231+ ,0.0484, 0.5777, 0.8228+ ,0.0445, 0.5826, 0.8223+ ,0.0408, 0.5874, 0.8217+ ,0.0372, 0.5922, 0.8209+ ,0.0342, 0.5968, 0.8198+ ,0.0317, 0.6012, 0.8186+ ,0.0296, 0.6055, 0.8171+ ,0.0279, 0.6097, 0.8154+ ,0.0265, 0.6137, 0.8135+ ,0.0255, 0.6176, 0.8114+ ,0.0248, 0.6214, 0.8091+ ,0.0243, 0.6250, 0.8066+ ,0.0239, 0.6285, 0.8039+ ,0.0237, 0.6319, 0.8010+ ,0.0235, 0.6352, 0.7980+ ,0.0233, 0.6384, 0.7948+ ,0.0231, 0.6415, 0.7916+ ,0.0230, 0.6445, 0.7881+ ,0.0229, 0.6474, 0.7846+ ,0.0227, 0.6503, 0.7810+ ,0.0227, 0.6531, 0.7773+ ,0.0232, 0.6558, 0.7735+ ,0.0238, 0.6585, 0.7696+ ,0.0246, 0.6611, 0.7656+ ,0.0263, 0.6637, 0.7615+ ,0.0282, 0.6663, 0.7574+ ,0.0306, 0.6688, 0.7532+ ,0.0338, 0.6712, 0.7490+ ,0.0373, 0.6737, 0.7446+ ,0.0418, 0.6761, 0.7402+ ,0.0467, 0.6784, 0.7358+ ,0.0516, 0.6808, 0.7313+ ,0.0574, 0.6831, 0.7267+ ,0.0629, 0.6854, 0.7221+ ,0.0692, 0.6877, 0.7173+ ,0.0755, 0.6899, 0.7126+ ,0.0820, 0.6921, 0.7078+ ,0.0889, 0.6943, 0.7029+ ,0.0956, 0.6965, 0.6979+ ,0.1031, 0.6986, 0.6929+ ,0.1104, 0.7007, 0.6878+ ,0.1180, 0.7028, 0.6827+ ,0.1258, 0.7049, 0.6775+ ,0.1335, 0.7069, 0.6723+ ,0.1418, 0.7089, 0.6669+ ,0.1499, 0.7109, 0.6616+ ,0.1585, 0.7129, 0.6561+ ,0.1671, 0.7148, 0.6507+ ,0.1758, 0.7168, 0.6451+ ,0.1849, 0.7186, 0.6395+ ,0.1938, 0.7205, 0.6338+ ,0.2033, 0.7223, 0.6281+ ,0.2128, 0.7241, 0.6223+ ,0.2224, 0.7259, 0.6165+ ,0.2324, 0.7275, 0.6107+ ,0.2423, 0.7292, 0.6048+ ,0.2527, 0.7308, 0.5988+ ,0.2631, 0.7324, 0.5929+ ,0.2735, 0.7339, 0.5869+ ,0.2845, 0.7354, 0.5809+ ,0.2953, 0.7368, 0.5749+ ,0.3064, 0.7381, 0.5689+ ,0.3177, 0.7394, 0.5630+ ,0.3289, 0.7406, 0.5570+ ,0.3405, 0.7417, 0.5512+ ,0.3520, 0.7428, 0.5453+ ,0.3635, 0.7438, 0.5396+ ,0.3753, 0.7446, 0.5339+ ,0.3869, 0.7454, 0.5283+ ,0.3986, 0.7461, 0.5229+ ,0.4103, 0.7467, 0.5175+ ,0.4218, 0.7473, 0.5123+ ,0.4334, 0.7477, 0.5072+ ,0.4447, 0.7482, 0.5021+ ,0.4561, 0.7485, 0.4972+ ,0.4672, 0.7487, 0.4924+ ,0.4783, 0.7489, 0.4877+ ,0.4892, 0.7491, 0.4831+ ,0.5000, 0.7491, 0.4786+ ,0.5106, 0.7492, 0.4741+ ,0.5212, 0.7492, 0.4698+ ,0.5315, 0.7491, 0.4655+ ,0.5418, 0.7490, 0.4613+ ,0.5519, 0.7489, 0.4571+ ,0.5619, 0.7487, 0.4531+ ,0.5718, 0.7485, 0.4490+ ,0.5816, 0.7482, 0.4451+ ,0.5913, 0.7479, 0.4412+ ,0.6009, 0.7476, 0.4374+ ,0.6103, 0.7473, 0.4335+ ,0.6197, 0.7469, 0.4298+ ,0.6290, 0.7465, 0.4261+ ,0.6382, 0.7460, 0.4224+ ,0.6473, 0.7456, 0.4188+ ,0.6564, 0.7451, 0.4152+ ,0.6653, 0.7446, 0.4116+ ,0.6742, 0.7441, 0.4081+ ,0.6830, 0.7435, 0.4046+ ,0.6918, 0.7430, 0.4011+ ,0.7004, 0.7424, 0.3976+ ,0.7091, 0.7418, 0.3942+ ,0.7176, 0.7412, 0.3908+ ,0.7261, 0.7405, 0.3874+ ,0.7346, 0.7399, 0.3840+ ,0.7430, 0.7392, 0.3806+ ,0.7513, 0.7385, 0.3773+ ,0.7596, 0.7378, 0.3739+ ,0.7679, 0.7372, 0.3706+ ,0.7761, 0.7364, 0.3673+ ,0.7843, 0.7357, 0.3639+ ,0.7924, 0.7350, 0.3606+ ,0.8005, 0.7343, 0.3573+ ,0.8085, 0.7336, 0.3539+ ,0.8166, 0.7329, 0.3506+ ,0.8246, 0.7322, 0.3472+ ,0.8325, 0.7315, 0.3438+ ,0.8405, 0.7308, 0.3404+ ,0.8484, 0.7301, 0.3370+ ,0.8563, 0.7294, 0.3336+ ,0.8642, 0.7288, 0.3300+ ,0.8720, 0.7282, 0.3265+ ,0.8798, 0.7276, 0.3229+ ,0.8877, 0.7271, 0.3193+ ,0.8954, 0.7266, 0.3156+ ,0.9032, 0.7262, 0.3117+ ,0.9110, 0.7259, 0.3078+ ,0.9187, 0.7256, 0.3038+ ,0.9264, 0.7256, 0.2996+ ,0.9341, 0.7256, 0.2953+ ,0.9417, 0.7259, 0.2907+ ,0.9493, 0.7264, 0.2859+ ,0.9567, 0.7273, 0.2808+ ,0.9639, 0.7285, 0.2754+ ,0.9708, 0.7303, 0.2696+ ,0.9773, 0.7326, 0.2634+ ,0.9831, 0.7355, 0.2570+ ,0.9882, 0.7390, 0.2504+ ,0.9922, 0.7431, 0.2437+ ,0.9952, 0.7476, 0.2373+ ,0.9973, 0.7524, 0.2310+ ,0.9986, 0.7573, 0.2251+ ,0.9991, 0.7624, 0.2195+ ,0.9990, 0.7675, 0.2141+ ,0.9985, 0.7726, 0.2090+ ,0.9976, 0.7778, 0.2042+ ,0.9964, 0.7829, 0.1995+ ,0.9950, 0.7880, 0.1949+ ,0.9933, 0.7931, 0.1905+ ,0.9914, 0.7981, 0.1863+ ,0.9894, 0.8032, 0.1821+ ,0.9873, 0.8083, 0.1780+ ,0.9851, 0.8133, 0.1740+ ,0.9828, 0.8184, 0.1700+ ,0.9805, 0.8235, 0.1661+ ,0.9782, 0.8286, 0.1622+ ,0.9759, 0.8337, 0.1583+ ,0.9736, 0.8389, 0.1544+ ,0.9713, 0.8441, 0.1505+ ,0.9692, 0.8494, 0.1465+ ,0.9672, 0.8548, 0.1425+ ,0.9654, 0.8603, 0.1385+ ,0.9638, 0.8659, 0.1343+ ,0.9623, 0.8716, 0.1301+ ,0.9611, 0.8774, 0.1258+ ,0.9600, 0.8834, 0.1215+ ,0.9593, 0.8895, 0.1171+ ,0.9588, 0.8958, 0.1126+ ,0.9586, 0.9022, 0.1082+ ,0.9587, 0.9088, 0.1036+ ,0.9591, 0.9155, 0.0990+ ,0.9599, 0.9225, 0.0944+ ,0.9610, 0.9296, 0.0897+ ,0.9624, 0.9368, 0.0850+ ,0.9641, 0.9443, 0.0802+ ,0.9662, 0.9518, 0.0753+ ,0.9685, 0.9595, 0.0703+ ,0.9710, 0.9673, 0.0651+ ,0.9736, 0.9752, 0.0597+ ,0.9763, 0.9831, 0.0538]++--------------------------------------------------------------------------------+-- Helpers++colors :: Text -> Vector PixelRGB8+colors = V.fromList . map (toColor . map (fromIntegral . digitToInt) . T.unpack) . T.chunksOf 6+ where+ toColor [r1,r2,g1,g2,b1,b2] =+ PixelRGB8 (r1 `shiftL` 4 + r2) (g1 `shiftL` 4 + g2) (b1 `shiftL` 4+b2)++ramp :: Vector PixelRGB8 -> Double -> PixelRGB8+ramp v = \t -> v V.! (max 0 $ min (len-1) $ round $ t * (len'-1))+ where+ len = V.length v+ len' = fromIntegral len
+ src/Reanimate/ColorSpace.hs view
@@ -0,0 +1,72 @@+module Reanimate.ColorSpace where++import qualified Data.ByteString.Lazy as BS+import Data.Csv+import Data.Map (Map)+import qualified Data.Map as Map+import Data.Maybe+import qualified Data.Vector as V+import Data.Colour.CIE+import Data.Colour.CIE.Illuminant+import Paths_reanimate+import System.IO.Unsafe++import Reanimate.Svg+import Graphics.SvgTree (Number(..), Tree)++type Nanometer = Integer+++lightXYZCoordinates :: Map Nanometer (Double, Double, Double)+lightXYZCoordinates = unsafePerformIO $ do+ dat <- BS.readFile =<< getDataFileName "data/CIExyz.csv"+ case decode NoHeader dat of+ Left err -> error err+ Right vec -> return $ Map.fromList+ [ (nm, (x,y,z)) | (nm,x,y,z) <- V.toList vec, nm <= 700 ]++renderXYZCoordinates :: Tree+renderXYZCoordinates =+ withFillOpacity 0 $+ mkLinePath $+ [ (x, 1-y)+ | (ang, (x,y,_z)) <- Map.toList lightXYZCoordinates+ ]++lightLABCoordinates :: Map Nanometer (Double, Double, Double)+lightLABCoordinates = Map.map fn lightXYZCoordinates+ where+ fn (x,y,z) = cieLABView d65 (cieXYZ x y z)++renderLABCoordinates :: Tree+renderLABCoordinates =+ withFillOpacity 0 $+ mkLinePath $+ [ (a/350, (1-b)/150)+ | (ang, (_l,a,b)) <- Map.toList lightLABCoordinates+ ]++-- (Long, Medium, Short)+coneSensitivity :: Map Nanometer (Double, Double, Double)+coneSensitivity = unsafePerformIO $ do+ dat <- BS.readFile =<< getDataFileName "data/cone_sensitivity_lms.csv"+ case decode NoHeader dat of+ Left err -> error err+ Right vec -> return $ Map.fromList+ [ (nm, (l,m,fromMaybe 0 s)) | (nm,l,m,s) <- V.toList vec ]++renderSensitivity :: Tree+renderSensitivity = mkGroup+ [ withStrokeColor "blue" $ draw (\(l,m,s) -> s)+ , withStrokeColor "green" $ draw (\(l,m,s) -> m)+ , withStrokeColor "red" $ draw (\(l,m,s) -> l)+ ]+ where+ draw fn = withFillOpacity 0 $ mkLinePath+ [ (fromIntegral (ang-minAng) / maxAng * width, fn val * height)+ | (ang, val) <- Map.toList coneSensitivity+ ]+ width = 100+ height = -50+ maxAng = fromIntegral $ fst (Map.findMax coneSensitivity) - minAng+ minAng = fst (Map.findMin coneSensitivity)
src/Reanimate/Driver.hs view
@@ -1,25 +1,35 @@+{-# LANGUAGE ScopedTypeVariables #-} module Reanimate.Driver ( reanimate ) where -import Control.Concurrent (MVar, forkIO, killThread, modifyMVar_,- newEmptyMVar, putMVar)-import Control.Exception (finally)-import Control.Monad.Fix (fix)-import qualified Data.Text as T+import Control.Concurrent (MVar, forkIO, forkOS, killThread,+ modifyMVar_, newEmptyMVar,+ putMVar, takeMVar)+import Control.Exception (SomeException, finally, handle)+import Control.Monad+import Control.Monad.Fix (fix)+import Data.Maybe+import qualified Data.Text as T+import qualified Data.Text.Read as T+import Data.Version+import GHC.Environment (getFullArgs) import Network.WebSockets-import System.Directory (findFile, listDirectory)-import System.Environment (getArgs, getProgName)+import Paths_reanimate+import Reanimate.Misc (runCmdLazy, runCmd_)+import Reanimate.Monad (Animation)+import Reanimate.Render (render, renderSnippets,+ renderSvgs)+import System.Directory (doesFileExist, findExecutable,+ findFile, listDirectory, withCurrentDirectory)+import System.Environment (getArgs, getProgName)+import System.Exit import System.FilePath import System.FSNotify-import System.IO (BufferMode (..), hPutStrLn, hSetBuffering,- stderr, stdin)--import Data.Maybe-import Paths_reanimate-import Reanimate.Misc (runCmdLazy, runCmd_, withTempDir,- withTempFile)-import Reanimate.Monad (Animation)-import Reanimate.Render (renderSvgs, render)-import Web.Browser (openBrowser)+import System.IO+import System.IO.Temp+import Text.ParserCombinators.ReadP+import qualified Text.PrettyPrint.ANSI.Leijen as Doc+import Text.Printf+import Web.Browser (openBrowser) opts = defaultConnectionOptions { connectionCompressionOptions = PermessageDeflateCompression defaultPermessageDeflate }@@ -31,60 +41,75 @@ hSetBuffering stdin NoBuffering case args of ["once"] -> renderSvgs animation+ ["snippets"] -> renderSnippets animation+ ["check"] -> checkEnvironment ["render", target] -> render animation target- _ -> withTempDir $ \tmpDir -> do+ _ -> do+ self <- findOwnSource url <- getDataFileName "viewer/build/index.html" putStrLn "Opening browser..." bSucc <- openBrowser url if bSucc then putStrLn "Browser opened." else hPutStrLn stderr $ "Failed to open browser. Manually visit: " ++ url+ putStrLn "Listening..." 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 -> do- conn <- acceptRequest pending- slave <- newEmptyMVar- let handler = modifyMVar_ slave $ \tid -> 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)- return tid- putStrLn "Found self. Listening..."- stop <- watchFile watch self handler- putMVar slave =<< forkIO (return ())- let loop = do- fps <- receiveData conn :: IO T.Text- handler- loop- loop `finally` stop+ putStrLn "New connection received."+ conn <- acceptRequest pending+ slave <- newEmptyMVar+ let handler = modifyMVar_ slave $ \tid -> do + putStrLn "Reloading code..."+ killThread tid+ tid <- forkOS $ slaveHandler conn self+ return tid+ killSlave = do+ tid <- takeMVar slave+ killThread tid+ stop <- watchFile watch self handler+ putMVar slave =<< forkIO (return ())+ let loop = do+ fps <- receiveData conn :: IO T.Text+ handler+ loop+ loop `finally` (stop >> killSlave)++slaveHandler conn self =+ withCurrentDirectory (takeDirectory self) $+ withSystemTempDirectory "reanimate" $ \tmpDir ->+ withTempFile tmpDir "reanimate.exe" $ \tmpExecutable handle -> do+ hClose handle+ sendTextData conn (T.pack "Compiling")+ ret <- runCmd_ "stack" $ ["ghc", "--"] ++ ghcOptions tmpDir ++ [takeFileName 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 $ 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 check event = takeFileName (eventPath event) == takeFileName file@@ -93,3 +118,157 @@ ghcOptions tmpDir = ["-rtsopts", "--make", "-threaded", "-O2"] ++ ["-odir", tmpDir, "-hidir", tmpDir]++-- FIXME: Gracefully disable code reloading if source is missing.+findOwnSource :: IO FilePath+findOwnSource = do+ fullArgs <- getFullArgs+ let stackSource = last fullArgs+ exist <- doesFileExist stackSource+ if exist+ then return stackSource+ else do+ prog <- getProgName+ lst <- listDirectory "."+ mbSelf <- findFile ("." : lst) prog+ case mbSelf of+ Nothing -> do+ hPutStrLn stderr "Failed to find own source code."+ exitFailure+ Just self -> pure self+++--------------------------------------------------------------------------+-- Check environment++checkEnvironment :: IO ()+checkEnvironment = do+ putStrLn "reanimate checks:"+ runCheck "Has ffmpeg" hasFFmpeg+ runCheck "Has LaTeX" hasLaTeX+ runCheck "Has XeLaTeX" hasXeLaTeX+ runCheck "Has dvisvgm" hasDvisvgm+ runCheck "Has povray" hasPovray+ forM_ latexPackages $ \pkg ->+ runCheck ("Has LaTeX package '"++ pkg ++ "'") $ hasTeXPackage "latex" $+ "{"++pkg++"}"+ forM_ xelatexPackages $ \pkg ->+ runCheck ("Has XeLaTeX package '"++ pkg ++ "'") $ hasTeXPackage "xelatex" $+ "{"++pkg++"}"+ where+ latexPackages =+ ["babel"+ ,"amsmath"+ ,"amssymb"+ ,"dsfont"+ ,"setspace"+ ,"relsize"+ ,"textcomp"+ ,"mathrsfs"+ ,"calligra"+ ,"wasysym"+ ,"ragged2e"+ ,"physics"+ ,"xcolor"+ ,"textcomp"+ ,"xfrac"+ ,"microtype"]+ xelatexPackages =+ ["ctex"]+ runCheck msg fn = do+ printf " %-35s" (msg ++ ":")+ val <- fn+ case val of+ Left err -> print $ Doc.red $ Doc.text err+ Right ok -> print $ Doc.green $ Doc.text ok++-- latex, dvisvgm, xelatex++hasLaTeX :: IO (Either String String)+hasLaTeX = hasProgram "latex"++hasXeLaTeX :: IO (Either String String)+hasXeLaTeX = hasProgram "xelatex"++hasDvisvgm :: IO (Either String String)+hasDvisvgm = hasProgram "dvisvgm"++hasPovray :: IO (Either String String)+hasPovray = hasProgram "povray"++hasFFmpeg :: IO (Either String String)+hasFFmpeg = do+ mbVersion <- ffmpegVersion+ return $ case mbVersion of+ Nothing -> Left "no"+ Just vs | vs < minVersion -> Left "too old"+ | otherwise -> Right (showVersion vs)+ where+ minVersion = Version [4,1,3] []+ffmpegVersion :: IO (Maybe Version)+ffmpegVersion = do+ mbPath <- findExecutable "ffmpeg"+ case mbPath of+ Nothing -> return Nothing+ Just path -> do+ ret <- runCmd_ path ["-version"]+ case ret of+ Left{} -> return Nothing+ Right out ->+ case map (take 3 . words) $ take 1 $ lines out of+ [["ffmpeg", "version", vs]] ->+ return $ parseVS vs+ _ -> return Nothing+ where+ parseVS vs = listToMaybe+ [ v | (v, "") <- readP_to_S parseVersion vs ]+++hasTeXPackage :: FilePath -> String -> IO (Either String String)+hasTeXPackage exec pkg = handle (\(e::SomeException) -> return $ Left "n/a") $+ withSystemTempDirectory "reanimate" $ \tmp_dir -> withTempFile tmp_dir "test.tex" $ \tex_file tex_handle -> do+ hPutStr tex_handle tex_document+ hPutStr tex_handle $ "\\usepackage" ++ pkg ++ "\n"+ hPutStr tex_handle "\\begin{document}\n"+ hPutStr tex_handle "blah\n"+ hPutStr tex_handle tex_epilogue+ hClose tex_handle+ ret <- runCmd_ exec ["-interaction=batchmode", "-halt-on-error", "-output-directory="++tmp_dir, tex_file]+ return $ case ret of+ Right{} -> Right "OK"+ Left{} -> Left "missing"+ where+ tex_document = "\\documentclass[preview]{standalone}\n"+ tex_xelatex =+ "\\usepackage[UTF8]{ctex}\n"++ tex_prologue =+ "\\usepackage[english]{babel}\n\+ \\\usepackage{amsmath}\n\+ \\\usepackage{amssymb}\n\+ \\\usepackage{dsfont}\n\+ \\\usepackage{setspace}\n\+ \\\usepackage{relsize}\n\+ \\\usepackage{textcomp}\n\+ \\\usepackage{mathrsfs}\n\+ \\\usepackage{calligra}\n\+ \\\usepackage{wasysym}\n\+ \\\usepackage{ragged2e}\n\+ \\\usepackage{physics}\n\+ \\\usepackage{xcolor}\n\+ \\\usepackage{textcomp}\n\+ \\\usepackage{xfrac}\n\+ \\\usepackage{microtype}\n\+ \\\linespread{1}\n\+ \\\begin{document}\n"++ tex_epilogue =+ "\n\+ \\\end{document}"++hasProgram :: String -> IO (Either String String)+hasProgram exec = do+ mbPath <- findExecutable exec+ return $ case mbPath of+ Nothing -> Left $ "'" ++ exec ++ "'' not found"+ Just path -> Right path
src/Reanimate/Examples.hs view
@@ -22,6 +22,7 @@ import Reanimate.LaTeX import Reanimate.Monad import Reanimate.Svg+import Reanimate.Signal import qualified Data.Colour.Palette.BrewerSet as D import qualified Diagrams.Backend.SVG as D@@ -490,25 +491,9 @@ -} -latex_draw :: Animation-latex_draw =- 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- morph_wave :: Animation morph_wave = autoReverse $ mkAnimation 2.5 $ do- morph <- signal 0 1+ morph <- getSignal signalLinear emit $ mkBackground "black" emit $ withStrokeColor "white" $ translate (-320/2) (-180/2) $ mkGroup [ translate 30 50 $ mkLinePath wave1@@ -523,7 +508,7 @@ morph_wave_circle :: Animation morph_wave_circle = autoReverse $ mkAnimation 2.5 $ do- idx <- signal 0 1+ idx <- getSignal signalLinear emit $ mkBackground "black" emit $ withStrokeColor "white" $ translate (-320/2) (-180/2) $ mkGroup [ translate 30 90 $ mkLinePath $ morphPath circle wave1 idx@@ -551,110 +536,12 @@ progressMeter :: Animation progressMeter = mkAnimation 3 $ do- h <- signal 0 100+ h <- getSignal $ signalFromTo 0 100 signalLinear emit $ center $ mkGroup [ withStrokeColor "white" $ withStrokeWidth (Num 2) $ withFillOpacity 0 $- mkRect (Num 0, Num 0) (Num 30) (Num 100)+ mkRect (Num 30) (Num 100) , withFillColor "white" $- mkRect (Num 0, Num 0) (Num 30) (Num h) ]---bbox :: Animation-bbox = 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.0\- \a20.0 20.0 90.0 0 1 0.0,40.0\- \a20.0 20.0 90.0 0 1 -40.0,0.0 Z"--latex_color :: Animation-latex_color = 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"--latex_basic :: Animation-latex_basic = 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}"--valentine :: Animation-valentine =- all_red `before`- ( background `sim`- (backgroundDelay `before`- foldr1 sim [ pause p `before` fallingLove x | (p, x) <- falling ]- ) `sim`- (heart_ani `before` heart_disappear) `sim`- (pause 5 `before` 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.19), (5.9, 0.53), (3.2, 0.14), (7.7, 0.99) ]- 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- mapF (scale n) drawHeart- fallingLove xPos = mkAnimation 2 $ do- n <- signal (-100) 100- o <- oscillate $ signal (-1) 1- emit $ scale 2 $ withFillColor "red" $- translate ((xPos*2-1)*60) n $ rotate (45*o) ai- 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+ mkRect (Num 30) (Num h) ] diaSize :: Animation@@ -670,7 +557,7 @@ wavyTree :: Animation wavyTree = mkAnimation 1 $ do- s <- oscillate $ signal 1 2+ s <- oscillate $ getSignal $ signalFromTo 1 2 signalLinear emit $ mkBackground "white" emit $ translate (-320/2) (-180/2) (dSvg s) where@@ -683,125 +570,3 @@ tree s = D.lSystem gens (s/16 @@ turn) (D.symbols "F") rules rules = M.fromList [D.rule 'F' "FF-[->F+F+>F]+[+>F->F->F]"]--tangentAndNormal :: Animation-tangentAndNormal = mkAnimation 5 $ do- s <- oscillate $ signalSCurve 2 0 1- emit $ mkBackground "white"- emit $ translate (-320/2) (-180/2) $ renderDiagram $- withEnvelope (D.rect 320 180 :: SvgDiagram) $- D.scale 50 $ D.translate (V2 (-2) (-0.75)) $ dia s- where- dia param =- frame 0.5 $- strokeLocTrail spline- <> mconcat- [ tangentLine- , baselineText "tangent" # D.translate tangentVector- , normalLine- , topLeftText "normal" # D.translate (-normalVector)- , rightAngleSquare- ] # moveTo pt # D.fontSize large- where- pts = map p2 [(0,0), (1,1), (2,1), (3,0), (3.5,0)]-- spline :: Located (Trail V2 Double)- spline = cubicSpline False pts-- pt = atParam spline param- tangentVector :: V2 Double- tangentVector = D.normalize $ tangentAtParam spline param- normalVector = D.normalize $ normalAtParam spline param-- symmetricLine :: V2 Double -> SvgDiagram- symmetricLine v = fromOffsets [2 *^ v] # D.center- tangentLine :: SvgDiagram- tangentLine = symmetricLine tangentVector- normalLine = symmetricLine normalVector-- rightAngleSquare :: SvgDiagram- rightAngleSquare = square 0.1 # alignBL # D.rotate (signedAngleBetween tangentVector unitX)---drawSunflower :: Animation-drawSunflower = mkAnimation 10 $ do- n <- signal 1 500- rot <- signal 0 45- emit $ mkBackground "black"- emit $ rotate rot $ translate (-320/2) (-180/2)- (dSvg $ round n)- where- cached = [ dSvg n | n <- [0..]]- dSvg n = renderDiagram $ withEnvelope (D.rect 320 180 :: SvgDiagram) $- D.scale 5 $ sunflower n-- mkCoords :: [P2 Double]- mkCoords =[coord (fromIntegral i) | i <- [1..]]- where- coord m = p2 $ fromPolar (sqrt m) (2.4 * m)- fromPolar r theta = (r * cos theta, r * sin theta)-- floret :: Double -> SvgDiagram- floret r = D.circle 0.6 # lw none # fc (colors !! n)- where- n = floor (1.4 * sqrt r) `mod` 10- colors = black : (reverse $ D.brewerSet D.YlOrBr 9)-- sunflower :: Int -> SvgDiagram- sunflower n = frame 4 $ position $ take n $ zip mkCoords florets- where- florets = [ floret (sqrt (fromIntegral i)) | i <- [1 ..]]--mkFilter :: String -> [FilterElement] -> Filter-mkFilter ident fe = defaultSvg & filterChildren .~ fe & attrId .~ Just ident--gooEffect :: Animation-gooEffect = mkAnimation 5 $ do- s <- oscillate $ signal 0 3- emit $ mkBackground "black"- emit $ FilterTree $ mkFilter "blur"- [FEGaussianBlur $ defaultSvg- & gaussianBlurStdDeviationX .~ Num dev- & filterResult .~ Just "blur"- ] & filterWidth .~ pure (Percent 3)- & filterX .~ pure (Percent (-1))- & filterHeight .~ pure (Percent 3)- & filterY .~ pure (Percent (-1))- emit $ FilterTree $ mkFilter "goo"- [FEGaussianBlur $ defaultSvg- & gaussianBlurStdDeviationX .~ Num dev- & filterResult .~ Just "blur"- ,FEColorMatrix $ defaultSvg- & colorMatrixType .~ Matrix- & colorMatrixValues .~ "1 0 0 0 0 \- \0 1 0 0 0 \- \0 0 1 0 0 \- \0 0 0 " ++ show (sharpness*2) ++ " -" ++ show sharpness- & filterResult .~ pure "goo"- ,FEComposite $ defaultSvg- & compositeIn .~ pure SourceGraphic- & compositeIn2 .~ pure (SourceRef "goo")- & compositeOperator .~ CompositeAtop- ] & filterWidth .~ pure (Percent 3)- & filterX .~ pure (Percent (-1))- & filterHeight .~ pure (Percent 3)- & filterY .~ pure (Percent (-1))- emit $ translate 0 (-radius*2) $ withFillColor "red" $ mkGroup- [ translate (s*(-radius)) 0 circ- , translate (s*radius) 0 circ- ]- emit $ withFillColor "red" $ mkGroup- [ translate (s*(-radius)) 0 circ- , translate (s*radius) 0 circ- ]- & filterRef .~ pure (Ref "blur")- emit $ translate 0 (radius*2) $ withFillColor "red" $ set filterRef (pure $ Ref "goo")- $ mkGroup [ translate (s*(-radius)) 0 circ- , translate (s*radius) 0 circ ]- where- sharpness = 60- dev = 10- radius = 30- circ = CircleTree $ defaultSvg- & circleCenter .~ (Num 0, Num 0)- & circleRadius .~ Num radius
+ src/Reanimate/Interpolate.hs view
@@ -0,0 +1,73 @@+{-# LANGUAGE RecordWildCards #-} +module Reanimate.Interpolate where + +import Codec.Picture +import Data.Colour +import Data.Colour.CIE +import Data.Colour.CIE.Illuminant +import Data.Colour.SRGB +import Data.Colour.RGBSpace.HSV +import Data.Colour.RGBSpace +import Data.Fixed + +data ColorComponents = ColorComponents + { colorUnpack :: Colour Double -> (Double, Double, Double) + , colorPack :: Double -> Double -> Double -> Colour Double } + +rgbComponents :: ColorComponents +rgbComponents = ColorComponents rgbUnpack sRGB + where + rgbUnpack :: Colour Double -> (Double, Double, Double) + rgbUnpack c = + case toSRGB c of + RGB r g b -> (r,g,b) + +hsvComponents :: ColorComponents +hsvComponents = ColorComponents unpack pack + where + unpack = hsvView.toSRGB + pack a b c = uncurryRGB sRGB $ hsv a b c + +labComponents :: ColorComponents +labComponents = ColorComponents unpack pack + where + unpack = cieLABView d65 + pack = cieLAB d65 + +xyzComponents :: ColorComponents +xyzComponents = ColorComponents cieXYZView cieXYZ + +lchComponents :: ColorComponents +lchComponents = ColorComponents unpack pack + where + toDeg,toRad :: Double -> Double + toRad deg = deg/180 * pi + toDeg rad = rad/pi * 180 + unpack :: Colour Double -> (Double, Double, Double) + unpack color = + let (l,a,b) = cieLABView d65 color + c = sqrt (a*a + b*b) + h :: Double + h = (toDeg(atan2 b a) + 360) `mod'` 360 + isZero = round (c*10000) == 0 + in (l, c, if isZero then 0/0 else h) + pack l c h = + cieLAB d65 l (cos (toRad h) * c) (sin (toRad h) * c) + +interpolate :: ColorComponents -> Colour Double -> Colour Double -> (Double -> Colour Double) +interpolate ColorComponents{..} from to = \d -> + colorPack (a1 + (a2-a1)*d) (b1 + (b2-b1)*d) (c1 + (c2-c1)*d) + where + (a1,b1,c1) = colorUnpack from + (a2,b2,c2) = colorUnpack to + +interpolateRGB8 :: ColorComponents -> PixelRGB8 -> PixelRGB8 -> (Double -> PixelRGB8) +interpolateRGB8 comps from to = toRGB8 . interpolate comps (fromRGB8 from) (fromRGB8 to) + +toRGB8 :: Colour Double -> PixelRGB8 +toRGB8 c = PixelRGB8 r g b + where + RGB r g b = toSRGBBounded c + +fromRGB8 :: PixelRGB8 -> Colour Double +fromRGB8 (PixelRGB8 r g b) = sRGB24 r g b
src/Reanimate/LaTeX.hs view
@@ -29,7 +29,7 @@ ("% plain latex\n" <> tex) xelatex :: Text -> Tree-xelatex tex = (unsafePerformIO . (cacheMem . cacheDiskSvg) latexToSVG)+xelatex tex = (unsafePerformIO . (cacheMem . cacheDiskSvg) xelatexToSVG) ("% xelatex\n" <> tex) latexAlign :: Text -> Tree
+ src/Reanimate/Memo.hs view
@@ -0,0 +1,73 @@+{-# LANGUAGE RankNTypes, ExistentialQuantification, BangPatterns #-} +module Reanimate.Memo + ( Key(..) + , memo + ) where + +import Control.Monad.ST +import Data.STRef +import System.Mem.StableName +import Debug.Trace +import System.IO.Unsafe +import Control.Monad.ST.Unsafe +import Data.IORef +import qualified Data.Map as Map +import Data.Dynamic +import Data.Typeable +import Data.Maybe + +data DynamicName = forall a. DynamicName !(StableName a) | forall a. (Eq a, Ord a, Typeable a) => DynamicKey a +instance Eq DynamicName where + DynamicName a == DynamicName b = eqStableName a b + DynamicKey a == DynamicKey b = + case cast a of + Nothing -> False + Just a' -> a'==b + _ == _ = False + +instance Ord DynamicName where + DynamicName a `compare` DynamicName b = + hashStableName a `compare` hashStableName b + DynamicName{} `compare` _ = LT + DynamicKey a `compare` DynamicKey b = + case cast a of + Nothing -> typeOf a `compare` typeOf b + Just a' -> a' `compare` b + _ `compare` _ = GT + +data CacheMap = CacheMap !(Map.Map DynamicName CacheMap) !(Map.Map DynamicName Dynamic) + +emptyCacheMap :: CacheMap +emptyCacheMap = CacheMap Map.empty Map.empty + +cacheMapLookup :: [DynamicName] -> CacheMap -> Maybe Dynamic +cacheMapLookup [] _ = Nothing +cacheMapLookup [k] (CacheMap _ vals) = Map.lookup k vals +cacheMapLookup (k:ks) (CacheMap sub _) = + cacheMapLookup ks =<< Map.lookup k sub + +cacheMapInsert :: [DynamicName] -> Dynamic -> CacheMap -> CacheMap +cacheMapInsert [] _ m = m +cacheMapInsert [k] v (CacheMap sub vals) = CacheMap sub (Map.insert k v vals) +cacheMapInsert (k:ks) v (CacheMap sub vals) = + CacheMap (Map.alter fn k sub) vals + where + fn = Just . cacheMapInsert ks v . fromMaybe emptyCacheMap + +{-# NOINLINE cacheMap #-} +cacheMap :: IORef CacheMap +cacheMap = unsafePerformIO (newIORef emptyCacheMap) + +data Key = forall a. Key !a | forall a. (Typeable a, Eq a, Ord a) => KeyPrim !a + +fromKey :: Key -> IO DynamicName +fromKey (Key val) = DynamicName <$> makeStableName val +fromKey (KeyPrim val) = pure (DynamicKey val) + +memo :: Typeable a => [Key] -> a -> a +memo !k v = unsafePerformIO $ do + keys <- mapM fromKey k + atomicModifyIORef' cacheMap $ \m -> + case fromDynamic =<< cacheMapLookup keys m of + Just v' -> (m, v') + Nothing -> (cacheMapInsert keys (toDyn v) m, v)
src/Reanimate/Misc.hs view
@@ -69,7 +69,7 @@ withTempDir :: (FilePath -> IO a) -> IO a withTempDir action = do dir <- getTemporaryDirectory- (path, handle) <- openTempFile dir "reanimate-XXXXXX"+ (path, handle) <- openTempFile dir "reanimate" hClose handle removeFile path createDirectory (dir </> path)@@ -78,6 +78,6 @@ withTempFile :: String -> (FilePath -> IO a) -> IO a withTempFile ext action = do dir <- getTemporaryDirectory- (path, handle) <- openTempFile dir ("reanimate-XXXXXX" <.> ext)+ (path, handle) <- openTempFile dir ("reanimate" <.> ext) hClose handle action path -- `finally` removeFile path
src/Reanimate/Monad.hs view
@@ -14,6 +14,7 @@ Tree, Tree (..), xmlOfDocument, xmlOfTree) import Graphics.SvgTree.Printer import Reanimate.Svg+import Reanimate.Signal import Text.XML.Light (elContent) import Text.XML.Light.Output @@ -86,17 +87,8 @@ andThen :: Animation -> Animation -> Animation andThen a b = a `sim` (pause (duration a) `before` b) -signal :: Double -> Double -> Frame Double-signal from to = Frame $ \d t -> pure $- from + (to-from)*(t/d)--signalSCurve :: Double -> Double -> Double -> Frame Double-signalSCurve steepness from to = do- s <- signal 0 1- let s' = if s < 0.5- then 0.5 * (2*s)**steepness- else 1-0.5 * (2 - 2*s)**steepness- pure $ from + (to-from)*s'+getSignal :: Signal -> Frame Double+getSignal s = Frame $ \d t -> pure $ s (t/d) frameAt :: Double -> Animation -> Tree frameAt t (Animation d (Frame f)) = mkGroup $ execState (f d (min d t)) id []@@ -131,9 +123,23 @@ pauseAtEnd :: Double -> Animation -> Animation pauseAtEnd p a = a `andThen` pause p +pauseAtBeginning :: Double -> Animation -> Animation+pauseAtBeginning d1 a =+ Animation d1 (freezeFrame 0 a) `before` a++pauseAround :: Double -> Double -> Animation -> Animation+pauseAround start end = pauseAtEnd end . pauseAtBeginning start++freezeFrame :: Double -> Animation -> Frame ()+freezeFrame t (Animation d f) = Frame $ \_ _ -> unFrame f d t+ adjustSpeed :: Double -> Animation -> Animation adjustSpeed factor (Animation d fn) = Animation (d/factor) $ Frame $ \_dur t -> unFrame fn d (t*factor)++setDuration :: Double -> Animation -> Animation+setDuration newD (Animation d fn) =+ Animation newD $ Frame $ \dur t -> unFrame fn dur t reverseAnimation :: Animation -> Animation reverseAnimation (Animation d fn) = Animation d $ Frame $ \_dur t ->
+ src/Reanimate/Povray.hs view
@@ -0,0 +1,67 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+module Reanimate.Povray+ ( povray+ , povrayQuick+ , povraySlow+ ) where++import Codec.Picture.Png+import Control.Exception (SomeException, handle)+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as BL+import Data.IORef+import Data.Map (Map)+import qualified Data.Map as Map+import Data.Monoid+import Reanimate.Cache+import Reanimate.Cache+import Reanimate.Memo+import Reanimate.Misc+import Reanimate.Raster+import Reanimate.Svg+import System.IO++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)++povrayRaw :: [String] -> Text -> Tree+povrayRaw args script =+ memo [Key mkPovrayImage, KeyPrim args, KeyPrim script]+ (unsafePerformIO $ mkPovrayImage args script)++povray :: [String] -> Text -> Tree+povray args = povrayRaw (["+H180","+W320", "+A"] ++ args)++povrayQuick :: [String] -> Text -> Tree+povrayQuick args = povrayRaw (["+H180","+W320"] ++ args)++povraySlow :: [String] -> Text -> Tree+povraySlow args = povrayRaw (["+H1440","+W2560", "+A"] ++ args)++mkPovrayImage :: [String] -> Text -> IO Tree+mkPovrayImage args script = cacheDiskKey key $ do+ povray <- requireExecutable "povray"+ withTempFile "pov" $ \pov_file -> do+ let out = replaceExtension pov_file "png"+ T.writeFile pov_file script+ ret <- runCmd_ povray (args ++ ["-D","+UA", pov_file, "+o"++out])+ case ret of+ Left{} -> error "povray went wrong"+ Right{} -> do+ png <- B.readFile out+ case decodePng png of+ Left{} -> error "bad image"+ Right img -> return $ center $ scaleToSize 320 180 $ embedDynamicImage img+ where+ key = T.concat (script:map T.pack args)
+ src/Reanimate/Raster.hs view
@@ -0,0 +1,37 @@+module Reanimate.Raster+ ( embedImage+ , embedDynamicImage+ ) where++import Control.Lens+import Codec.Picture+import Codec.Picture.Png+import Codec.Picture.Types+import qualified Data.ByteString.Base64.Lazy as Base64+import qualified Data.ByteString.Lazy.Char8 as LBS+import Graphics.SvgTree (Tree(..), defaultSvg)+import qualified Graphics.SvgTree as Svg++-- XXX: Use Px instead of Num for width and height?+{-# INLINE embedImage #-}+embedImage :: PngSavable a => Image a -> Tree+embedImage img =+ ImageTree $ defaultSvg+ & Svg.imageWidth .~ Svg.Num (fromIntegral $ imageWidth img)+ & Svg.imageHeight .~ Svg.Num (fromIntegral $ imageHeight img)+ & Svg.imageHref .~ ("data:image/png;base64," ++ imgData)+ where+ imgData = LBS.unpack $ Base64.encode (encodePng img)++{-# INLINE embedDynamicImage #-}+embedDynamicImage :: DynamicImage -> Tree+embedDynamicImage img =+ ImageTree $ defaultSvg+ & Svg.imageWidth .~ Svg.Num (fromIntegral $ dynamicMap imageWidth img)+ & Svg.imageHeight .~ Svg.Num (fromIntegral $ dynamicMap imageHeight img)+ & Svg.imageHref .~ ("data:image/png;base64," ++ imgData)+ where+ imgData =+ case encodeDynamicPng img of+ Left err -> error err+ Right dat -> LBS.unpack $ Base64.encode dat
src/Reanimate/Render.hs view
@@ -1,16 +1,19 @@ module Reanimate.Render ( render , renderSvgs+ , renderSnippets ) where import Control.Monad (forM_) import Control.Parallel.Strategies+import Control.Concurrent.QSemN+import Control.Concurrent+import Control.Exception import qualified Data.ByteString.Lazy.Char8 as BS import qualified Data.Text as T import qualified Data.Text.IO as T import Graphics.SvgTree (Number (..)) import Reanimate.Diagrams-import Reanimate.Examples import Reanimate.Misc import Reanimate.Monad import System.Directory (renameFile)@@ -21,17 +24,48 @@ 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 ()++ 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" +-- XXX: Merge with 'renderSvgs'+renderSnippets :: Animation -> IO ()+renderSnippets ani = do+ print frameCount+ forM_ [0..frameCount-1] $ \nth -> do+ let now = (duration ani / (fromIntegral frameCount-1)) * fromIntegral nth+ frame = frameAt (if frameCount<=1 then 0 else now) ani+ svg = renderSvg Nothing Nothing frame+ putStr (show nth)+ T.putStrLn $ T.concat . T.lines . T.pack $ svg+ where+ frameCount = 50 +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 formatFPS :: Format -> Int@@ -84,17 +118,18 @@ --------------------------------------------------------------------------------- -- Helpers--- XXX: Move to a different module and unify with helpers from LaTeX. --- XXX: Use threads generateFrames ani width_ rate action = withTempDir $ \tmp -> do+ done <- newMVar 0 let frameName nth = tmp </> printf nameTemplate nth rendered = [ renderSvg width height $ nthFrame n | n <- frames] `using` parBuffer 16 rdeepseq- forM_ (zip [0::Int ..] rendered) $ \(n, frame) -> do- writeFile (frameName n) frame- putStr $ "\r" ++ show (n+1) ++ "/" ++ show frameCount- hFlush stdout+ concurrentForM_ frames $ \n -> do+ writeFile (frameName n) $ renderSvg width height $ nthFrame n+ modifyMVar_ done $ \nDone -> do+ putStr $ "\r" ++ show (nDone+1) ++ "/" ++ show frameCount+ hFlush stdout+ return (nDone+1) putStrLn "\n" action (tmp </> nameTemplate) where@@ -105,3 +140,12 @@ frameCount = round (duration ani * fromIntegral rate) :: Int nameTemplate :: String nameTemplate = "render-%05d.svg"++concurrentForM_ :: [a] -> (a -> IO ()) -> IO ()+concurrentForM_ lst action = do+ n <- getNumCapabilities+ sem <- newQSemN n+ forM_ lst $ \elt -> do+ waitQSemN sem 1+ forkIO (action elt `finally` signalQSemN sem 1)+ waitQSemN sem n
+ src/Reanimate/Scene.hs view
@@ -0,0 +1,134 @@+{-# LANGUAGE RankNTypes #-}+module Reanimate.Scene where++import Control.Monad.ST+import Control.Monad.ST.Unsafe+import Data.IORef+import Data.Map (Map)+import qualified Data.Map as Map+import Data.STRef+import Debug.Trace+import Data.Ord+import Data.List+import System.IO.Unsafe+import System.Mem.StableName++import Reanimate.Monad++data World+type ZIndex = Int++-- (seq duration, par duration)+-- [(Time, Animation, ZIndex)]+-- Map Time [(Animation, ZIndex)]+type Timeline = [(Time, Animation, ZIndex)]+newtype Scene s a = M { unM :: Time -> ST s (a, Duration, Duration, Timeline) }++unionTimeline :: Timeline -> Timeline -> Timeline+unionTimeline = (++)++emptyTimeline :: Timeline+emptyTimeline = []++instance Functor (Scene s) where+ fmap f action = M $ \t -> do+ (a, d1, d2, tl) <- unM action t+ return (f a, d1, d2, tl)++instance Applicative (Scene s) where+ pure a = M $ \_ -> return (a, 0, 0, emptyTimeline)+ f <*> g = M $ \t -> do+ (f', s1, p1, tl1) <- unM f t+ (g', s2, p2, tl2) <- unM g (t+s1)+ return (f' g', s1+s2, max p1 (s1+p2), unionTimeline tl1 tl2)++instance Monad (Scene s) where+ return = pure+ f >>= g = M $ \t -> do+ (a, s1, p1, tl1) <- unM f t+ (b, s2, p2, tl2) <- unM (g a) (t+s1)+ return (b, s1+s2, max p1 (s1+p2), unionTimeline tl1 tl2)++--data Frame a = Frame {unFrame :: Duration -> Time -> State ([Tree] -> [Tree]) a}+sceneAnimation :: (forall s. Scene s a) -> Animation+sceneAnimation action = Animation (max s p) $ Frame $ \d t ->+ sequence_ $ map snd $ sortBy (comparing fst)+ [ (z, unFrame frameGen dur (t-startT))+ | (startT, Animation dur frameGen, z) <- tl+ , startT < t+ , startT+dur > t+ ]+ where+ (_, s, p, tl) = runST (unM action 0)++debug :: String -> Scene s ()+debug msg = M $ \t -> trace msg (return ((), 0, 0, emptyTimeline))++someaction :: Scene s ()+someaction = debug "someaction"++fork :: Scene s a -> Scene s a+fork (M action) = M $ \t -> do+ (a, s, p, tl) <- action t+ return (a, 0, max s p, tl)++play :: Animation -> Scene s ()+play = playZ 0++playZ :: ZIndex -> Animation -> Scene s ()+playZ z ani = M $ \t -> do+ let d = duration ani+ return ((), d, 0, [(t, ani, z)])++queryNow :: Scene s Time+queryNow = M $ \t -> return (t, 0, 0, emptyTimeline)++-- Wait until all forked and sequential animations have finished.+waitAll :: Scene s a -> Scene s a+waitAll (M action) = M $ \t -> do+ (a, s, p, tl) <- action t+ return (a, max s p, 0, tl)++waitUntil :: Time -> Scene s ()+waitUntil tNew = do+ now <- queryNow+ wait (max 0 (tNew - now))++wait :: Duration -> Scene s ()+wait d = M $ \t ->+ return ((), d, 0, emptyTimeline)++adjustZ :: (ZIndex -> ZIndex) -> Scene s a -> Scene s a+adjustZ fn (M action) = M $ \t -> do+ (a, s, p, tl) <- action t+ return (a, s, p, [ (startT, ani, fn z) | (startT, ani, z) <- tl ])++withSceneDuration :: Scene s a -> Scene s Duration+withSceneDuration s = do+ t1 <- queryNow+ s+ t2 <- queryNow+ return (t2-t1)++{-+blackness+show numbers+fade in colormap+slide to middle++do fork $ play $ setDuration 5 showNumbers+ # fadeOut 1 # fadeIn 1+ wait 3++ fork $ do+ playZ (-1) $ setDuration 5 $ revealImage 0 0.5+ # pauseAround 1 1+ playZ (-1) $ setDuration 5 $ revealImage 0.5 1+ # pauseAtEnd 1++ play $ setDuration 5 $ colormap 0 0.5+ # fadeIn 1 # pauseAround 1 1+ play $ setDuration 5 $ colormap 0.5 1+ # fadeOut 1 # pauseAtEnd 1++-}
+ src/Reanimate/Signal.hs view
@@ -0,0 +1,35 @@+module Reanimate.Signal where++type Signal = Double -> Double++signalFromList :: [(Double, Signal)] -> Signal+signalFromList fns t = worker 0 fns+ where+ worker _ [] = 0+ worker now [(len, fn)] = fn (min 1 ((t-now) / min (1-now) len))+ worker now ((len, fn):rest)+ | now+len < t = worker (now+len) rest+ | otherwise = fn ((t-now) / len)++signalFlat :: Double -> Signal+signalFlat x = const x++signalLinear :: Signal+signalLinear = id++signalFromTo :: Double -> Double -> Signal -> Signal+signalFromTo from to c t = from + (to-from)*(c t)++signalReverse :: Signal -> Signal+signalReverse fn t = fn (1-t)++signalCurve :: Double -> Signal+signalCurve steepness s =+ if s < 0.5+ then 0.5 * (2*s)**steepness+ else 1-0.5 * (2 - 2*s)**steepness++signalBell :: Double -> Signal+signalBell steepness s+ | s < 0.5 = signalCurve steepness (s/0.5)+ | otherwise = signalCurve steepness (1-((s-0.5)/0.5))
src/Reanimate/Svg.hs view
@@ -2,7 +2,7 @@ module Reanimate.Svg where import Codec.Picture (PixelRGBA8 (..))-import Codec.Picture.Types+import Codec.Picture.Types () import Control.Arrow import Control.Lens (over, set, (%~), (&), (.~), (^.)) import Control.Monad.Fix@@ -350,20 +350,26 @@ CircleTree{} -> error "CircleTree" PolyLineTree{} -> error "PolyLineTree" EllipseTree{} -> error "EllipseTree"- LineTree{} -> error "LineTree"+ LineTree line -> map pointToRPoint [line^.linePoint1, line^.linePoint2] RectangleTree rect ->- case mapTuple (toUserUnit defaultDPI) (rect^.rectUpperLeftCorner) of- (Num x, Num y) -> [V2 x y] +++ case pointToRPoint (rect^.rectUpperLeftCorner) of+ V2 x y -> [V2 x y] ++ case mapTuple (fmap $ toUserUnit defaultDPI) (rect^.rectWidth, rect^.rectHeight) of (Just (Num w), Just (Num h)) -> [V2 (x+w) (y+h)] _ -> []- _ -> [] TextTree{} -> []- ImageTree{} -> []+ ImageTree img ->+ case (img^.imageCornerUpperLeft, img^.imageWidth, img^.imageHeight) of+ ((Num x, Num y), Num w, Num h) ->+ [V2 x y, V2 (x+w) (y+h)]+ _ -> [] MeshGradientTree{} -> [] where m = Transform.mkMatrix (t^.transform) mapTuple f = f *** f+ pointToRPoint p =+ case mapTuple (toUserUnit defaultDPI) p of+ (Num x, Num y) -> (V2 x y) lowerTransformations :: Tree -> Tree lowerTransformations = worker Transform.identity@@ -438,6 +444,12 @@ scale :: Double -> Tree -> Tree scale a = withTransformations [Scale a Nothing] +scaleToSize :: Double -> Double -> Tree -> Tree+scaleToSize w h t =+ scaleXY (w/w') (h/h') t+ where+ (_x, _y, w', h') = boundingBox t+ scaleXY :: Double -> Double -> Tree -> Tree scaleXY x y = withTransformations [Scale x (Just y)] @@ -475,6 +487,16 @@ where (x, y, w, h) = boundingBox t +centerX :: Tree -> Tree+centerX t = translate (-x-w/2) 0 t+ where+ (x, y, w, h) = boundingBox t++centerY :: Tree -> Tree+centerY t = translate 0 (-y-h/2) t+ where+ (x, y, w, h) = boundingBox t+ mkColor :: String -> Texture mkColor name = case Map.lookup name svgNamedColors of@@ -496,21 +518,34 @@ withFillOpacity :: Double -> Tree -> Tree withFillOpacity opacity = fillOpacity .~ Just (realToFrac opacity) +withGroupOpacity :: Double -> Tree -> Tree+withGroupOpacity opacity = groupOpacity .~ Just (realToFrac opacity)+ withStrokeWidth :: Number -> Tree -> Tree withStrokeWidth width = strokeWidth .~ pure width withClipPathRef :: ElementRef -> Tree -> Tree withClipPathRef ref = clipPathRef .~ pure ref -mkRect :: Point -> Number -> Number -> Tree-mkRect corner width height = RectangleTree $ defaultSvg- & rectUpperLeftCorner .~ corner+withId :: String -> Tree -> Tree+withId idTag = attrId .~ Just idTag++mkRect :: Number -> Number -> Tree+mkRect width height = center $ RectangleTree $ defaultSvg+ & rectUpperLeftCorner .~ (Num 0, Num 0) & rectWidth .~ Just width & rectHeight .~ Just height +mkCircle :: Number -> Tree+mkCircle radius = CircleTree $ defaultSvg+ & circleCenter .~ (Num 0, Num 0)+ & circleRadius .~ radius++-- XXX: This is broken. mkBoundingRect :: Tree -> Double -> Tree mkBoundingRect src margin =- mkRect (Num $ x-margin, Num $ y-margin) (Num $ w+margin*2) (Num $ h+margin*2)+ translate (x-margin) (y-margin) $+ mkRect (Num $ w+margin*2) (Num $ h+margin*2) where (x, y, w, h) = boundingBox src @@ -523,6 +558,17 @@ mkGroup forest = GroupTree $ defaultSvg & groupChildren .~ forest +mkDefinitions :: [Tree] -> Tree+mkDefinitions forest = DefinitionTree $ defaultSvg+ & groupChildren .~ forest++mkUse :: String -> Tree+mkUse name = UseTree (defaultSvg & useName .~ name) Nothing++mkClipPath :: String -> [Tree] -> Tree+mkClipPath idTag forest = withId idTag $ ClipPathTree $ (defaultSvg+ & clipPathContent .~ forest)+ mkPathString :: String -> Tree mkPathString = mkPathText . T.pack @@ -541,11 +587,11 @@ , LineTo OriginAbsolute [ V2 x y | (x, y) <- rest ] ] mkBackground :: String -> Tree-mkBackground color = withFillColor color $ mkRect (Num $ -320/2, Num $ -180/2) (Percent 1) (Percent 1)+mkBackground color = withFillColor color $ mkRect (Num 320) (Num 180) mkBackgroundPixel :: PixelRGBA8 -> Tree mkBackgroundPixel pixel =- withFillColorPixel pixel $ mkRect (Num $ -320/2, Num $ -180/2) (Percent 1) (Percent 1)+ withFillColorPixel pixel $ mkRect (Num 320) (Num 180) withSubglyphs :: [Int] -> (Tree -> Tree) -> Tree -> Tree withSubglyphs target fn t = evalState (worker t) 0
+ test/Spec.hs view
@@ -0,0 +1,12 @@+module Main (main) where++import Test.Tasty++import UnitTests++main :: IO ()+main = do+ tests1 <- unitTestFolder "examples/"+ tests2 <- compileTestFolder "examples/"+ tests3 <- compileVideoFolder "videos/"+ defaultMain $ testGroup "tests" [tests1, tests2, tests3]
+ test/UnitTests.hs view
@@ -0,0 +1,101 @@+module UnitTests+ ( unitTestFolder+ , compileTestFolder+ , compileVideoFolder+ ) where++import Control.Monad (forM)+import qualified Data.ByteString.Lazy as LBS+import Data.Maybe (catMaybes)+import Reanimate.Misc (withTempDir, withTempFile)+import System.Directory+import System.Exit+import System.FilePath+import System.IO+import System.Process+import Test.Tasty+import Test.Tasty.Golden+import Test.Tasty.HUnit++unitTestFolder :: FilePath -> IO TestTree+unitTestFolder path = do+ files <- getDirectoryContents path+ return $ testGroup "animate"+ [ goldenVsStringDiff file (\ref new -> ["diff", "--brief", ref, new]) fullPath (genGolden hsPath)+ | file <- files+ , let fullPath = path </> file+ hsPath = replaceExtension fullPath "hs"+ , takeExtension fullPath == ".golden"+ ]++genGolden :: FilePath -> IO LBS.ByteString+genGolden path = withTempDir $ \tmpDir -> withTempFile ".exe" $ \tmpExecutable -> do+ let ghcOpts = ["-rtsopts", "--make", "-O2"] +++ ["-odir", tmpDir, "-hidir", tmpDir, "-o", tmpExecutable]+ runOpts = ["+RTS", "-M1G"]+ -- XXX: Check for errors.+ _ <- readProcessWithExitCode "stack" (["ghc","--", path] ++ ghcOpts) ""+ -- ret <- runCmd_ "stack" $ ["ghc", "--"] ++ ghcOptions tmpDir ++ [self, "-o", tmpExecutable]+ -- ["-rtsopts", "--make", "-threaded", "-O2"] +++ -- ["-odir", tmpDir, "-hidir", tmpDir]+ (inh, outh, errh, pid) <- runInteractiveProcess tmpExecutable (["snippets"] ++ runOpts)+ Nothing Nothing+ hClose inh+ hClose errh+ LBS.hGetContents outh++compileTestFolder :: FilePath -> IO TestTree+compileTestFolder path = do+ files <- getDirectoryContents path+ return $ testGroup "compile"+ [ testCase file $ do+ (ret, _stdout, _stderr) <- readProcessWithExitCode "stack" (["ghc","--", fullPath] ++ ghcOpts) ""+ case ret of+ ExitFailure{} -> assertFailure "Failed to compile"+ ExitSuccess -> return ()+ | file <- files+ , let fullPath = path </> file+ , takeExtension file == ".hs" || takeExtension file == ".lhs"+ ]+ where+ ghcOpts = ["-fno-code", "-O0"]++compileVideoFolder :: FilePath -> IO TestTree+compileVideoFolder path = do+ files <- getDirectoryContents path+ return $ testGroup "videos"+ [ testCase dir $ do+ (ret, _stdout, _stderr) <- readProcessWithExitCode "stack" (["ghc","--", fullPath] ++ ghcOpts) ""+ case ret of+ ExitFailure{} -> assertFailure "Failed to compile"+ ExitSuccess -> return ()+ | dir <- files+ , let fullPath = path </> dir </> dir <.> "hs"+ , dir /= "." && dir /= ".."+ ]+ where+ ghcOpts = ["-fno-code", "-O0"]++--------------------------------------------------------------------------------+-- Helpers++-- findAnExecutable :: [String] -> IO (Maybe FilePath)+-- findAnExecutable [] = return Nothing+-- findAnExecutable (x:xs) = do+-- mbExec <- findExecutable x+-- case mbExec of+-- Just exec -> return (Just exec)+-- Nothing -> findAnExecutable xs+--+-- readFileOptional :: FilePath -> IO String+-- readFileOptional path = do+-- hasFile <- doesFileExist path+-- if hasFile then readFile path else return ""+--+-- assertExitCode :: String -> ExitCode -> Assertion+-- assertExitCode _ ExitSuccess = return ()+-- assertExitCode msg (ExitFailure code) = assertFailure (msg ++ ", code: " ++ show code)+--+-- assertMaybe :: String -> Maybe a -> IO a+-- assertMaybe _ (Just a) = return a+-- assertMaybe msg Nothing = assertFailure msg
viewer/build/index.html view
@@ -1,1 +1,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>+<!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.f7ad3e9b.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.c36ecb4a.chunk.js"></script></body></html>
viewer/build/service-worker.js view
@@ -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.2267f062fb7f449c2505790eb4da5a8a.js" ); workbox.clientsClaim();
− viewer/build/static/css/main.6efe09fd.chunk.css
@@ -1,2 +0,0 @@-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 */
+ viewer/build/static/css/main.f7ad3e9b.chunk.css view
@@ -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.f7ad3e9b.chunk.css.map */
+ viewer/build/static/js/main.c36ecb4a.chunk.js view
@@ -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.status="done";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.c36ecb4a.chunk.js.map
− viewer/build/static/js/main.db22f45d.chunk.js
@@ -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