{-| Various utilities for drawing axes with Cairo that will later be rendered using @Graphics.DynamicGraph.RenderCairo@ -} module Graphics.DynamicGraph.Axis ( blankCanvas, blankCanvasAlpha, drawAxes, gridXCoords, gridYCoords, xAxisLabels, yAxisLabels, xAxisGrid, yAxisGrid ) where import Control.Monad import Data.Colour.RGBSpace import Data.Colour.SRGB import Data.Colour.Names import Graphics.Rendering.Cairo import Graphics.Rendering.Pango -- | Make a pango layout, fill it with text and return its extents makeLayout :: PangoContext -- ^ Pango context -> String -- ^ The text -> Render (PangoLayout, PangoRectangle) makeLayout ctx text = liftIO $ do layout <- layoutEmpty ctx layoutSetMarkup layout text :: IO String (_, rect) <- layoutGetExtents layout return (layout, rect) -- | Draw some text in the top center layoutTopCentre :: PangoContext -- ^ Pango context -> String -- ^ The text -> Double -- ^ Width -> Double -- ^ Height -> Render () layoutTopCentre ctx text x y = do (layout, PangoRectangle _ _ w _) <- makeLayout ctx text moveTo (x - w/2) y showLayout layout -- | Draw some text right side half way up layoutRightCentre :: PangoContext -- ^ Pango context -> String -- ^ The text -> Double -- ^ Width -> Double -- ^ Height -> Render () layoutRightCentre ctx text x y = do (layout, PangoRectangle _ _ w h) <- makeLayout ctx text moveTo (x - w) (y - h/2) showLayout layout -- | Create a blank cairo canvas of the specified size and colour blankCanvas :: Colour Double -- ^ The colour -> Double -- ^ Width -> Double -- ^ Height -> Render () blankCanvas colour width height = do uncurryRGB setSourceRGB (toSRGB colour) rectangle 0 0 width height fill -- | Create a blank cairo canvas of the specified size and colour blankCanvasAlpha :: Colour Double -- ^ The colour -> Double -- ^ Transparency -> Double -- ^ Width -> Double -- ^ Height -> Render () blankCanvasAlpha colour alpha width height = do uncurryRGB (\x y z -> setSourceRGBA x y z alpha) (toSRGB colour) rectangle 0 0 width height fill -- | Draw a set of axes without any labels drawAxes :: Double -- ^ Width -> Double -- ^ Height -> Double -- ^ Top Margin -> Double -- ^ Bottom Margin -> Double -- ^ Left Margin -> Double -- ^ Right Margin -> Colour Double -- ^ Axis colour -> Double -- ^ Axis width -> Render () drawAxes width height topMargin bottomMargin leftMargin rightMargin axisColor axisWidth = do setDash [] 0 setLineCap LineCapRound setLineJoin LineJoinRound setLineWidth axisWidth uncurryRGB setSourceRGB (toSRGB axisColor) --Y axis moveTo leftMargin topMargin lineTo leftMargin (height - bottomMargin) stroke --X axis moveTo leftMargin (height - bottomMargin) lineTo (width - rightMargin) (height - bottomMargin) stroke -- | Calculate the coordinates to draw the X axis grid at gridXCoords :: Double -- ^ Width of graph -> Double -- ^ X offset to start at -> Double -- ^ Left margin -> Double -- ^ Right margin -> Double -- ^ Spacing between coordinates -> [Double] gridXCoords width offset leftMargin rightMargin spacing = takeWhile (<= (width - rightMargin)) $ iterate (+ spacing) (offset + leftMargin) -- | Calculate the coordinates to draw the Y axis grid at gridYCoords :: Double -- ^ Height of graph -> Double -- ^ Y offset to start at -> Double -- ^ Top margin -> Double -- ^ Bottom margin -> Double -- ^ Spacing between coordinates -> [Double] gridYCoords height offset topMargin bottomMargin spacing = takeWhile (>= topMargin) $ iterate (flip (-) spacing) (height - bottomMargin - offset) -- | Draw X axis labels xAxisLabels :: PangoContext -- ^ Pango context -> Colour Double -- ^ Label colour -> [String] -- ^ Grid labels -> [Double] -- ^ X coordinates to draw labels at -> Double -- ^ Y coordinate to draw labels at -> Render () xAxisLabels ctx textColor gridLabels gridXCoords yCoord = do uncurryRGB setSourceRGB (toSRGB textColor) forM_ (zip gridLabels gridXCoords) $ \(label, xCoord) -> layoutTopCentre ctx label xCoord yCoord -- | Draw Y axis labels yAxisLabels :: PangoContext -- ^ Pango context -> Colour Double -- ^ Label colour -> [String] -- ^ Grid label -> [Double] -- ^ Y coordinates to draw labels at -> Double -- ^ X coordinate to draw labels at -> Render () yAxisLabels ctx textColor gridLabels gridYCoords xCoord = do uncurryRGB setSourceRGB (toSRGB textColor) forM_ (zip gridLabels gridYCoords) $ \(label, yCoord) -> layoutRightCentre ctx label xCoord yCoord -- | Draw X axis grid xAxisGrid :: Colour Double -- ^ Grid colour -> Double -- ^ Width of grid lines -> [Double] -- ^ Grid line dashing -> Double -- ^ Starting Y coordinate -> Double -- ^ Ending Y coordinate -> [Double] -- ^ Grid X coordinates -> Render () xAxisGrid gridColor gridWidth gridDash yStart yEnd gridXCoords = do uncurryRGB setSourceRGB (toSRGB gridColor) setLineWidth gridWidth setDash gridDash 0 forM_ gridXCoords $ \xCoord -> do moveTo xCoord yStart lineTo xCoord yEnd stroke -- | Draw Y axis grid yAxisGrid :: Colour Double -- ^ Grid color -> Double -- ^ Width of grid lines -> [Double] -- ^ Grid line dashing -> Double -- ^ Starting X coordinate -> Double -- ^ Ending X coordinate -> [Double] -- ^ Grid Y coordinates -> Render () yAxisGrid gridColor gridWidth gridDash xStart xEnd gridYCoords = do uncurryRGB setSourceRGB (toSRGB gridColor) setLineWidth gridWidth setDash gridDash 0 forM_ gridYCoords $ \yCoord -> do moveTo xStart yCoord lineTo xEnd yCoord stroke