packages feed

svgone 0.1.0.0 → 0.2.0.0

raw patch · 5 files changed

+151/−41 lines, 5 filesdep ~basedep ~bytestringdep ~processPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: base, bytestring, process

API changes (from Hackage documentation)

+ Svgone: runFile :: [SomePlugin] -> FilePath -> FilePath -> IO ()
+ Svgone.Plugin.MergePaths: AbsolutePath :: PathStyle
+ Svgone.Plugin.MergePaths: SmartPath :: PathStyle
+ Svgone.Plugin.MergePaths: data PathStyle
+ Svgone.Plugin.RemoveAttributes: data P
+ Svgone.Plugin.RemoveAttributes: data family PluginOptions a
+ Svgone.Plugin.RemoveAttributes: instance Svgone.Plugin.Plugin Svgone.Plugin.RemoveAttributes.P

Files

src/Svgone.hs view
@@ -1,14 +1,28 @@ module Svgone where  import Data.Text (Text)+import qualified Data.Text.IO as T import Graphics.SvgTree (Document, parseSvgFile, saveXmlFile) import Svgone.Plugin import qualified Svgone.Plugin.CollapseGroups as CollapseGroups import qualified Svgone.Plugin.MergePaths as MergePaths+import qualified Svgone.Plugin.RemoveAttributes as RemoveAttributes  data SomePlugin where     SomePlugin :: Plugin a => PluginOptions a -> SomePlugin +runFile ::+    -- | Operations to perform, left to right.+    [SomePlugin] ->+    -- | Input file.+    FilePath ->+    -- | Output file+    FilePath ->+    IO ()+runFile ps in' out = do+    t <- T.readFile in'+    run ps in' t out+ run ::     -- | Operations to perform, left to right.     [SomePlugin] ->@@ -30,5 +44,6 @@ allPluginsWithDefaults :: [SomePlugin] allPluginsWithDefaults =     [ SomePlugin $ defaultOpts @CollapseGroups.P+    , SomePlugin $ defaultOpts @RemoveAttributes.P     , SomePlugin $ defaultOpts @MergePaths.P     ]
src/Svgone/Plugin/MergePaths.hs view
@@ -1,6 +1,6 @@ {-# OPTIONS_GHC -Wno-type-defaults #-} -module Svgone.Plugin.MergePaths (P, PluginOptions (..)) where+module Svgone.Plugin.MergePaths (P, PluginOptions (..), PathStyle (..)) where  import Control.Lens import Control.Monad@@ -10,6 +10,7 @@ import Data.List.NonEmpty (NonEmpty ((:|))) import qualified Data.List.NonEmpty as NE import Data.Maybe+import Data.Monoid import Data.Tuple import GHC.Generics import Graphics.SvgTree hiding (Text)@@ -20,12 +21,19 @@ data P type Opts = PluginOptions P +data PathStyle+    = -- | Just use absolute coordinates.+      AbsolutePath+    | -- | Use relative offsets and horizontal/vertical lines where possible.+      SmartPath+ instance Plugin P where     data PluginOptions P = Opts         { -- | For floating-point equality.           optsTolerance :: Double+        , pathStyle :: PathStyle         }-    defaultOpts = Opts 1+    defaultOpts = Opts 1 SmartPath     plugin :: Opts -> Document -> Document     plugin opts = documentElements %~ map Tree . branches opts . map (^. treeBranch)     pluginName = "merge-paths"@@ -37,7 +45,7 @@     polygons' = do         (attrs, paths) <- snd <<<$>>> classifyOn fst polygons         merged <- mergePaths opts $ NE.toList paths-        pure $ PathNode $ fromPolygonPath merged attrs+        pure $ PathNode $ fromPolygonPath opts merged attrs  mergePaths :: Opts -> [PolygonPath] -> [PolygonPath] mergePaths opts = \case@@ -89,9 +97,12 @@     deriving (Eq, Show, Generic)  toPolygonPath :: Path -> Maybe (DrawAttributes, PolygonPath)-toPolygonPath (Path attrs pcs) = case pcs of-    MoveTo OriginAbsolute [v] : xs -> (attrs,) . PolygonPath . (v :|) <$> f v xs-    _ -> Nothing+toPolygonPath (Path attrs pcs) = do+    guard $ -- only proceed if there is no visible stroke+        maybe True nearZeroNumber (getLast $ attrs ^. strokeWidth)+            || maybe False nearZero (attrs ^. strokeOpacity)+    MoveTo OriginAbsolute [v] : xs <- pure pcs+    (attrs,) . PolygonPath . (v :|) <$> f v xs   where     f :: V2 Double -> [PathCommand] -> Maybe [V2 Double]     f v0@(V2 x0 y0) = \case@@ -108,9 +119,15 @@             g v = (v :) <$> f v cs         [] -> Nothing -- should end with 'EndPath' -fromPolygonPath :: PolygonPath -> DrawAttributes -> Path-fromPolygonPath (PolygonPath (v0 :| p)) =-    flip Path $-        [MoveTo OriginAbsolute [v0]]-            ++ map (LineTo OriginAbsolute . pure) p-            ++ [EndPath]+fromPolygonPath :: Opts -> PolygonPath -> DrawAttributes -> Path+fromPolygonPath Opts{..} (PolygonPath p@(v0 :| p')) =+    flip Path . (MoveTo OriginAbsolute [v0] :) $ (++ [EndPath]) case pathStyle of+        AbsolutePath -> map (LineTo OriginAbsolute . pure) p'+        SmartPath -> map (uncurry fromTo) . pairAdjacent $ NE.toList p+  where+    fromTo a b+        | abs (d ^. _y) < optsTolerance = HorizontalTo OriginRelative [d ^. _x]+        | abs (d ^. _x) < optsTolerance = VerticalTo OriginRelative [d ^. _y]+        | otherwise = LineTo OriginRelative [b - a]+      where+        d = b - a
+ src/Svgone/Plugin/RemoveAttributes.hs view
@@ -0,0 +1,61 @@+module Svgone.Plugin.RemoveAttributes (P, PluginOptions (..)) where++import Control.Lens+import Data.Monoid+import Graphics.SvgTree+import Linear.Epsilon+import Svgone.Plugin+import Util++data P+type Opts = PluginOptions P++instance Plugin P where+    data PluginOptions P = Opts+        { defaultAttributes :: Bool+        , -- | Remove all stroke attributes if the stroke isn't visible.+          invisiblePathStroke :: Bool+        }+    defaultOpts = Opts True True+    plugin :: Opts -> Document -> Document+    plugin Opts{..} =+        documentElements+            %~ map+                ( Tree+                    . ( \x ->+                            maybe+                                x+                                ( PathNode+                                    . over+                                        drawAttributes+                                        ( applyWhen invisiblePathStroke removeInvisibleStroke+                                            . applyWhen defaultAttributes removeDefaultAttributes+                                        )+                                )+                                $ pathBranch x+                      )+                    . (^. treeBranch)+                )+    pluginName = "remove-attributes"++removeDefaultAttributes :: HasDrawAttributes p => p -> p+removeDefaultAttributes attrs+    | Just x <- attrs ^. fillOpacity, nearZero $ abs $ x - 1 = attrs & fillOpacity .~ Nothing+    | otherwise = attrs++removeInvisibleStroke :: DrawAttributes -> DrawAttributes+removeInvisibleStroke attrs+    | Last (Just x) <- attrs ^. strokeWidth, nearZeroNumber x = remove+    | Just x <- attrs ^. strokeOpacity, nearZero x = remove+    | otherwise = attrs+  where+    remove =+        attrs+            & strokeWidth .~ Last Nothing+            & strokeColor .~ Last Nothing+            & strokeOpacity .~ Nothing+            & strokeLineCap .~ Last Nothing+            & strokeLineJoin .~ Last Nothing+            & strokeMiterLimit .~ Last Nothing+            & strokeOffset .~ Last Nothing+            & strokeDashArray .~ Last Nothing
src/Util.hs view
@@ -3,6 +3,7 @@ module Util where  import Control.Monad+import Data.Bool import Data.List.NonEmpty (NonEmpty ((:|))) import qualified Data.List.NonEmpty as NE import qualified Data.Map as Map@@ -12,6 +13,9 @@  --TODO upstream to svg-tree, extra, linear etc. +applyWhen :: Bool -> (a -> a) -> a -> a+applyWhen = flip $ bool id+ infixl 5 <<$>> (<<$>>) :: (Functor f, Functor g) => (a -> b) -> f (g a) -> f (g b) (<<$>>) = fmap . fmap@@ -50,40 +54,40 @@ -} equivalentCycles :: NonEmpty a -> [NonEmpty a] equivalentCycles = concatMap refls . rots- where-  refls xs = [xs, NE.reverse xs]-  rots = go []-  go ys = \case-    x :| xs -> (x :| xs ++ ys) : maybe [] (go $ ys ++ [x]) (NE.nonEmpty xs)+  where+    refls xs = [xs, NE.reverse xs]+    rots = go []+    go ys = \case+        x :| xs -> (x :| xs ++ ys) : maybe [] (go $ ys ++ [x]) (NE.nonEmpty xs)  -- | Based on wikipedia: https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection#Given_two_points_on_each_line intersectLines :: (Eq a, Ord a, Fractional a, Show a) => (V2 a, V2 a) -> (V2 a, V2 a) -> Maybe (V2 a) intersectLines (V2 x1 y1, V2 x2 y2) (V2 x3 y3, V2 x4 y4) = do-  guard $ den /= 0 -- lines are not parallel or coincident-  guard $ t >= 0 && t <= 1 -- intersection is on first line segment-  guard $ u >= 0 && u <= 1 -- intersection is on second line segment-  pure $ V2 x y- where-  dx12 = x1 - x2-  dx13 = x1 - x3-  dx34 = x3 - x4+    guard $ den /= 0 -- lines are not parallel or coincident+    guard $ t >= 0 && t <= 1 -- intersection is on first line segment+    guard $ u >= 0 && u <= 1 -- intersection is on second line segment+    pure $ V2 x y+  where+    dx12 = x1 - x2+    dx13 = x1 - x3+    dx34 = x3 - x4 -  dy12 = y1 - y2-  dy13 = y1 - y3-  dy34 = y3 - y4+    dy12 = y1 - y2+    dy13 = y1 - y3+    dy34 = y3 - y4 -  den = dx12 * dy34 - dy12 * dx34+    den = dx12 * dy34 - dy12 * dx34 -  u = (dy12 * dx13 - dx12 * dy13) / den-  t = (dx13 * dy34 - dy13 * dx34) / den+    u = (dy12 * dx13 - dx12 * dy13) / den+    t = (dx13 * dy34 - dy13 * dx34) / den -  x = x1 - t * dx12-  y = y1 - t * dy12+    x = x1 - t * dx12+    y = y1 - t * dy12  pathBranch :: TreeBranch -> Maybe Path pathBranch = \case-  PathNode p -> Just p-  _ -> Nothing+    PathNode p -> Just p+    _ -> Nothing  deriving instance Ord Cap deriving instance Ord DrawAttributes@@ -95,3 +99,15 @@ deriving instance Ord TextAnchor deriving instance Ord Texture deriving instance Ord Transformation++nearZeroNumber :: Number -> Bool+nearZeroNumber = \case+    Num d -> nearZero d+    Px d -> nearZero d+    Em d -> nearZero d+    Percent d -> nearZero d+    Pc d -> nearZero d+    Mm d -> nearZero d+    Cm d -> nearZero d+    Point d -> nearZero d+    Inches d -> nearZero d
svgone.cabal view
@@ -1,6 +1,6 @@ cabal-version:       3.0 name:                svgone-version:             0.1.0.0+version:             0.2.0.0 license:             BSD-3-Clause maintainer:          George Thomas <georgefsthomas@gmail.com> homepage:            https://github.com/georgefst/svgone@@ -13,8 +13,8 @@  common common     build-depends:-        base ^>= 4.14.1,-        bytestring ^>= 0.10.12,+        base ^>= { 4.13, 4.14 },+        bytestring ^>= 0.10.10,         containers ^>= 0.6.2,         directory ^>= 1.3.6,         extra ^>= 1.7.9,@@ -24,10 +24,10 @@         linear ^>= 1.21.3,         mtl ^>= 2.2.2,         pretty-simple ^>= 4.0.0,-        process ^>= 1.6.9,+        process ^>= 1.6.8,         reanimate-svg ^>= 0.13.0,         safe ^>= 0.3.19,-        text ^>= 1.2.4,+        text ^>= 1.2.3,     ghc-options:         -Wall         -fdefer-typed-holes@@ -78,7 +78,8 @@     exposed-modules:         Svgone         Svgone.Plugin-        Svgone.Plugin.MergePaths         Svgone.Plugin.CollapseGroups+        Svgone.Plugin.MergePaths+        Svgone.Plugin.RemoveAttributes     other-modules:         Util