packages feed

tracetree-0.1.0.0: ttrender/Debug/Trace/Tree/Render/Simple.hs

-- | Render simple trees
module Debug.Trace.Tree.Render.Simple (renderTree) where

import Diagrams.Prelude
import Diagrams.Backend.Cairo (B)
import Graphics.SVGFonts
import Graphics.SVGFonts.ReadFont (PreparedFont)
import Text.Regex.Posix ((=~))

import Debug.Trace.Tree.Simple
import Debug.Trace.Tree.Render.Options
import Debug.Trace.Tree.Render.Constants
import qualified Debug.Trace.Tree.Render.Edged as Edged

renderTree :: RenderOptions -> SimpleTree -> Diagram B
renderTree options@RenderOptions{..} =
      Edged.renderTree drawEdgeLabel (drawNode options) renderShowCoords
    . applyOptions options

drawNode :: RenderOptions -> Maybe String -> Diagram B
drawNode RenderOptions{..} node =
      maybeVertical node
    $ makeBox node
  where
    makeBox (Just str) = box str (lookup str renderColours)
    makeBox Nothing    = mempty

    maybeVertical Nothing    = id
    maybeVertical (Just str) = if any (\regexp -> str =~ regexp) renderVertical
                                 then rotateBy (1/4)
                                 else id

drawEdgeLabel :: Maybe String -> Maybe String -> String -> (Diagram B, ArrowOpts Double)
drawEdgeLabel _ Nothing str = (
    stroke (textSVG' (textOpts constEdgeLabelHeight bit) str) # fc black # lw none
  , arrOpts & shaftStyle %~ dashingG [constMissingNodeDash, constMissingNodeDash] 0
            & headTexture .~ solid grey
  )
drawEdgeLabel _ (Just _) str = (
    stroke (textSVG' (textOpts constEdgeLabelHeight bit) str) # fc black # lw none
  , arrOpts
  )

-- NOTE: global sizes are relative to the final vector space of the diagram,
-- and are not affected by scaling. We don't want to use normalized because
-- normalized values depend on the size of the final diagram, and the size of
-- the diagram may very greatly depending on the size of the tree.
-- See Section 2.5, "Measurement units" of the diagrams manual.
arrOpts :: ArrowOpts Double
arrOpts = with & headLength .~ global constArrowHeadLength
               & shaftStyle %~ lw (global constArrowLineWidth) . lc grey

textOpts :: Double -> PreparedFont Double -> TextOpts Double
textOpts h fnt = with {
    textFont   = fnt
  , textHeight = h
  }

box :: String -> Maybe (Colour Double) -> Diagram B
box str mc = mconcat [
      stroke txt # fc black # lw none
    , boundingBoxRect bb # setBg mc # lc black # lw (global 1)
    ]
  where
    txt :: Path V2 Double
    txt = textSVG' (textOpts constNodeLabelHeight lin) str

    bb :: BoundingBox V2 Double
    bb = boundingBoxGrow constNodePadding (boundingBox txt) <> boundingBox minBB

    minBB :: Diagram B
    minBB = square constNodeLabelHeight

    setBg (Just c) = fc c
    setBg Nothing  = id

{-------------------------------------------------------------------------------
  Auxiliary: operations on bounding boxes
-------------------------------------------------------------------------------}

boundingBoxGrow :: Double -> BoundingBox V2 Double -> (BoundingBox V2 Double)
boundingBoxGrow padding bb =
    fromCorners (p2 (x  - padding, y  - padding))
                (p2 (x' + padding, y' + padding))
  where
    (unp2 -> (x, y), unp2 -> (x', y')) = getCorners' bb

boundingBoxRect :: BoundingBox V2 Double -> Diagram B
boundingBoxRect = uncurry rect . boundingBoxSize

boundingBoxSize :: Num a => BoundingBox V2 a -> (a, a)
boundingBoxSize bb = ((x' - x), (y' - y))
  where
    (unp2 -> (x, y), unp2 -> (x', y')) = getCorners' bb

getCorners' :: Num a => BoundingBox V2 a -> (Point V2 a, Point V2 a)
getCorners' bb =
    case getCorners bb of
      Just (ll, ur) -> (ll     , ur    )
      Nothing       -> (origin , origin)