module Graphics.Implicit.Export.PolylineFormats where
import Prelude((.), ($), (), minimum, maximum, unzip, concat, show, (++), unwords, map, mapM_, snd, compare, min, max, Ord, Num)
import Graphics.Implicit.Definitions (Polyline, ℝ2)
import Graphics.Implicit.Export.TextBuilderUtils (Text, Builder, mempty, toLazyText, mconcat, bf, (<>), buildTruncFloat)
import Text.Blaze.Svg.Renderer.Text (renderSvg)
import Text.Blaze.Svg11 ((!),docTypeSvg,g,polyline,toValue)
import Text.Blaze.Internal (stringValue)
import qualified Text.Blaze.Svg11.Attributes as A
import qualified Data.List as List
svg :: [Polyline] -> Text
svg plines = renderSvg . svg11 . svg' $ plines
where
(xmin, xmax, ymin, ymax) = (minimum xs, maximum xs, minimum ys, maximum ys)
where (xs,ys) = unzip (concat plines)
svg11 content = docTypeSvg ! A.version "1.1"
! A.width (stringValue $ show (xmaxxmin) ++ "mm")
! A.height (stringValue $ show (ymaxymin) ++ "mm")
! A.viewbox (stringValue $ unwords . map show $ [0,0,xmaxxmin,ymaxymin])
$ content
svg' [] = mempty
svg' polylines = thinBlueGroup $ mapM_ poly polylines
poly line = polyline ! A.points pointList
where pointList = toValue $ toLazyText $ mconcat [bf (xxmin) <> "," <> bf (ymax y) <> " " | (x,y) <- line]
thinBlueGroup = g ! A.stroke "rgb(0,0,255)" ! A.strokeWidth "1" ! A.fill "none"
hacklabLaserGCode :: [Polyline] -> Text
hacklabLaserGCode polylines = toLazyText $ gcodeHeader <> mconcat (map interpretPolyline orderedPolylines) <> gcodeFooter
where
orderedPolylines =
snd . unzip
. List.sortBy (\(a,_) (b, _) -> compare a b)
. map (\x -> (polylineRadius x, x))
$ polylines
polylineRadius :: forall t. (Ord t, Num t) => [(t, t)] -> t
polylineRadius [] = 0
polylineRadius polyline' = max (xmax' xmin') (ymax' ymin') where
((xmin', xmax'), (ymin', ymax')) = polylineRadius' polyline'
polylineRadius' :: forall a a1. (Ord a1, Ord a, Num a1, Num a) => [(a, a1)] -> ((a, a), (a1, a1))
polylineRadius' [] = ((0,0),(0,0))
polylineRadius' [(x,y)] = ((x,x),(y,y))
polylineRadius' ((x,y):ps) = ((min x xmin,max x xmax),(min y ymin, max y ymax))
where ((xmin, xmax), (ymin, ymax)) = polylineRadius' ps
gcodeHeader :: Builder
gcodeHeader = mconcat [
"(generated by ImplicitCAD, based of hacklab wiki example)\n"
,"M63 P0 (laser off)\n"
,"G0 Z0.002 (laser off)\n"
,"G21 (units=mm)\n"
,"F400 (set feedrate)\n"
,"M3 S1 (enable laser)\n\n"]
gcodeFooter :: Builder
gcodeFooter = mconcat [
"M5 (disable laser)\n"
,"G00 X0.0 Y0.0 (move to 0)\n"
,"M2 (end)"]
gcodeXY :: ℝ2 -> Builder
gcodeXY (x,y) = mconcat ["X", buildTruncFloat x, " Y", buildTruncFloat y]
interpretPolyline (start:others) = mconcat [
"G00 ", gcodeXY start
,"\nM62 P0 (laser on)\n"
,mconcat [ "G01 " <> gcodeXY point <> "\n" | point <- others]
,"M63 P0 (laser off)\n\n"
]
interpretPolyline [] = mempty