From dcc60213b223b49574394b15ae8c405064db49ac Mon Sep 17 00:00:00 2001 From: Ruifeng Xie Date: Sat, 30 Dec 2023 00:22:40 +0100 Subject: [PATCH 1/8] Fix compatibility with mtl-2.3 and lts-22 --- src/Reanimate/Svg.hs | 1 + src/Reanimate/Svg/LineCommand.hs | 4 ++-- stack-lts-22.yaml | 8 ++++++++ 3 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 stack-lts-22.yaml diff --git a/src/Reanimate/Svg.hs b/src/Reanimate/Svg.hs index e890d37..ecddaba 100644 --- a/src/Reanimate/Svg.hs +++ b/src/Reanimate/Svg.hs @@ -15,6 +15,7 @@ module Reanimate.Svg ) where import Control.Lens ((%~), (&), (.~), (?~), (^.)) +import Control.Monad (liftM3, liftM4) import Control.Monad.State import Graphics.SvgTree import Linear.V2 (V2 (V2)) diff --git a/src/Reanimate/Svg/LineCommand.hs b/src/Reanimate/Svg/LineCommand.hs index d928ce5..29c9e82 100644 --- a/src/Reanimate/Svg/LineCommand.hs +++ b/src/Reanimate/Svg/LineCommand.hs @@ -16,9 +16,9 @@ module Reanimate.Svg.LineCommand where import Control.Lens ((%~), (&), (.~)) +import Control.Monad (forM) import Control.Monad.Fix (MonadFix (mfix)) -import Control.Monad.State (MonadState (get, put), State, evalState, forM, gets, - modify) +import Control.Monad.State (MonadState (get, put), State, evalState, gets, modify) import Data.Functor (($>)) import Data.Maybe (mapMaybe) import qualified Data.Vector.Unboxed as V diff --git a/stack-lts-22.yaml b/stack-lts-22.yaml new file mode 100644 index 0000000..a537a9d --- /dev/null +++ b/stack-lts-22.yaml @@ -0,0 +1,8 @@ +resolver: lts-22.3 + +allow-newer: false + +packages: +- . + +extra-deps: [] From 389160c35128346c20eb0e63c26cd2c47fff50e8 Mon Sep 17 00:00:00 2001 From: Ai-Ya-Ya Date: Fri, 26 Dec 2025 21:38:42 +0000 Subject: [PATCH 2/8] bump aeson ub 2.2 -> 2.3 --- reanimate.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reanimate.cabal b/reanimate.cabal index 3b6fdb3..51d2723 100644 --- a/reanimate.cabal +++ b/reanimate.cabal @@ -110,7 +110,7 @@ library build-depends: base >=4.10 && <5, JuicyPixels >=3.3.3, - aeson >=1.3.0.0 && <2.2, + aeson >=1.3.0.0 && <2.3, ansi-terminal >=0.8.0.4, array >=0.5.2.0, attoparsec >=0.13.2.0, From 6b09d21b0b6f4d3798e69c50b8594d8060eec273 Mon Sep 17 00:00:00 2001 From: Ai-Ya-Ya Date: Fri, 26 Dec 2025 22:48:24 +0000 Subject: [PATCH 3/8] bump websockets lb 0.12.7.0 -> 0.13.0.0 serverRequirePong was removed in 0.13 --- reanimate.cabal | 2 +- src/Reanimate/Driver/Server.hs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/reanimate.cabal b/reanimate.cabal index 51d2723..9567be2 100644 --- a/reanimate.cabal +++ b/reanimate.cabal @@ -147,7 +147,7 @@ library unordered-containers >=0.2.0.0, vector >=0.12.0.0, vector-space >=0.13, - websockets >=0.12.7.0, + websockets >=0.13.0.0, xml >=1.3.14, cryptohash-sha256, base64-bytestring, diff --git a/src/Reanimate/Driver/Server.hs b/src/Reanimate/Driver/Server.hs index f3f8c79..7606c19 100644 --- a/src/Reanimate/Driver/Server.hs +++ b/src/Reanimate/Driver/Server.hs @@ -58,8 +58,7 @@ daemon = do let options = ServerOptions { serverHost = "127.0.0.1" , serverPort = 9161 - , serverConnectionOptions = opts - , serverRequirePong = Nothing } + , serverConnectionOptions = opts } runServerWithOptions options (\pending -> do tid <- myThreadId From bae64560264bb5162c4756cea58f92c7ca5d64ab Mon Sep 17 00:00:00 2001 From: Ruifeng Xie Date: Sat, 30 Dec 2023 18:46:09 +0100 Subject: [PATCH 4/8] fix compatibility with unix-2.8 - breaking change: not compatible with unix-2.7 --- unix/Detach.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unix/Detach.hs b/unix/Detach.hs index c38ba8f..f929307 100644 --- a/unix/Detach.hs +++ b/unix/Detach.hs @@ -8,7 +8,7 @@ import System.Posix.Process (createSession, forkProcess) detach :: IO () -> IO Bool detach daemon = do void $ forkProcess $ do - devnull <- openFd "/dev/null" ReadWrite Nothing defaultFileFlags + devnull <- openFd "/dev/null" ReadWrite defaultFileFlags void $ dupTo devnull stdInput void $ dupTo devnull stdOutput void $ dupTo devnull stdError From a9b758ea0a6cdaba0b407ef8ebf5a9e999dc4ddb Mon Sep 17 00:00:00 2001 From: Ai-Ya-Ya Date: Fri, 26 Dec 2025 23:16:11 +0000 Subject: [PATCH 5/8] setup unit tests for Nix Both for a `nix-build`, where `cabal` executable is not present, but also just for using Stack with nix integration. With nix integration, Stack will make a `nix-shell` where `stack` executable is not found. We use "bare-bones" GHC to solve this. This GHC doesn't exactly respect our Stack or Cabal configurations, so we need to avoid `cubicbezier`'s `matrices` dependency so that `Data.Matrix` resolves to `matrix` package. Also, we don't error on compiler warnings when compiling test files through Nix. I don't know if these warnings would show up on a regular Stack or Cabal config (I'm on NixOS), so it's unaffected for those. TL;DR: Added testing support for Nix build environments; Cabal and Stack environments should be unchanged. --- examples/expression.hs | 7 +++ examples/sorting.hs | 1 + stack.yaml | 7 ++- stack.yaml.lock | 26 ++++++++++ test/UnitTests.hs | 112 +++++++++++++++++++++++++++++++++++------ 5 files changed, 137 insertions(+), 16 deletions(-) create mode 100644 stack.yaml.lock diff --git a/examples/expression.hs b/examples/expression.hs index ed090ad..ae5f776 100644 --- a/examples/expression.hs +++ b/examples/expression.hs @@ -60,6 +60,10 @@ op2LaTeX LeftParen = "(" op2LaTeX RightParen = ")" -- Some supportive types and instances. +instance Functor (Expr a) where + fmap f (Var x) = Var (f x) + fmap f (BinOp x op y) = BinOp (fmap f x) op (fmap f y) + fmap f (Paren l x r) = Paren l (fmap f x) r instance Bifunctor Expr where bimap _ g (Var x) = Var (g x) @@ -91,6 +95,9 @@ instance Bitraversable p => Traversable (Both p) where -- | Make a bifunctor @f (a, -) (b, -)@ (namely @BiAlongside a b f@) out of @f@. newtype BiAlongside a b f x y = BiAlongside { runBiAlongside :: f (a, x) (b, y) } +instance Bifunctor p => Functor (BiAlongside a b p c) where + fmap f (BiAlongside x) = BiAlongside (bimap id (fmap f) x) + instance Bifunctor p => Bifunctor (BiAlongside a b p) where bimap f g = BiAlongside . bimap (second f) (second g) . runBiAlongside diff --git a/examples/sorting.hs b/examples/sorting.hs index 376c987..2c55fdd 100755 --- a/examples/sorting.hs +++ b/examples/sorting.hs @@ -6,6 +6,7 @@ module Main (main) where import Codec.Picture +import Control.Monad (forM_, when, zipWithM_) import Control.Monad.ST import Control.Monad.State.Strict import Data.Text (Text) diff --git a/stack.yaml b/stack.yaml index 4db2d71..e08bbc0 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,8 +1,11 @@ -resolver: lts-18.15 +resolver: lts-24.25 allow-newer: false packages: - . -extra-deps: [] +extra-deps: +- geojson-4.1.1@sha256:f450e81d4fe1993f97c7cdb2b9743d54d2621c37b67d8a89536e5e09d33fd737,4233 +- reanimate-svg-0.13.0.1@sha256:260957e61e2e290a6cbca1d129fa0da66d624a224d0016d3bdc76fa210c70cbd,2950 + diff --git a/stack.yaml.lock b/stack.yaml.lock new file mode 100644 index 0000000..ff055aa --- /dev/null +++ b/stack.yaml.lock @@ -0,0 +1,26 @@ +# This file was autogenerated by Stack. +# You should not edit this file by hand. +# For more information, please see the documentation at: +# https://docs.haskellstack.org/en/stable/topics/lock_files + +packages: +- completed: + hackage: geojson-4.1.1@sha256:f450e81d4fe1993f97c7cdb2b9743d54d2621c37b67d8a89536e5e09d33fd737,4233 + pantry-tree: + sha256: 0a1c1e2c6533b27efd472de9931e99d5cd05e0db568840e6e6dc014c8a2591cd + size: 2376 + original: + hackage: geojson-4.1.1@sha256:f450e81d4fe1993f97c7cdb2b9743d54d2621c37b67d8a89536e5e09d33fd737,4233 +- completed: + hackage: reanimate-svg-0.13.0.1@sha256:260957e61e2e290a6cbca1d129fa0da66d624a224d0016d3bdc76fa210c70cbd,2950 + pantry-tree: + sha256: 93a217570c21431d5c9cf369058c8227aa1dee3a506edfed324550969c55a685 + size: 39432 + original: + hackage: reanimate-svg-0.13.0.1@sha256:260957e61e2e290a6cbca1d129fa0da66d624a224d0016d3bdc76fa210c70cbd,2950 +snapshots: +- completed: + sha256: c0060a2ed91942bd6426f00617125fe9f0fef613dad0acc5f0ea10e8d499f799 + size: 726335 + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/24/25.yaml + original: lts-24.25 diff --git a/test/UnitTests.hs b/test/UnitTests.hs index ad36b0f..bbba966 100644 --- a/test/UnitTests.hs +++ b/test/UnitTests.hs @@ -5,9 +5,11 @@ module UnitTests ) where import Control.Exception +import Control.Monad (filterM) import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as LBS import Data.List (sort) +import Data.Maybe (maybe) import qualified Data.Text as T import qualified Data.Text.IO as T import System.Directory @@ -23,22 +25,78 @@ import Test.Tasty.HUnit data BuildSystem = Cabal | Stack + | NixBuild NixBuildSystem + +data NixBuildSystem + = NixCabal + | NixStack + +-- required for nix build +nixGHCOptions :: [String] +nixGHCOptions = + [ "-XPackageImports" + , "-XPatternSynonyms" + , "-Wall" + , "-fno-ignore-asserts" + , "-DNO_HGEOMETRY" + , "-hide-package" + , "matrices" + ] + +findStackAutogen :: IO (Maybe FilePath) +findStackAutogen = do + let base = ".stack-work" "dist" + baseExists <- doesDirectoryExist base + if not baseExists + then return Nothing + else do + subDirs <- listDirectories base + case subDirs of + [] -> return Nothing + (arch:_) -> do + let archPath = base arch + cabalDirs <- listDirectories archPath + case cabalDirs of + [] -> return Nothing + (ver:_) -> return $ Just (archPath ver "build" "autogen") + +includeBuild :: NixBuildSystem -> IO [String] +includeBuild NixCabal = return ["-idist/build/autogen"] +includeBuild NixStack = do + mbPath <- findStackAutogen + case mbPath of + Nothing -> return [] + Just p -> return ["-i" ++ p] + +listDirectories :: FilePath -> IO [FilePath] +listDirectories path = do + contents <- listDirectory path + filterM (\f -> doesDirectoryExist (path f)) contents {-# NOINLINE buildSystem #-} buildSystem :: BuildSystem buildSystem = unsafePerformIO $ do + exeCabal <- findExecutable "cabal" + exeStack <- findExecutable "stack" + newbuild <- doesDirectoryExist "dist-newstyle" - stack <- doesDirectoryExist ".stack-work" - if newbuild then pure Cabal - else if stack then pure Stack - else error "Unknown build system." + v1build <- doesDirectoryExist "dist" + stack <- doesDirectoryExist ".stack-work" + + return $ case (exeCabal, exeStack) of + (Nothing, _) | v1build -> NixBuild NixCabal + (_, Nothing) | stack -> NixBuild NixStack + (Just _, _) | newbuild -> Cabal + (_, Just _) | stack -> Stack + _ -> error "Unknown build system." {-# NOINLINE unitTestsDisabled #-} unitTestsDisabled :: Bool unitTestsDisabled = case buildSystem of - Cabal -> False - Stack -> unsafePerformIO $ do + Cabal -> False + NixBuild _ -> False + Stack -> unsafePerformIO $ do (ret, _, _) <- readProcessWithExitCode "stack" ["exec","--","ghc", "-e", "Reanimate.duration Reanimate.Builtin.Documentation.drawCircle"] "" case ret of ExitFailure{} -> pure True @@ -64,16 +122,26 @@ unitTestFolder path = do genGolden :: FilePath -> IO LBS.ByteString genGolden path = do (inh, outh, errh, pid) <- case buildSystem of - Stack -> runInteractiveProcess "stack" ["runhaskell", path, "test"] + Stack -> runInteractiveProcess "stack" ["runhaskell", path, "test"] Nothing Nothing - Cabal -> runInteractiveProcess "cabal" ["v2-exec", "runhaskell", path, "test"] + Cabal -> runInteractiveProcess "cabal" ["v2-exec", "runhaskell", path, "test"] Nothing Nothing + NixBuild nixbuild -> do + nixIncludeDir <- includeBuild nixbuild + runInteractiveProcess "ghc" + ( ["-isrc", "-iunix"] + ++ nixIncludeDir + ++ nixGHCOptions + ++ ["--run", path, "--", "test"] + ) Nothing Nothing -- hSetBinaryMode outh True - -- hSetNewlineMode outh universalNewlineMode + -- hSetNewlineMode outh universalNewlineMode + hClose inh out <- BS.hGetContents outh err <- T.hGetContents errh code <- waitForProcess pid + case code of ExitSuccess -> return $ LBS.fromChunks [out] ExitFailure{} -> error $ "Failed to run: " ++ T.unpack err @@ -87,9 +155,17 @@ compileTestFolder path = do [ testCase file $ do (ret, _stdout, err) <- case buildSystem of - Stack -> readProcessWithExitCode "stack" (["ghc","--", fullPath] ++ ghcOpts) "" - Cabal -> readProcessWithExitCode "cabal" + Stack -> readProcessWithExitCode "stack" (["ghc","--", fullPath] ++ ghcOpts) "" + Cabal -> readProcessWithExitCode "cabal" (["v2-exec","ghc","--", "-package", "reanimate", fullPath] ++ ghcOpts) "" + NixBuild nixbuild -> do + nixIncludeDir <- includeBuild nixbuild + readProcessWithExitCode "ghc" + ( ["-isrc", "-iunix"] + ++ nixIncludeDir + ++ nixGHCOptions + ++ [fullPath] ++ (init ghcOpts) + ) "" _ <- evaluate (length err) case ret of ExitFailure{} -> assertFailure $ "Failed to compile:\n" ++ err @@ -100,7 +176,7 @@ compileTestFolder path = do , notElem (replaceExtension file "golden") goldenFiles ] where - ghcOpts = ["-fno-code", "-O0", "-Werror", "-Wall"] + ghcOpts = ["-fno-code", "-O0", "-Wall", "-Werror"] compileVideoFolder :: FilePath -> IO TestTree compileVideoFolder _ | unitTestsDisabled = return $ testGroup "videos (disabled)" [] @@ -113,8 +189,16 @@ compileVideoFolder path = do [ testCase dir $ do (ret, _stdout, err) <- case buildSystem of - Stack -> readProcessWithExitCode "stack" (["ghc","--", "-i"++pathdir, fullPath] ++ ghcOpts) "" - Cabal -> readProcessWithExitCode "cabal" (["v2-exec", "ghc","--", "-package", "reanimate", "-i"++pathdir, fullPath] ++ ghcOpts) "" + Stack -> readProcessWithExitCode "stack" (["ghc","--", "-i"++pathdir, fullPath] ++ ghcOpts) "" + Cabal -> readProcessWithExitCode "cabal" (["v2-exec", "ghc","--", "-package", "reanimate", "-i"++pathdir, fullPath] ++ ghcOpts) "" + NixBuild nixbuild -> do + nixIncludeDir <- includeBuild nixbuild + readProcessWithExitCode "ghc" + ( ["-isrc", "-iunix", "-i"++pathdir] + ++ nixIncludeDir + ++ nixGHCOptions + ++ [fullPath] ++ ghcOpts + ) "" _ <- evaluate (length err) case ret of ExitFailure{} -> assertFailure $ "Failed to compile:\n" ++ err From 78223f593785074e6b172d40c94c772ab851ea5e Mon Sep 17 00:00:00 2001 From: Ai-Ya-Ya Date: Mon, 29 Dec 2025 19:14:53 +0000 Subject: [PATCH 6/8] fix rendering in reverse order --- src/Reanimate/Morph/Linear.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Reanimate/Morph/Linear.hs b/src/Reanimate/Morph/Linear.hs index d73ea6b..0460820 100644 --- a/src/Reanimate/Morph/Linear.hs +++ b/src/Reanimate/Morph/Linear.hs @@ -91,5 +91,5 @@ closestLinearCorrespondenceA src' dst' = linearTrajectory :: Trajectory linearTrajectory (src,dst) | pSize src == pSize dst = \t -> mkPolygon $ - V.zipWith (lerp $ realToFrac t) (polygonPoints dst) (polygonPoints src) + V.zipWith (lerp $ realToFrac t) (polygonPoints src) (polygonPoints dst) | otherwise = error $ "Invalid lengths: " ++ show (pSize src, pSize dst) From e0a09825bf02e083a05e5a5f29dd3a36467c58eb Mon Sep 17 00:00:00 2001 From: Ai-Ya-Ya Date: Mon, 29 Dec 2025 19:15:52 +0000 Subject: [PATCH 7/8] add stack for use in Nix devshells --- stack-nix.yaml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 stack-nix.yaml diff --git a/stack-nix.yaml b/stack-nix.yaml new file mode 100644 index 0000000..38da8e0 --- /dev/null +++ b/stack-nix.yaml @@ -0,0 +1,21 @@ +resolver: lts-24.25 + +allow-newer: true + +packages: +- . + +extra-deps: +- geojson-4.1.1@sha256:f450e81d4fe1993f97c7cdb2b9743d54d2621c37b67d8a89536e5e09d33fd737,4233 +- reanimate-svg-0.13.0.1@sha256:260957e61e2e290a6cbca1d129fa0da66d624a224d0016d3bdc76fa210c70cbd,2950 + +nix: + enable: true + packages: + - zlib + - ffmpeg + - imagemagick + - librsvg + +system-ghc: true +install-ghc: false From 5e81ab337f3ff4cbc77af5d6214a890192890cf5 Mon Sep 17 00:00:00 2001 From: Ai-Ya-Ya Date: Mon, 29 Dec 2025 19:24:31 +0000 Subject: [PATCH 8/8] Fix last edge of rectangle rendering in reverse order I found an issue where a rectangle that is supposed to be animated (in terms of corners) `BL -> BR -> TR -> TL -> cycle back to BL` are instead rendered as `BL -> BR -> TR -> TL`, and then suddenly cuts to the completed rectangle, before retracting the line `TL -> BL` from the BL corner (so, instead of a line dropping from TL down, imagine the full line retracting from BL up toward TL). It seems that `LineEnd` doesn't mean "draw a line from the current position to the initial position" like `Z` does in SVG specifications. So, I manually added the last edge of a rectangle as a `PathCommand`. This does change all the tests that depend on animating rectangles. The only difference is the explicit `v` representing the downstroke. I'm not sure if this is ideal (i.e. if `LineEnd` is intended to behave like SVG `Z`) but it works. --- examples/golden/doc_andThen.golden | 10 +++++----- examples/golden/doc_applyVar.golden | 2 +- examples/golden/doc_destroySprite.golden | 2 +- examples/golden/doc_drawBox.golden | 2 +- examples/golden/doc_fadeT.golden | 2 +- examples/golden/doc_fork.golden | 2 +- examples/golden/doc_parA.golden | 10 +++++----- examples/golden/doc_pathify.golden | 2 +- examples/golden/doc_spriteVar.golden | 2 +- src/Reanimate/Svg.hs | 1 + 10 files changed, 18 insertions(+), 17 deletions(-) diff --git a/examples/golden/doc_andThen.golden b/examples/golden/doc_andThen.golden index 14d72ab..4972e7e 100644 --- a/examples/golden/doc_andThen.golden +++ b/examples/golden/doc_andThen.golden @@ -3,8 +3,8 @@ 2 3 4 -5 -6 -7 -8 -9 +5 +6 +7 +8 +9 diff --git a/examples/golden/doc_applyVar.golden b/examples/golden/doc_applyVar.golden index d67944a..189a52b 100644 --- a/examples/golden/doc_applyVar.golden +++ b/examples/golden/doc_applyVar.golden @@ -7,4 +7,4 @@ 6 7 8 -9 +9 diff --git a/examples/golden/doc_destroySprite.golden b/examples/golden/doc_destroySprite.golden index b90da20..3be4453 100644 --- a/examples/golden/doc_destroySprite.golden +++ b/examples/golden/doc_destroySprite.golden @@ -7,4 +7,4 @@ 6 7 8 -9 +9 diff --git a/examples/golden/doc_drawBox.golden b/examples/golden/doc_drawBox.golden index 77bd964..d945856 100644 --- a/examples/golden/doc_drawBox.golden +++ b/examples/golden/doc_drawBox.golden @@ -7,4 +7,4 @@ 6 7 8 -9 +9 diff --git a/examples/golden/doc_fadeT.golden b/examples/golden/doc_fadeT.golden index b1b1d17..3112af6 100644 --- a/examples/golden/doc_fadeT.golden +++ b/examples/golden/doc_fadeT.golden @@ -7,4 +7,4 @@ 6 7 8 -9 +9 diff --git a/examples/golden/doc_fork.golden b/examples/golden/doc_fork.golden index 8dd954b..841096e 100644 --- a/examples/golden/doc_fork.golden +++ b/examples/golden/doc_fork.golden @@ -7,4 +7,4 @@ 6 7 8 -9 +9 diff --git a/examples/golden/doc_parA.golden b/examples/golden/doc_parA.golden index efd8be2..91f320d 100644 --- a/examples/golden/doc_parA.golden +++ b/examples/golden/doc_parA.golden @@ -3,8 +3,8 @@ 2 3 4 -5 -6 -7 -8 -9 +5 +6 +7 +8 +9 diff --git a/examples/golden/doc_pathify.golden b/examples/golden/doc_pathify.golden index c46d978..42c0f26 100644 --- a/examples/golden/doc_pathify.golden +++ b/examples/golden/doc_pathify.golden @@ -7,4 +7,4 @@ 6 7 8 -9 +9 diff --git a/examples/golden/doc_spriteVar.golden b/examples/golden/doc_spriteVar.golden index d67944a..189a52b 100644 --- a/examples/golden/doc_spriteVar.golden +++ b/examples/golden/doc_spriteVar.golden @@ -7,4 +7,4 @@ 6 7 8 -9 +9 diff --git a/src/Reanimate/Svg.hs b/src/Reanimate/Svg.hs index ecddaba..a174c9f 100644 --- a/src/Reanimate/Svg.hs +++ b/src/Reanimate/Svg.hs @@ -264,6 +264,7 @@ pathify = mapTree worker ,HorizontalTo OriginRelative [w] ,VerticalTo OriginRelative [h] ,HorizontalTo OriginRelative [-w] + ,VerticalTo OriginRelative [-h] ,EndPath ] LineTree line | Just (x1,y1, x2, y2) <- unpackLine line -> PathTree $ defaultSvg