diagrams 0.2 → 0.2.1
raw patch · 10 files changed
+224/−69 lines, 10 filesdep ~basedep ~cairodep ~colour
Dependency ranges changed: base, cairo, colour, containers, mtl
Files
- CHANGELOG +0/−14
- CHANGES +23/−0
- Graphics/Rendering/Diagrams.hs +20/−3
- Graphics/Rendering/Diagrams/Engine.hs +54/−5
- Graphics/Rendering/Diagrams/Layouts.hs +46/−0
- Graphics/Rendering/Diagrams/Paths.hs +11/−0
- Graphics/Rendering/Diagrams/Shapes.hs +50/−25
- Graphics/Rendering/Diagrams/Types.hs +13/−17
- LICENSE +1/−1
- diagrams.cabal +6/−4
− CHANGELOG
@@ -1,14 +0,0 @@-New in version 0.2:-- * rawCairo function for creating primitive diagrams from raw Cairo code- * line join, cap, and dashing attributes- * bouding box visualization functions for debugging- * new padding and positioning combinators- * PDF, PS, and SVG output- * experimental tree layout- * paths, polygons, bezier curves- * create diagrams from text- * render on top of arbitrary background images- * depend on colour package instead of built-in colors- * new examples: snellen, wordcloud, dragon- * emptyPath and pathConcat
+ CHANGES view
@@ -0,0 +1,23 @@+Version 0.2.1: September 24, 2009++ * new shape primitive 'arc'+ * multi-page PS/PDF output (Ganesh Sittampalam)+ * new grid layout combinators (Ganesh Sittampalam)+ * new primitives for generating rounded rectangles and rectangular paths+ * more path functions, and new path layout 'positionAlong'+ * various bug fixes and minor improvements++Version 0.2: January 31, 2009++ * rawCairo function for creating primitive diagrams from raw Cairo code+ * line join, cap, and dashing attributes+ * bouding box visualization functions for debugging+ * new padding and positioning combinators+ * PDF, PS, and SVG output+ * experimental tree layout+ * paths, polygons, bezier curves+ * create diagrams from text+ * render on top of arbitrary background images+ * depend on colour package instead of built-in colors+ * new examples: snellen, wordcloud, dragon+ * emptyPath and pathConcat
Graphics/Rendering/Diagrams.hs view
@@ -28,8 +28,11 @@ -- ** Shapes , circle+ , arc+ , rectPath , rect , roundRect+ , roundRectF , regPolyPath , regPoly@@ -51,6 +54,7 @@ , Path , emptyPath , pathFromVertices, pathFromVectors+ , pathToVertices, pathToVectors , pathConcat , closed, isClosed , rotPath@@ -76,7 +80,10 @@ , hdistribA, vdistribA , position, positionA+ , positionAlong, positionAlongA + , grid, gridA, gridAs+ , VAlignment , top, vcenter, bottom , HAlignment@@ -126,6 +133,7 @@ -- $render , renderAs+ , renderPagesAs , OutputType(..) , SizeSpec(..) , renderOverPNG@@ -240,9 +248,10 @@ -- $render -- Rendering diagrams to a file is accomplished with the 'renderAs'--- function. 'renderOverPNG' also provides a specialized rendering--- method, which overlays a rendererd diagram on top of an existing--- PNG.+-- function. 'renderPagesAs' renders multiple diagrams as multiple+-- pages for suitable backends. 'renderOverPNG' also provides a+-- specialized rendering method, which overlays a rendererd diagram on+-- top of an existing PNG. -- | Render a diagram to a file. renderAs :: OutputType -- ^ The output type to use (PNG, PS, PDF, or SVG)@@ -251,6 +260,14 @@ -> Diagram -- ^ The diagram to render. -> IO () renderAs otype = renderWithBackend (chooseBackend otype)++-- | Render a list of diagrams as separate pages to a file.+renderPagesAs :: OutputType -- ^ The output type to use (PS or PDF)+ -> String -- ^ The name of the file to create.+ -> SizeSpec -- ^ The desired width or height of the image.+ -> [Diagram] -- ^ The diagram to render.+ -> IO ()+renderPagesAs otype = renderPagesWithBackend (choosePagesBackend otype) -- | @renderOverPNG infile outfile d@ reads the PNG @infile@ -- and renders @d@ over this, saving the output as a PNG to @outfile@.
Graphics/Rendering/Diagrams/Engine.hs view
@@ -31,6 +31,10 @@ , renderOverlayPNG , chooseBackend + , writePagesPS, writePagesPDF+ , renderPagesWithBackend+ , choosePagesBackend+ -- ** Internals , atomic@@ -42,6 +46,7 @@ import qualified Graphics.Rendering.Cairo as C import Control.Monad.Reader+import Data.List (intersperse) -- $preproc -- These functions take a user-generated 'Diagram' object and@@ -106,11 +111,18 @@ -- scale and translate the output so that it fits within the target -- output width and height. let ((x,y), d') = sizeAndPos d- s = case size of- Width w -> w/x -- scale so width is as desired- Height h -> h/y -- scale so height is as desired- Auto -> 1 -- do not scale- in (,) (s*x,s*y) $ do+ (s,sx,sy) = case (size,x,y) of+ (Width w,0,0) -> (1,w,w)+ (Height h,0,0) -> (1,h,h)+ (Width w,0,_) -> (1,w,y)+ (Height h,_,0) -> (1,x,h)+ (Width w,_,_) -> (w/x, w, y * w/x)+ (Height h,_,_) -> (h/y, x * h/y, h)+ (Auto, 0,0) -> (1,1,1)+ (Auto, 0,_) -> (1,y,y)+ (Auto, _,0) -> (1,x,x)+ (Auto, _,_) -> (1,x,y)+ in (,) (sx, sy) $ do C.scale s s C.translate (x/2) (y/2) @@ -154,16 +166,36 @@ withSurface fileName w h $ \surface -> C.renderWith surface r +writePagesSurface :: (String -> Double -> Double -> (C.Surface -> IO ()) -> IO ())+ -> (C.Surface -> Double -> Double -> C.Render ())+ -> String+ -> [(Point, C.Render ())]+ -> IO ()+writePagesSurface withSurface surfaceSetSize fileName pages =+ withSurface fileName 0 0 $ \surface -> C.renderWith surface $+ sequence_ $ concat $ intersperse [C.showPage] $+ [[C.save, surfaceSetSize surface w h, r, C.restore] | ((w,h),r) <- pages]+ -- | Given a rendered diagram, output it to a file in PostScript -- format with the given width and height. writePS :: String -> Point -> C.Render () -> IO () writePS = writeSurface C.withPSSurface +-- | Given a list of rendered diagrams with their height and width,+-- output them as separate pages to a file in PostScript format+writePagesPS :: String -> [(Point, C.Render ())] -> IO ()+writePagesPS = writePagesSurface C.withPSSurface C.psSurfaceSetSize+ -- | Given a rendered diagram, output it to a file in PDF -- format with the given width and height. writePDF :: String -> Point -> C.Render () -> IO () writePDF = writeSurface C.withPDFSurface +-- | Given a list of rendered diagrams with their height and width,+-- output them as separate pages to a file in DFt format+writePagesPDF :: String -> [(Point, C.Render ())] -> IO ()+writePagesPDF = writePagesSurface C.withPDFSurface C.pdfSurfaceSetSize+ -- | Given a rendered diagram, output it to a file in SVG -- format with the given width and height. writeSVG :: String -> Point -> C.Render () -> IO ()@@ -180,6 +212,17 @@ renderWithBackend backend name size dia = backend name wh r where (wh, r) = compose size dia +-- | Given a file name, an output size specification, and a list of+-- 'Diagram's, use a \"backend\" to render the 'Diagram's as separate+-- pages to an actual physical output.+renderPagesWithBackend :: (String -> [(Point, C.Render ())] -> IO ())+ -> String+ -> SizeSpec+ -> [Diagram]+ -> IO ()+renderPagesWithBackend backend name size dias+ = backend name (map (compose size) dias)+ renderOverlayPNG :: FilePath -> FilePath -> Diagram -> IO () renderOverlayPNG srcfile dstfile dia = overlayPNG srcfile dstfile r where r = snd $ compose' 0 Auto dia@@ -189,6 +232,12 @@ chooseBackend PS = writePS chooseBackend PDF = writePDF chooseBackend SVG = writeSVG++choosePagesBackend :: OutputType -> (String -> [(Point, C.Render ())] -> IO ())+choosePagesBackend PS = writePagesPS+choosePagesBackend PDF = writePagesPDF+choosePagesBackend PNG = error "PNG doesn't support multiple pages"+choosePagesBackend SVG = error "SVG doesn't support multiple pages" -- | Perform a rendering operation atomically, by saving the state and -- restoring it afterwards.
Graphics/Rendering/Diagrams/Layouts.hs view
@@ -30,7 +30,10 @@ , hdistribA, vdistribA , position, positionA+ , positionAlong, positionAlongA + , grid, gridA, gridAs+ , VAlignment , top, vcenter, bottom , HAlignment@@ -51,9 +54,12 @@ import Graphics.Rendering.Diagrams.Types import Graphics.Rendering.Diagrams.Attributes import Graphics.Rendering.Diagrams.Shapes+import Graphics.Rendering.Diagrams.Paths import Graphics.Rendering.Diagrams.Engine (sizeAndPos) import Control.Arrow (first)+import Control.Monad.Cont +import Data.List (transpose) import Data.Tree -- Union -----------------------------------------------@@ -377,7 +383,17 @@ positionA ha va pds = Compound (Layout (Positioned ps ha va) ds) where (ps, ds) = unzip pds +-- | Create a diagram from a list of subdiagrams and a given path, by+-- positioning the subdiagrams at successive vertices of the path.+-- If there are more diagrams than path vertices, the extra diagrams+-- will be discarded.+positionAlong :: [Diagram] -> Path -> Diagram+positionAlong = positionAlongA hcenter vcenter +-- | A version of 'positionAlong' with explicit alignment.+positionAlongA :: HAlignment -> VAlignment -> [Diagram] -> Path -> Diagram+positionAlongA ha va ds p = positionA ha va $ zip (pathToVertices (0,0) p) ds+ -- Tree ------------------------------------------ -- | Lay out a 'Tree' (from "Data.Tree") of 'Diagram's in a top-down@@ -449,3 +465,33 @@ -> Diagram -- ^ Old diagram -> Diagram withSize f dia = Compound (Layout (FromSize f) (Identity dia))++diagSize :: Diagram -> Cont Diagram (Double, Double) +diagSize d = Cont { runCont = \f -> flip withSize d $ \w h -> f (w, h) } ++-- | Align diagrams into a grid, specifying individual alignments for each item.+-- Warning: there is currently an exponential performace blowup if you nest grids+-- (exponential in how deep the nesting is).+gridAs :: [[(HAlignment, VAlignment)]] -> [[Diagram]] -> Diagram++-- | Align diagrams into a grid with each item aligned as specified.+-- Warning: there is currently an exponential performace blowup if you nest grids+-- (exponential in how deep the nesting is).+gridA :: HAlignment -> VAlignment -> [[Diagram]] -> Diagram++-- | Align diagrams into a grid, with each item centered horizontally and vertically+-- Warning: there is currently an exponential performace blowup if you nest grids.+-- (exponential in how deep the nesting is).+grid :: [[Diagram]] -> Diagram++grid = gridA hcenter vcenter+gridA h v = gridAs (repeat (repeat (h, v)))+gridAs alignss diagss+ = flip runCont id $+ do sizess <- mapM (mapM diagSize) diagss+ let widths = map (maximum . map fst) (transpose sizess)+ heights = map (maximum . map snd) sizess+ newsizess = [[(w, h) | w <- widths] | h <- heights]+ adjss = zipWith (zipWith (\ (nw, nh) (ow, oh) -> (nw - ow, nh - oh))) newsizess sizess+ padTo (width, height) (halign, valign) = padA width height halign valign+ return $ vcat $ map hcat $ zipWith3 (zipWith3 padTo) adjss alignss diagss
Graphics/Rendering/Diagrams/Paths.hs view
@@ -18,6 +18,7 @@ emptyPath , pathFromVertices, pathFromVectors+ , pathToVertices, pathToVectors , pathConcat , closed, isClosed @@ -38,6 +39,11 @@ pathInt :: Point -> Path -> [Point] pathInt s (Path _ vs) = scanl (.+.) s vs +-- | Convert a path into a list of vertices, starting with the given+-- vertex.+pathToVertices :: Point -> Path -> [Point]+pathToVertices = pathInt+ -- | \"Differentiate\" a list of vertex coordinates to produce an open -- path. Calling 'pathDeriv' on an empty list will result in a -- run-time error.@@ -51,6 +57,11 @@ -- direction and four in the positive y direction. pathFromVectors :: [Vec] -> Path pathFromVectors = Path Open++-- | Convert a path to a list of vectors corresponding to the edges of+-- the path.+pathToVectors :: Path -> [Vec]+pathToVectors (Path _ vs) = vs -- | The empty path, i.e. a path with no edges. emptyPath :: Path
Graphics/Rendering/Diagrams/Shapes.hs view
@@ -13,13 +13,16 @@ ----------------------------------------------------------------------------- module Graphics.Rendering.Diagrams.Shapes ( circle+ , arc , regPolyPath , regPoly , rotRegPoly , rect+ , rectPath , roundRect+ , roundRectF , straight , curved@@ -57,18 +60,25 @@ c $ C.restore where dist (x,y) = sqrt (x*x + y*y) --- | A unit circle centered at the origin.-data Circle = Circle deriving (Eq, Show, Read)-instance ShapeClass Circle where- shapeSize _ = (2,2)- renderShape _ = do- c $ C.arc 0 0 1 0 (2*pi)+-- | A unit arc centered at the origin, with a given start and end+-- angle in radians.+data Arc = Arc Double Double deriving (Eq, Show, Read)+instance ShapeClass Arc where+ shapeSize _ = (2,2)+ renderShape (Arc a1 a2) = do+ c $ C.arc 0 0 1 a1 a2 draw -- | @circle r@ is a circle with radius @r@. circle :: Double -> Diagram-circle r = scale r $ Prim (Shape Circle)+circle r = arc r 0 1 +-- | @arc r a1 a2@ is a circular arc with radius @r@, starting at+-- angle @a1*2*pi@ and proceeding in a direction of increasing angle+-- to @a2*2*pi@.+arc :: Double -> Double -> Double -> Diagram+arc r a1 a2 = scale r $ Prim (Shape (Arc (a1*2*pi) (a2*2*pi)))+ -- | A path-based shape. data PathShape = PathShape PathStyle Path deriving (Eq, Show, Read) instance ShapeClass PathShape where@@ -120,28 +130,37 @@ rotRegPoly :: Int -> Double -> Double -> Diagram rotRegPoly n r a = straight . closed . rotPath a $ regPolyPath n r --- | @Rect w@ is a 2w (width) by 2 (height) rectangle, centered at the--- origin.-data Rect = Rect Double deriving (Eq, Show, Read)-instance ShapeClass Rect where- shapeSize (Rect w) = (2*w,2)- renderShape (Rect w) = do- c $ C.rectangle (-w) (-1) (2*w) 2- draw+-- -- | @Rect w@ is a 2w (width) by 2 (height) rectangle, centered at the+-- -- origin.+-- data Rect = Rect Double deriving (Eq, Show, Read)+-- instance ShapeClass Rect where+-- shapeSize (Rect w) = (2*w,2)+-- renderShape (Rect w) = do+-- c $ C.rectangle (-w) (-1) (2*w) 2+-- draw +-- | @rectPath w h@ is a closed path describing a rectangle of width+-- @w@ and height @h@.+rectPath :: Double -> Double -> Path+rectPath w h = closed $ pathFromVertices [(0,0), (w,0), (w,h), (0,h)]+ -- | @rect w h@ is a rectangle of width @w@ and height @h@. rect :: Double -> Double -> Diagram-rect w h = scale (h/2) $ Prim (Shape (Rect (w/h)))+rect w h = straight $ rectPath w h --- | @RoundRect w h@ is a rectangle with rounded corners.--- Both dimensions are given to avoid unnecessary--- distortion in the curves when scaling.-data RoundRect = RoundRect Double Double deriving (Eq, Show, Read)+-- -- | @rect w h@ is a rectangle of width @w@ and height @h@.+-- rect :: Double -> Double -> Diagram+-- rect w h = scale (h/2) $ Prim (Shape (Rect (w/h)))++-- | @RoundRect w h f@ is a rectangle with rounded corners taking up a+-- fraction @f@ of the smaller side. Both dimensions are given to+-- avoid unnecessary distortion in the curves when scaling.+data RoundRect = RoundRect Double Double Double deriving (Eq, Show, Read) instance ShapeClass RoundRect where- shapeSize (RoundRect w h) = (w,h)- renderShape (RoundRect w h) = do+ shapeSize (RoundRect w h _) = (w,h)+ renderShape (RoundRect w h f) = do let (x,y) = (w/2, h/2)- r = min (w/3) (h/3)+ r = min (w * f) (h * f) c $ do C.arc (r-x) (r-y) r pi (-pi/2) C.lineTo (x-r) (-y) C.arc (x-r) (r-y) r (-pi/2) 0@@ -156,8 +175,14 @@ -- rounded corners having a radius one third the length of the -- shortest edge. roundRect :: Double -> Double -> Diagram-roundRect w h = Prim $ Shape $ RoundRect w h+roundRect w h = Prim . Shape $ RoundRect w h (1/3) +-- | @roundRect w h f@ is a rectangle of width @w@ and height @h@ with+-- rounded corners having a radius @f@ times the length of the+-- shortest edge.+roundRectF :: Double -> Double -> Double -> Diagram+roundRectF w h f = Prim . Shape $ RoundRect w h f+ -- | @Text s t@ is a text string @t@ at size @s@. data Text = Text Double String deriving (Eq, Show, Read) instance ShapeClass Text where@@ -180,7 +205,7 @@ unsafeExtents :: Text -> (CI.FontExtents, CI.TextExtents) unsafeExtents (Text s t) = unsafePerformIO $ do bracket- (CI.create =<< C.createImageSurface C.FormatARGB32 1000 1000)+ (CI.create =<< C.createImageSurface C.FormatARGB32 1 1) (CI.destroy) (\cxt -> do CI.setFontSize cxt s te <- CI.textExtents cxt t
Graphics/Rendering/Diagrams/Types.hs view
@@ -53,7 +53,7 @@ import Control.Monad.Reader import Data.Colour import Data.Colour.Names (black)-import qualified Data.Colour.SRGB.Linear as RGB+import qualified Data.Colour.SRGB as RGB -- Diagrams ---------------------------------------------------------- @@ -95,33 +95,29 @@ -- require Floating and Real (so that we can convert to Double with -- fromRational . toRational), and let type defaulting figure out that -- in the expression 'lc black', black should have type Colour Double.--- This is still unsatisfactory, though---even though we know we will--- (almost) always be getting (Colour Doubles), we still run the--- values through fromRational . toRational! instance (Floating a, Real a) => Color (Colour a) where colorToRGBA col = (r,g,b,1)- where c' = RGB.toRGB col- r = toDouble $ RGB.channelRed c'- g = toDouble $ RGB.channelGreen c'- b = toDouble $ RGB.channelBlue c'+ where c' = RGB.toSRGB . colourConvert $ col+ r = RGB.channelRed c'+ g = RGB.channelGreen c'+ b = RGB.channelBlue c' instance (Floating a, Real a) => Color (AlphaColour a) where colorToRGBA col = (r,g,b,a)- where a = toDouble $ alphaChannel col- c' = RGB.toRGB $ alphaToColour col- r = toDouble $ RGB.channelRed c'- g = toDouble $ RGB.channelGreen c'- b = toDouble $ RGB.channelBlue c'--toDouble :: (Real a) => a -> Double-toDouble = fromRational . toRational+ where col' = alphaColourConvert col+ a = alphaChannel col'+ c' = RGB.toSRGB . alphaToColour $ col'+ r = RGB.channelRed c'+ g = RGB.channelGreen c'+ b = RGB.channelBlue c' instance Color SomeColor where colorToRGBA (SomeColor col) = colorToRGBA col alphaToColour :: (Floating a, Ord a, Fractional a) => AlphaColour a -> Colour a-alphaToColour ac = darken (recip (alphaChannel ac)) (ac `over` black)+alphaToColour ac | alphaChannel ac == 0 = ac `over` black+ | otherwise = darken (recip (alphaChannel ac)) (ac `over` black) -- Points ------------------------------------------------------------
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) Brent Yorgey 2008+Copyright (c) Brent Yorgey 2008-2009 All rights reserved.
diagrams.cabal view
@@ -1,5 +1,5 @@ Name: diagrams-Version: 0.2+Version: 0.2.1 Stability: experimental Description: An embedded domain-specific language (EDSL) for creating simple diagrams, built on top of the Cairo@@ -8,7 +8,7 @@ Synopsis: An EDSL for creating simple diagrams License: BSD3 License-file: LICENSE-Extra-source-files: README,CHANGELOG+Extra-source-files: README,CHANGES Author: Brent Yorgey Maintainer: byorgey@gmail.com Category: Graphics@@ -20,10 +20,12 @@ default: False library- ghc-options: -Wall+ ghc-options: -Wall if flag(testing) ghc-options: -Werror- Build-Depends: base,mtl,containers,cairo,colour+ Build-Depends: base >= 2 && < 5, mtl >= 1.0 && < 1.2,+ containers >= 0.2 && < 0.3,+ cairo >= 0.9 && < 0.11, colour >= 2.2.1 && < 2.3 Exposed-Modules: Graphics.Rendering.Diagrams, Graphics.Rendering.Diagrams.Types, Graphics.Rendering.Diagrams.Paths,