juicy-gcode 0.3.0.0 → 1.0.0.0
raw patch · 7 files changed
+245/−106 lines, 7 filesdep +JuicyPixelsdep +aesondep +unordered-containersdep −configurator
Dependencies added: JuicyPixels, aeson, unordered-containers, yaml
Dependencies removed: configurator
Files
- ChangeLog.md +5/−0
- README.md +58/−36
- juicy-gcode.cabal +10/−7
- src/GCode.hs +121/−31
- src/Graphics/Path.hs +5/−1
- src/Main.hs +5/−17
- src/Render.hs +41/−14
ChangeLog.md view
@@ -1,5 +1,10 @@ # Revision history for juicy-gcode +## 1.0.0.0 -- 2023-05-28++- Breaking change: change favor file format to YAML+- Enable color specific configuration+ ## 0.3.0.0 -- 2022-12-14 - Add linear approximation algorithm
README.md view
@@ -1,18 +1,26 @@-# Juicy-gcode: A lightweight SVG to GCode converter for maximal curve fitting+# Juicy-gcode: A lightweight 2.5D SVG to GCode converter for maximal curve fitting [](https://hackage.haskell.org/package/juicy-gcode) [](https://ci.appveyor.com/project/domoszlai/juicy-gcode) ## Overview -Juicy-gcode is a configurable SVG to G-code converter that approximates bezier curves based on your needs:-- with [biarcs](http://dlacko.org/blog/2016/10/19/approximating-bezier-curves-by-biarcs/) for maximal curve fitting: bezier curves are approximated with arcs ([G2, G3 commands](https://marlinfw.org/meta/gcode/)), the resulting path is [G1 continues](https://skill-lync.com/blogs/introductions-to-surface-continuities-and-its-types)-- with linear approximation when arcs are not supported by your firmware: bezier curves are approximated with line segments ([G0, G1 commands](https://marlinfw.org/meta/gcode/)), the resulting path is [not smooth](https://skill-lync.com/blogs/introductions-to-surface-continuities-and-its-types), and the generated gcode is usually significantly larger -- with cubic bezier curves if your firmware supports it: some firmwares (e.g. [Marlin](https://marlinfw.org/docs/gcode/G005.html)) can handle bezier curves directly ([G5 command](https://marlinfw.org/meta/gcode/)), the result is [G2 continues](https://skill-lync.com/blogs/introductions-to-surface-continuities-and-its-types)+Juicy-GCode is a command-line application that converts SVG files to GCode. It provides flexible options for curve approximation and allows configurable GCode generation based on color information. +## Features ++- **Configurable Curve Approximation**: Juicy-GCode offers three approximation methods to suit your needs+ - with [biarcs](http://dlacko.org/blog/2016/10/19/approximating-bezier-curves-by-biarcs/) for maximal curve fitting: bezier curves are approximated with arcs ([G2, G3 commands](https://marlinfw.org/meta/gcode/)), the resulting path is [G1 continuous](https://skill-lync.com/blogs/introductions-to-surface-continuities-and-its-types)+ - with linear approximation when arcs are not supported by your firmware: bezier curves are approximated with line segments ([G0, G1 commands](https://marlinfw.org/meta/gcode/)), the resulting path is [not smooth](https://skill-lync.com/blogs/introductions-to-surface-continuities-and-its-types), and the generated GCode is usually significantly larger + - with cubic bezier curves if your firmware supports it: some firmwares (e.g. [Marlin](https://marlinfw.org/docs/gcode/G005.html)) can handle bezier curves directly ([G5 command](https://marlinfw.org/meta/gcode/)), the result is [G2 continuous](https://skill-lync.com/blogs/introductions-to-surface-continuities-and-its-types)+- **Configurable 2.5D GCode generation**: Juicy-GCode allows you to configure the GCode generation by color information (this feature enables e.g. carving of 3D objects based on a single SVG file)+ - extra GCode can be added to the beginning of a continous colored path+ - GCode [parameters](https://reprap.org/wiki/G-code#G0_.26_G1:_Move) e.g. `E`, `F` or `S` can be set per color+ - the number of passes can be set for a given color (upcoming feature)+ ## Installation -The easiest way is to download one of the pre-built binaries from the [releases page](https://github.com/domoszlai/juicy-gcode/releases).+The easiest way is to download one of the prebuilt binaries from the [releases page](https://github.com/domoszlai/juicy-gcode/releases). Alternatively, you can build from source code as follows: - Install [Stack](https://docs.haskellstack.org/en/stable/install_and_upgrade/) if you do not have it yet@@ -23,6 +31,8 @@ ## Usage +> :warning: **Breaking change**: Since version 1.0.0.0, the flavor file format changed to YAML. If you use a pre 1.0 version, please refer to [this historical README file](https://github.com/domoszlai/juicy-gcode/blob/9d573918eb1c4a99801c8d6745f7471ba987828c/README.md)+ > :warning: **Breaking change**: Since version 0.3.0.0, `--generate-bezier` has been removed in favor of the more generic `--curve-fitting` parameter and `--resolution` has been renamed to `--tolerance` > :warning: **Breaking change**: Since version 0.2.0.1, default DPI is changed to 96 and the option to mirror the Y axis is removed (it is always mirrored now for correct result)@@ -42,7 +52,7 @@ Sometimes you want to overwrite some default settings. These are the * *--dpi* (default 96 DPI) [the resolution of the SVG file](https://developer.mozilla.org/en-US/docs/Web/CSS/resolution) that is used to determine the size of the SVG when it does not contain explicit units-* *--tolerance* (default is 0.1 mm) maximum allowed derivation of the approximation curve+* *--tolerance* (default is 0.1 mm) maximum allowed deviation of the approximation curve ``` $ juicy-gcode SVGFILE --dpi 72 --tolerance 0.01 @@ -62,49 +72,61 @@ ## Configuration -The generated GCode is highly dependent on the actual device it will be executed by. In juicy-gcode these settings are called-GCode *flavor* and consists of the following:+The generated GCode is highly dependent on the actual device it will be executed by and one might also want to generate different GCode for different colors. In `juicy-gcode` these settings are called GCode *flavor* and consist of the following: - Begin GCode routine (commands that are executed *before* the actual print job) - End GCode routine (commands that are executed *after* the actual print job)-- Tool on (commands to switch the tool on, e.g. lower pen)-- Tool off (commands to switch the tool off e.g. lift pen)+- Tool on (commands to switch the tool on, e.g. lower the pen)+- Tool off (commands to switch the tool off e.g. lift the pen)+- Color dependent settings -These settings can be provided by a configuration file. The default settings+These settings can be provided by a YAML configuration file. The default settings are made for being able to test the generated GCode in an emulator e.g. with [LaserWeb](https://laserweb.yurl.ch/) or [my hanging plotter simulator](https://github.com/domoszlai/hanging-plotter-simulator). -```-gcode-{- begin = "G17;G90;G0 Z1;G0 X0 Y0"- end = "G0 Z1"- toolon = "G00 Z1"- tooloff = "G01 Z0 F10.00"-}+```YAML+begin: |+ G17+ G90+ G0 Z1+ G0 X0 Y0+end: |+ G0 Z1+toolon: |+ G01 Z0 F10.00+tooloff: |+ G00 Z1 ``` -In the case you want to overwrite it, copy this favor to a text file and modify it according to your need. Then use juicy-gcode as follows:+By providing your own configuration file, you can also specify color dependent settings. As an example, the following+configuration adds extra GCode configuration for the color codes `#000000` and `#FF0000`. -```-$ juicy-gcode SVGFILE -f FLAVORFILE+```YAML+colors:+ "000000": + before: |+ ; black+ passes: 1+ parameters:+ F: 1.0 + S: 80+ "FF0000": + before: |+ ; red+ passes: 1+ parameters:+ F: 2.0 + S: 25 ``` -## Future development--Juicy-gcode was originally developed as a testbed for my hanging plotter project, but over the years-it reached maturity and became a really usuable tool. My main idea for further development is to turn it-into a tool that can drive CNCs in 2.5 dimensions (e.g. carving, engraving) with just one colored SVG file.--To be able to test and enjoy that software, I need a proper CNC. Please consider donating a small amount for that purpose,-or donate an actual CNC if you have a spare one for whatever reason.--**[Donate for a CNC](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=UGFZYDQSTF58L&source=https://github.com/domoszlai/juicy-gcode/)**+In this example, GCode comments will be generated before any continous red or black path, the `F` and `S` GCode parameters are+set different for red and black paths (and not set for any other colors) and the number of passes are set to 1 for both colors (passes is an upcoming feature, currently this option is ignored) -Collected so far: 229.47€-Target: >= 209€+Use the `-f` option to pass the GCode flavor to `juicy-gcode`: -Thank you so much for all people supporting the development!+```+$ juicy-gcode SVGFILE -f FLAVORFILE+``` ## Limitations
juicy-gcode.cabal view
@@ -1,5 +1,5 @@ name: juicy-gcode-version: 0.3.0.0+version: 1.0.0.0 license: BSD3 license-file: LICENSE author: dlacko@@ -11,7 +11,7 @@ bug-reports: https://github.com/domoszlai/juicy-gcode/issues build-type: Simple description:- SVG to G-code converter that aims to support most SVG features. The flavor of the generated G-Code can be influenced providing a configuration file.+ SVG to G-code converter that aims to support most SVG features. It provides flexible options for curve approximation and allows configurable GCode generation based on color information. extra-source-files: ChangeLog.md, README.md cabal-version: >=1.10@@ -39,14 +39,17 @@ build-depends: base >=4.8 && <5,- lens >=4.15.4 && <4.20,- linear >=1.20 && <1.22,+ lens >=4.15.4 && <5.3,+ linear >=1.20 && <1.23, optparse-applicative >=0.13 && <0.20,- configurator >=0.3 && <0.4,- text >=1.2.2 && <1.3,+ aeson >=1.5 && <2.2,+ yaml >=0.11.8 && <0.12,+ text >=1.2.2 && <1.3 || >=2.0 && <2.1, matrix >=0.3.5 && <0.4, svg-tree >=0.6 && <0.7,- gitrev >=1.3.0 && <1.4+ gitrev >=1.3.0 && <1.4,+ JuicyPixels >=3.3.7 && <3.4,+ unordered-containers >=0.2.16 && <0.3 GHC-Options: -Wall default-language: Haskell2010
src/GCode.hs view
@@ -1,64 +1,154 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE StrictData #-}+ module GCode ( GCodeFlavor(..)- , defaultFlavor , toString ) where +import GHC.Generics++import Data.HashMap.Strict (HashMap)+import qualified Data.HashMap.Strict as HashMap import Data.List+import Data.Aeson (FromJSON (..)) import Text.Printf import Graphics.Path import Utils -data GCodeFlavor = GCodeFlavor { _begin :: String- , _end :: String- , _toolon :: String- , _tooloff :: String- }+import Codec.Picture (PixelRGBA8(..))+import Data.Maybe (fromMaybe)+import Data.Char (isSpace) +data GCodeColorBehavior = GCodeColorBehavior {+ before :: Maybe String+ , passes :: Maybe Int+ , parameters :: Maybe (HashMap String Float)+} deriving (Show, Generic)++instance FromJSON GCodeColorBehavior++data GCodeFlavor = GCodeFlavor {+ begin :: Maybe String+ , end :: Maybe String+ , toolon :: Maybe String+ , tooloff :: Maybe String+ , colors :: Maybe (HashMap String GCodeColorBehavior)+} deriving (Show, Generic)++instance FromJSON GCodeFlavor++data ColorState = ColorState {+ csColor :: String+ , csBefore :: Maybe String+ , csParams :: Maybe String+ , csPasses :: Int+}++trim :: String -> String+trim = dropWhileEnd isSpace . dropWhile isSpace++buildColorState :: HashMap String GCodeColorBehavior -> String -> ColorState+buildColorState colorMap color =+ ColorState color colorBefore colorParams colorPasses+ where+ colorBefore = fmap trim $ HashMap.lookup color colorMap >>= before+ colorPasses = fromMaybe 1 $ HashMap.lookup color colorMap >>= passes+ colorParams = case fromMaybe [] (HashMap.lookup color colorMap >>= parameters >>= (Just . HashMap.toList)) of+ [] -> Nothing+ params -> Just $ unwords $ map (\(w,p) -> w ++ show p) params+ defaultFlavor :: GCodeFlavor-defaultFlavor = GCodeFlavor "G17\nG90\nG0 Z1\nG0 X0 Y0" "G0 Z1" "G01 Z0 F10.00" "G00 Z1"+defaultFlavor = GCodeFlavor (Just "G17\nG90\nG0 Z1\nG0 X0 Y0") (Just "G0 Z1") (Just "G01 Z0 F10.00") (Just "G00 Z1") Nothing -toString :: GCodeFlavor -> Int -> [PathCommand] -> String-toString (GCodeFlavor begin end on off) dpi gops - = begin ++- "\n" ++ - intercalate "\n" (toString' gops (0,0) True) ++ - "\n" ++ - end ++- "\n"+toString :: Maybe GCodeFlavor -> Int -> [ColoredPath] -> String+toString mbConfig dpi cps+ = maybe [] ((++ "\n").trim) (begin config) +++ intercalate "\n" (toString' (flatten cps) (0,0) True Nothing) +++ "\n" +++ maybe [] ((++ "\n").trim) (end config) where+ config = fromMaybe defaultFlavor mbConfig++ colorMap :: HashMap String GCodeColorBehavior+ colorMap = fromMaybe HashMap.empty (colors config)++ augmentCommand mbColor cmd = maybe cmd (\params -> cmd ++ " " ++ params) (mbColor >>= csParams)++ mbToolonCommands = fmap trim (toolon config)+ mbTooloffCommands = fmap trim (tooloff config)++ toColorString Nothing = "000000"+ toColorString (Just (PixelRGBA8 r g b _)) = concatMap (printf "%02X") [r, g, b]++ flatten = concatMap (\(ColoredPath color path) -> Left (toColorString color): map Right path)+ dd :: Double dd = fromIntegral dpi mm :: Double -> Double mm px = (px * 2.54 * 10) / dd - toString' (MoveTo p@(x,y) : gs) _ False- = printf "G00 X%.4f Y%.4f" (mm x) (mm y) : toString' gs p False- toString' (MoveTo p@(x,y) : gs) _ True- = off : printf "G00 X%.4f Y%.4f" (mm x) (mm y) : toString' gs p False- toString' gs cp False- = on : toString' gs cp True- toString' (LineTo p@(x,y) : gs) _ True- = printf "G01 X%.4f Y%.4f" (mm x) (mm y) : toString' gs p True- toString' (ArcTo (ox,oy) p@(x,y) cw : gs) (cx,cy) True- = arcStr : toString' gs p True+ toString' :: [Either String PathCommand] -> (Double, Double) -> Bool -> Maybe ColorState -> [String]++ -- Move, tool off+ toString' (Right (MoveTo p@(x,y)) : gs) _ False mbCurrentColor+ = printf "G00 X%.4f Y%.4f" (mm x) (mm y) :+ toString' gs p False mbCurrentColor++ -- Switch off tool before move+ toString' (Right (MoveTo p@(x,y)) : gs) _ True mbCurrentColor+ = let cont = printf "G00 X%.4f Y%.4f" (mm x) (mm y) : toString' gs p False mbCurrentColor+ in case mbTooloffCommands of+ Nothing -> cont+ Just tooloffCommands -> tooloffCommands : cont++ -- Switch on tool before drawing+ toString' gs cp False mbCurrentColor+ = let cont = toString' gs cp True mbCurrentColor+ in case mbToolonCommands of+ Nothing -> cont+ Just toolonCommands -> toolonCommands : cont++ -- Drawing line+ toString' (Right (LineTo p@(x,y)) : gs) _ True mbCurrentColor+ = augmentCommand mbCurrentColor (printf "G01 X%.4f Y%.4f" (mm x) (mm y)) :+ toString' gs p True mbCurrentColor++ -- Drawing arc+ toString' (Right (ArcTo (ox,oy) p@(x,y) cw) : gs) (cx,cy) True mbCurrentColor+ = arcStr : toString' gs p True mbCurrentColor where i = ox - cx j = oy - cy cmd = if' cw "G03" "G02" - arcStr = printf "%s X%.4f Y%.4f I%.4f J%.4f" cmd (mm x) (mm y) (mm i) (mm j)- toString' (BezierTo (c1x,c1y) (c2x,c2y) p2@(p2x,p2y) : gs) (p1x,p1y) True- = bStr : toString' gs p2 True+ arcStr = augmentCommand mbCurrentColor $+ printf "%s X%.4f Y%.4f I%.4f J%.4f" cmd (mm x) (mm y) (mm i) (mm j)++ -- Drawing bezier+ toString' (Right (BezierTo (c1x,c1y) (c2x,c2y) p2@(p2x,p2y)) : gs) (p1x,p1y) True mbCurrentColor+ = bStr : toString' gs p2 True mbCurrentColor where i = c1x - p1x j = c1y - p1y p = c2x - p2x q = c2y - p2y - bStr = printf "G05 I%.4f J%.4f P%.4f Q%.4f X%.4f Y%.4f"- (mm i) (mm j) (mm p) (mm q) (mm p2x) (mm p2y)+ bStr = augmentCommand mbCurrentColor $+ printf "G05 I%.4f J%.4f P%.4f Q%.4f X%.4f Y%.4f" (mm i) (mm j) (mm p) (mm q) (mm p2x) (mm p2y) - toString' [] _ _ = []+ -- Potential color change, no current color+ toString' (Left newColor : gs) p drawing mbCurrentColor+ | fmap csColor mbCurrentColor == Just newColor+ = toString' gs p drawing mbCurrentColor+ | otherwise+ = let cont = toString' gs p drawing (Just newColorState)+ in case csBefore newColorState of+ Nothing -> cont+ Just beforeCmd -> beforeCmd : cont+ where+ newColorState = buildColorState colorMap newColor++ toString' [] _ _ _ = []
src/Graphics/Path.hs view
@@ -1,8 +1,12 @@ module Graphics.Path ( - PathCommand(..)+ PathCommand(..),+ ColoredPath(..) ) where import Graphics.Point+import Codec.Picture (PixelRGBA8 (..))++data ColoredPath = ColoredPath (Maybe PixelRGBA8) [PathCommand] -- all of them are invariant under affine transformation data PathCommand
src/Main.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE TemplateHaskell #-} {-# OPTIONS_GHC -Wno-unrecognised-pragmas #-} {-# HLINT ignore "Use lambda-case" #-}+{-# HLINT ignore "Use tuple-section" #-} import qualified Graphics.Svg as SVG @@ -9,12 +10,11 @@ import Data.Version (showVersion) import Development.GitRev (gitHash) -import Data.Text (Text, pack, unpack, replace)-import qualified Data.Configurator as C+import Data.Maybe (fromMaybe)+import qualified Data.Yaml as Y import Render import GCode-import Data.Maybe (fromMaybe) data Options = Options { _svgfile :: String , _cfgfile :: Maybe String@@ -68,24 +68,12 @@ runWithOptions (Options svgFile mbCfg mbOut dpi resolution mbApproximation) = do mbDoc <- SVG.loadSvgFile svgFile- flavor <- maybe (return defaultFlavor) readFlavor mbCfg+ mbConfig <- maybe (return Nothing) (fmap Just . Y.decodeFileThrow) mbCfg case mbDoc of- (Just doc) -> writer (toString flavor dpi $ renderDoc (fromMaybe BiArc mbApproximation) dpi resolution doc)+ (Just doc) -> writer (toString mbConfig dpi $ renderDoc (fromMaybe BiArc mbApproximation) dpi resolution doc) Nothing -> putStrLn "juicy-gcode: error during opening the SVG file" where writer = maybe putStr writeFile mbOut--toLines :: Text -> String-toLines t = unpack $ replace (pack ";") (pack "\n") t--readFlavor :: FilePath -> IO GCodeFlavor-readFlavor cfgFile = do- cfg <- C.load [C.Required cfgFile]- begin <- C.require cfg (pack "gcode.begin")- end <- C.require cfg (pack "gcode.end")- toolon <- C.require cfg (pack "gcode.toolon")- tooloff <- C.require cfg (pack "gcode.tooloff")- return $ GCodeFlavor (toLines begin) (toLines end) (toLines toolon) (toLines tooloff) versionOption :: Parser (a -> a) versionOption = infoOption
src/Render.hs view
@@ -3,10 +3,16 @@ renderDoc ) where ++import Codec.Picture (PixelRGBA8(..))+ import Data.Maybe ( fromMaybe )+import Data.Monoid import qualified Graphics.Svg as SVG import qualified Graphics.Svg.CssTypes as CSS+import Graphics.Svg.Types (DrawAttributes)+ import qualified Linear import Graphics.Path@@ -86,15 +92,16 @@ data Approximation = BiArc | CubicBezier | Linear -renderDoc :: Approximation -> Int -> Double -> SVG.Document -> [PathCommand]+renderDoc :: Approximation -> Int -> Double -> SVG.Document -> [ColoredPath] renderDoc approximation dpi resolution doc = stage2 $ renderTrees (docTransform dpi doc) (SVG._elements doc) where pxresolution = fromIntegral dpi / 2.45 / 10 * resolution -- TODO: make it tail recursive- stage2 :: [PathCommand] -> [PathCommand]- stage2 dops = approximate dops (Linear.V2 0 0)+ stage2 :: [ColoredPath] -> [ColoredPath]+ stage2 = map (\(ColoredPath color path) -> + ColoredPath color (approximate path (Linear.V2 0 0))) where approximate [] _ = [] approximate (MoveTo p:ds) _ = MoveTo p : approximate ds (fromPoint p)@@ -217,64 +224,84 @@ renderPathCommands _ _ _ _ = [] - renderTree :: TransformationMatrix -> SVG.Tree -> [PathCommand]+ extractColor :: DrawAttributes -> Maybe PixelRGBA8+ extractColor attr = case getLast $ SVG._strokeColor attr of+ (Just (SVG.ColorRef rgba8)) -> Just rgba8+ _ -> Nothing++ renderTree :: TransformationMatrix -> SVG.Tree -> [ColoredPath] renderTree m (SVG.GroupTree g) = renderTrees (applyTransformations m (SVG._transform (SVG._groupDrawAttributes g))) (SVG._groupChildren g)- renderTree m (SVG.PathTree p) = map (transform tr) $ renderPathCommands (0,0) (0,0) Nothing (SVG._pathDefinition p)+ renderTree m (SVG.PathTree p) + = [ColoredPath color + (map (transform tr) $ renderPathCommands (0,0) (0,0) Nothing (SVG._pathDefinition p))] where+ color = extractColor (SVG._pathDrawAttributes p) tr = applyTransformations m (SVG._transform (SVG._pathDrawAttributes p)) renderTree m (SVG.RectangleTree r) | rx == 0.0 && ry == 0.0- = map (transform tr) [MoveTo (x,y), LineTo (x+w,y), LineTo (x+w,y+h), LineTo (x,y+h), LineTo (x,y)]+ = [ColoredPath color+ (map (transform tr) [MoveTo (x,y), LineTo (x+w,y), LineTo (x+w,y+h), LineTo (x,y+h), LineTo (x,y)])] | otherwise- = map (transform tr)+ = [ColoredPath color+ (map (transform tr) ([MoveTo (x,y+ry)] ++ convertSvgArc (x,y+ry) rx ry 0 False True (x+rx, y) ++ [LineTo (x+w-rx,y)] ++ convertSvgArc (x+w-rx,y) rx ry 0 False True (x+w, y+ry) ++ [LineTo (x+w,y+h-ry)] ++ convertSvgArc (x+w,y+h-ry) rx ry 0 False True (x+w-rx, y+h) ++ [LineTo (x+rx,y+h)] ++ convertSvgArc (x+rx, y+h) rx ry 0 False True (x, y+h-ry) ++- [LineTo (x,y+ry)])+ [LineTo (x,y+ry)]))] where+ color = extractColor (SVG._rectDrawAttributes r) (x,y) = fromSvgPoint dpi (SVG._rectUpperLeftCorner r) w = fromSvgNumber dpi (SVG._rectWidth r) h = fromSvgNumber dpi (SVG._rectHeight r) (rx, ry) = mapTuple (fromSvgNumber dpi) (SVG._rectCornerRadius r) tr = applyTransformations m (SVG._transform (SVG._rectDrawAttributes r)) - renderTree m (SVG.LineTree l) = [MoveTo p1, LineTo p2]+ renderTree m (SVG.LineTree l) = [ColoredPath color [MoveTo p1, LineTo p2]] where+ color = extractColor (SVG._lineDrawAttributes l) p1 = transform tr (fromSvgPoint dpi (SVG._linePoint1 l)) p2 = transform tr (fromSvgPoint dpi (SVG._linePoint2 l)) tr = applyTransformations m (SVG._transform (SVG._lineDrawAttributes l)) - renderTree m (SVG.PolyLineTree l) = map (transform tr) (MoveTo p0:map LineTo ps)+ renderTree m (SVG.PolyLineTree l) + = [ColoredPath color (map (transform tr) (MoveTo p0:map LineTo ps))] where+ color = extractColor (SVG._polyLineDrawAttributes l) (p0:ps) = map (\(Linear.V2 x y) -> (x,y)) (SVG._polyLinePoints l) tr = applyTransformations m (SVG._transform (SVG._polyLineDrawAttributes l)) - renderTree m (SVG.PolygonTree l) = map (transform tr) (MoveTo p0:map LineTo (ps ++ [p0]))+ renderTree m (SVG.PolygonTree l) + = [ColoredPath color (map (transform tr) (MoveTo p0:map LineTo (ps ++ [p0])))] where+ color = extractColor (SVG._polygonDrawAttributes l) (p0:ps) = map (\(Linear.V2 x y) -> (x,y)) (SVG._polygonPoints l) tr = applyTransformations m (SVG._transform (SVG._polygonDrawAttributes l)) - renderTree m (SVG.EllipseTree e) = map (transform tr) (MoveTo (cx-rx,cy) : bs1++bs2++bs3++bs4)+ renderTree m (SVG.EllipseTree e) + = [ColoredPath color (map (transform tr) (MoveTo (cx-rx,cy) : bs1++bs2++bs3++bs4))] where bs1 = convertSvgArc (cx-rx, cy) rx ry 0 False True (cx, cy-ry) bs2 = convertSvgArc (cx, cy-ry) rx ry 0 False True (cx+rx, cy) bs3 = convertSvgArc (cx+rx, cy) rx ry 0 False True (cx, cy+ry) bs4 = convertSvgArc (cx, cy+ry) rx ry 0 False True (cx-rx, cy) + color = extractColor (SVG._ellipseDrawAttributes e) (cx,cy) = fromSvgPoint dpi (SVG._ellipseCenter e) rx = fromSvgNumber dpi (SVG._ellipseXRadius e) ry = fromSvgNumber dpi (SVG._ellipseYRadius e) tr = applyTransformations m (SVG._transform (SVG._ellipseDrawAttributes e)) - renderTree m (SVG.CircleTree c) = map (transform tr) (MoveTo (cx-r,cy) : bs1++bs2++bs3++bs4)+ renderTree m (SVG.CircleTree c)+ = [ColoredPath color (map (transform tr) (MoveTo (cx-r,cy) : bs1++bs2++bs3++bs4))] where bs1 = convertSvgArc (cx-r, cy) r r 0 False True (cx, cy-r) bs2 = convertSvgArc (cx, cy-r) r r 0 False True (cx+r, cy) bs3 = convertSvgArc (cx+r, cy) r r 0 False True (cx, cy+r) bs4 = convertSvgArc (cx, cy+r) r r 0 False True (cx-r, cy) + color = extractColor (SVG._circleDrawAttributes c) (cx,cy) = fromSvgPoint dpi (SVG._circleCenter c) r = fromSvgNumber dpi (SVG._circleRadius c) tr = applyTransformations m (SVG._transform (SVG._circleDrawAttributes c))@@ -282,5 +309,5 @@ {- The rest: None, UseTree, SymbolTree, TextTree, ImageTree -} renderTree _ _ = [] - renderTrees :: TransformationMatrix -> [SVG.Tree] -> [PathCommand]+ renderTrees :: TransformationMatrix -> [SVG.Tree] -> [ColoredPath] renderTrees m es = concatMap (renderTree m) es