Minor improvements in graph rendering

This commit is contained in:
Rodrigo Setti 2017-08-15 23:20:10 -07:00
parent 73baeba40b
commit 41387d5308
No known key found for this signature in database
GPG Key ID: 3E2EB67B3A72ABD3
4 changed files with 54 additions and 48 deletions

View File

@ -13,7 +13,7 @@ x {
description "can we build the technology ourselves" description "can we build the technology ourselves"
} h * b; } h * b;
h hire + acquihire; h = hire + acquihire;
hire { hire {
title "hire" title "hire"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 132 KiB

View File

@ -10,7 +10,6 @@ Portability : POSIX
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NoMonomorphismRestriction #-} {-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UnicodeSyntax #-} {-# LANGUAGE UnicodeSyntax #-}
module MasterPlan.Backend.Graph (render, RenderOptions(..)) where module MasterPlan.Backend.Graph (render, RenderOptions(..)) where
@ -20,7 +19,7 @@ import Control.Monad.State
import Data.List (intersperse) import Data.List (intersperse)
import qualified Data.List.NonEmpty as NE import qualified Data.List.NonEmpty as NE
import qualified Data.Map as M import qualified Data.Map as M
import Data.Maybe (catMaybes, fromMaybe) import Data.Maybe
import Data.Tree import Data.Tree
import Diagrams.Backend.Rasterific import Diagrams.Backend.Rasterific
import Diagrams.Prelude hiding (Product, Sum, render) import Diagrams.Prelude hiding (Product, Sum, render)
@ -90,10 +89,10 @@ toRenderModel sys rootK = case M.lookup rootK (bindings sys) of
then pure $ Node (AtomicNode, NodeRef $ ProjectKey $ bindingTitle b) [] then pure $ Node (AtomicNode, NodeRef $ ProjectKey $ bindingTitle b) []
else modify (n:) >> bindingToRM n b else modify (n:) >> bindingToRM n b
-- |how many children -- |how many leaf nodes
treeSize :: Tree a -> Double leafCount :: Tree a -> Double
treeSize (Node _ []) = 1 leafCount (Node _ []) = 1
treeSize (Node _ ts) = sum $ treeSize <$> ts leafCount (Node _ ts) = sum $ leafCount <$> ts
-- |Options for rendering -- |Options for rendering
data RenderOptions = RenderOptions { colorByProgress :: Bool -- ^Whether to color boxes depending on progress data RenderOptions = RenderOptions { colorByProgress :: Bool -- ^Whether to color boxes depending on progress
@ -113,15 +112,15 @@ render fp (RenderOptions colorByP w h rootK props) sys =
renderTree :: Bool -> [ProjAttribute] -> RenderModel -> QDiagram B V2 Double Any renderTree :: Bool -> [ProjAttribute] -> RenderModel -> QDiagram B V2 Double Any
renderTree colorByP props (Node (_, n) []) = alignL $ renderNode colorByP props n renderTree colorByP props (Node (_, n) []) = alignL $ renderNode colorByP props n
renderTree colorByP props x@(Node (ty, n) ts@(t:_)) = renderTree colorByP props x@(Node (ty, n) ts@(t:_)) =
(strutY (12 * treeSize x) <> alignL (centerY $ renderNode colorByP props n)) (strutY (12 * leafCount x) <> alignL (centerY $ renderNode colorByP props n))
||| (translateX 2 typeSymbol # withEnvelope (mempty :: D V2 Double) <> hrule 4 # lwO 2) ||| (translateX 2 typeSymbol # withEnvelope (mempty :: D V2 Double) <> hrule 4 # lwO 2)
||| centerY (headBar === treeBar sizes) ||| centerY (headBar === treeBar sizes)
||| centerY (vcat $ map renderSubTree ts) ||| centerY (vcat $ map renderSubTree ts)
where where
sizes = map ((* 6) . treeSize) ts sizes = map ((* 6) . leafCount) ts
renderSubTree subtree = hrule 4 # lwO 2 ||| renderTree colorByP props subtree renderSubTree subtree = hrule 4 # lwO 2 ||| renderTree colorByP props subtree
headBar = strutY $ treeSize t * 6 headBar = strutY $ leafCount t * 6
treeBar :: [Double] -> QDiagram B V2 Double Any treeBar :: [Double] -> QDiagram B V2 Double Any
treeBar (s1:s2:ss) = vrule s1 # lwO 2 === vrule s2 # lwO 2 === treeBar (s2:ss) treeBar (s1:s2:ss) = vrule s1 # lwO 2 === vrule s2 # lwO 2 === treeBar (s2:ss)
@ -134,49 +133,56 @@ renderTree colorByP props x@(Node (ty, n) ts@(t:_)) =
ProductNode -> text "x" ProductNode -> text "x"
SequenceNode -> text "->" SequenceNode -> text "->"
AtomicNode -> mempty AtomicNode -> mempty
in txt # fontSizeL 2 # bold <> circle 2 # fc white # lwO 1 in txt # fontSizeL 2 # bold <> roundedRect 3 2 1 # fc white # lwO 1
renderNode :: Bool -> [ProjAttribute] -> PNode -> QDiagram B V2 Double Any renderNode :: Bool -> [ProjAttribute] -> PNode -> QDiagram B V2 Double Any
renderNode _ _ (NodeRef (ProjectKey n)) = renderNode _ _ (NodeRef (ProjectKey n)) =
text n <> roundedRect 30 12 0.5 # lwO 2 # fc white # dashingN [0.005, 0.005] 0 text n <> roundedRect 30 12 0.5 # lwO 2 # fc white # dashingN [0.005, 0.005] 0
renderNode colorByP props (PNode _ prop c t p) = renderNode colorByP props (PNode _ prop c t p) =
centerY nodeDia # withEnvelope (rect 30 12 :: D V2 Double) centerY nodeDia <> strutY 12
where where
nodeDia = nodeDia =
let hSizeAndSections = catMaybes [ (,2) <$> headerSection let sections = if isJust titleHeader
, (,6) <$> descriptionSection then catMaybes [ headerSection
, (,2) <$> urlSection , descriptionSection
, (,2) <$> bottomSection] , urlSection
sections = map (\s -> strutY (snd s) <> fst s) hSizeAndSections , bottomSection]
outerRect = rect 30 (sum $ map snd hSizeAndSections) # lwO 2 else maybeToList simplifiedNode
sectionsWithSep = vcat (intersperse (hrule 30 # dashingN [0.005, 0.005] 0 # lwO 1) sections) sectionsWithSep = vcat (intersperse (hrule nodeW # dashingN [0.005, 0.005] 0 # lwO 1) sections)
in outerRect # fcColor `beneath` centerY sectionsWithSep in centerY (sectionsWithSep <> boundingRect sectionsWithSep # fc projColor # lwO 2)
nodeW = 30
simplifiedNode = case [progressHeader, trustHeader' text, costHeader] of
[Nothing, Nothing, Nothing] -> Nothing
l -> Just $ strutY 2 <> strutX nodeW <> mconcat (catMaybes l)
givenProp :: ProjAttribute -> Maybe a -> Maybe a givenProp :: ProjAttribute -> Maybe a -> Maybe a
givenProp pro x = if pro `elem` props then x else Nothing givenProp pro x = if pro `elem` props then x else Nothing
headerSection = case [progressHeader, titleHeader, costHeader] of headerSection = case [progressHeader, titleHeader, costHeader] of
[Nothing, Nothing, Nothing] -> Nothing [Nothing, Nothing, Nothing] -> Nothing
l -> Just $ strutX 30 <> mconcat (catMaybes l) l -> Just $ strutY 2 <> strutX nodeW <> mconcat (catMaybes l)
progressHeader = givenProp PProgress $ Just $ displayProgress p # translateX (-14) progressHeader = givenProp PProgress $ Just $ displayProgress p # translateX (-nodeW/2 + 1)
titleHeader = givenProp PTitle $ (bold . text . title) <$> prop titleHeader = givenProp PTitle $ (bold . text . title) <$> prop
costHeader = givenProp PCost $ Just $ displayCost c # translateX 14 costHeader = givenProp PCost $ Just $ displayCost c # translateX (nodeW/2 - 1)
descriptionSection, urlSection, bottomSection :: Maybe (QDiagram B V2 Double Any) descriptionSection, urlSection, bottomSection :: Maybe (QDiagram B V2 Double Any)
descriptionSection = givenProp PDescription $ prop >>= description >>= (pure . text) -- TODO:50 line breaks descriptionSection = givenProp PDescription $ prop >>= description >>= (pure . (strutY 6 <>) . text) -- TODO:50 line breaks
urlSection = givenProp PUrl $ prop >>= url >>= (pure . text) -- TODO:40 ellipsis urlSection = givenProp PUrl $ prop >>= url >>= (pure . (strutY 2 <>) . text) -- TODO:40 ellipsis
bottomSection = case [trustSubSection, ownerSubSection] of bottomSection = case [trustHeader, ownerHeader] of
[Nothing, Nothing] -> Nothing [Nothing, Nothing] -> Nothing
l -> Just $ strutX 30 <> mconcat (catMaybes l) l -> Just $ strutY 2 <> strutX nodeW <> mconcat (catMaybes l)
ownerSubSection = prop >>= owner >>= (pure . translateX 14 . rightText) ownerHeader = prop >>= owner >>= (pure . translateX (nodeW/2 -1) . rightText)
trustSubSection = translateX (-14) <$> trustHeader = translateX (-nodeW/2+1) <$> trustHeader' leftText
case t of
_ | PTrust `notElem` props -> Nothing trustHeader' txt = case t of
t' | t' == 1 -> Nothing _ | PTrust `notElem` props -> Nothing
t' | t' == 0 -> Just $ leftText "impossible" t' | t' == 1 -> Nothing
_ -> Just $ leftText ("trust = " ++ percentageText t) t' | t' == 0 -> Just $ txt "impossible"
_ -> Just $ txt ("trust = " ++ percentageText t)
displayCost c' displayCost c'
| c' == 0 = mempty | c' == 0 = mempty
@ -188,9 +194,9 @@ renderNode colorByP props (PNode _ prop c t p) =
-- color is red if the project hasn't started, green if it's done, or yellow -- color is red if the project hasn't started, green if it's done, or yellow
-- otherwise (i.e. in progress) -- otherwise (i.e. in progress)
fcColor = projColor =
fc $ if colorByP then if colorByP then
(if p == 0 then pink else if p == 1 then lightgreen else lightyellow) (if p == 0 then pink else if p == 1 then lightgreen else lightyellow)
else white else white
percentageText pct = show ((round $ pct * 100) :: Integer) ++ "%" percentageText pct = show ((round $ pct * 100) :: Integer) ++ "%"

View File

@ -7,11 +7,11 @@ Maintainer : rodrigosetti@gmail.com
Stability : experimental Stability : experimental
Portability : POSIX Portability : POSIX
-} -}
{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE OverloadedLists #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE UnicodeSyntax #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE UnicodeSyntax #-}
module MasterPlan.Data ( ProjectExpr(..) module MasterPlan.Data ( ProjectExpr(..)
, ProjectProperties(..) , ProjectProperties(..)
, ProjectSystem(..) , ProjectSystem(..)
@ -39,7 +39,7 @@ import Data.Generics
import Data.List.NonEmpty (NonEmpty ((:|))) import Data.List.NonEmpty (NonEmpty ((:|)))
import qualified Data.List.NonEmpty as NE import qualified Data.List.NonEmpty as NE
import qualified Data.Map as M import qualified Data.Map as M
import Data.String (IsString) import Data.String (IsString)
-- * Types -- * Types
@ -118,8 +118,8 @@ cost ∷ ProjectSystem → ProjectExpr → Cost
cost sys (Reference n) = cost sys (Reference n) =
case M.lookup n (bindings sys) of case M.lookup n (bindings sys) of
Just (BindingAtomic _ (Cost c) _ (Progress p)) -> Cost $ c * (1 - p) -- cost is weighted by remaining progress Just (BindingAtomic _ (Cost c) _ (Progress p)) -> Cost $ c * (1 - p) -- cost is weighted by remaining progress
Just (BindingExpr _ p) -> cost sys p -- TODO:0 avoid cyclic Just (BindingExpr _ p) -> cost sys p -- TODO:0 avoid cyclic
Nothing -> defaultCost -- mentioned but no props neither task defined Nothing -> defaultCost -- mentioned but no props neither task defined
cost sys (Sequence ps) = costConjunction sys ps cost sys (Sequence ps) = costConjunction sys ps
cost sys (Product ps) = costConjunction sys ps cost sys (Product ps) = costConjunction sys ps
cost sys (Sum ps) = cost sys (Sum ps) =
@ -140,8 +140,8 @@ trust ∷ ProjectSystem → ProjectExpr → Trust
trust sys (Reference n) = trust sys (Reference n) =
case M.lookup n (bindings sys) of case M.lookup n (bindings sys) of
Just (BindingAtomic _ _ (Trust t) (Progress p)) -> Trust $ p + t * (1-p) Just (BindingAtomic _ _ (Trust t) (Progress p)) -> Trust $ p + t * (1-p)
Just (BindingExpr _ p) -> trust sys p -- TODO:10 avoid cyclic Just (BindingExpr _ p) -> trust sys p -- TODO:10 avoid cyclic
Nothing -> defaultTrust -- mentioned but no props neither task defined Nothing -> defaultTrust -- mentioned but no props neither task defined
trust sys (Sequence ps) = trustConjunction sys ps trust sys (Sequence ps) = trustConjunction sys ps
trust sys (Product ps) = trustConjunction sys ps trust sys (Product ps) = trustConjunction sys ps
trust sys (Sum ps) = trust sys (Sum ps) =