{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
module UU.UUAGC.Diagrams
(production, child, agrule, indrule,
shaftL, shaftR, shaftT, shaftB, shaftD,
(#),
AGDiagram, AGBackend, Child) where
import Diagrams.Prelude
import Graphics.SVGFonts (textSVG_, Spacing (..), TextOpts (..), lin2, Mode (..))
import Data.List (isPrefixOf)
import System.IO.Unsafe (unsafePerformIO)
production :: AGBackend b =>
[String] -> String -> [String] -> [Child b] -> AGDiagram b
production = node True
newtype Child b = Child { unChild :: AGDiagram b }
child :: AGBackend b => [String] -> String -> [String] -> Child b
child i n s = Child $ node False i n s []
agrule :: AGBackend b =>
Trail V2 Double -> String -> String -> AGDiagram b -> AGDiagram b
agrule sh s1 s2 = connectPerim' (with & headLength .~ (normalized 0.025) & arrowShaft .~ sh) n1 n2 (tb t1) (tb t2) where
t1 = "lhs." `isPrefixOf` s1
t2 = "lhs." `isPrefixOf` s2
n1 | '.' `notElem` s1 = s1
| t1 = s1 ++ ".inh"
| otherwise = s1 ++ ".syn"
n2 = if t2 then s2 ++ ".syn" else s2 ++ ".inh"
tb False = 90 @@ deg
tb True = 270 @@ deg
indrule :: AGBackend b => String -> String -> AGDiagram b -> AGDiagram b
indrule s1 s2 = connectPerim' (with & headLength .~ (normalized 0.025) & arrowShaft .~ shaftB & shaftStyle %~ dashed . opacity 0.5) n1 n2 tb tb where
t = "lhs." `isPrefixOf` s1
n1 = if t then s1 ++ ".syn" else s1 ++ ".inh"
n2 = if t then s2 ++ ".inh" else s2 ++ ".syn"
tb = if t then 90 @@ deg else 270 @@ deg
dashed = dashingN [0.01,0.01] 0
shaftL, shaftR, shaftT, shaftB, shaftD :: Trail V2 Double
shaftL = fromSegments [bezier3 (r2 (0.5,0.3)) (r2 (0.5,-0.3)) (r2 (1,0))]
shaftR = fromSegments [bezier3 (r2 (0.5,-0.3)) (r2 (0.5,0.3)) (r2 (1,0))]
shaftT = arc xDir (-3/5 @@ turn)
shaftB = arc xDir (2/5 @@ turn)
shaftD = straightShaft
type AGDiagram b = QDiagram b V2 Double Any
class (Renderable (Path V2 Double) b, Backend b (V b) Double) => AGBackend b where
instance (Renderable (Path V2 Double) b, Backend b (V b) Double) => AGBackend b
attr :: AGBackend b =>
String -> Bool -> (String -> String) -> AGDiagram b
attr s t f = stack t (unitSquare # named (f s) # lc black) (text' 0.7 s) where
stack True a b = beside unitY a (b === strutY 0.2)
stack False a b = beside (-unitY) a (strutY 0.2 === b)
node :: AGBackend b =>
Bool -> [String] -> String -> [String] -> [Child b] -> AGDiagram b
node top inh s syn ch = res # applyAll lines where
res = toprow
===
(if null ch then mempty else strutY 2)
===
(hcats 1.5 $ map unChild ch) # centerX
lines = alines ++ chLines
chLines = [ line name (getName $ unChild c) # lc grey | c <- ch ]
hcats s = hcat' (with & sep .~ s)
els = inhs ++ [lhs] ++ syns
toprow = beside unitX (
beside (-unitX) lhs
(hcats 0.3 inhs ||| strutX 0.3))
(strutX 0.3 ||| hcats 0.3 syns)
inhs = map (\i -> attr i top (\n -> name ++ "." ++ n ++ ".inh")) inh
syns = map (\s -> attr s top (\n -> name ++ "." ++ n ++ ".syn")) syn
alines = zipWith line (map getName els) (map getName $ tail els) # lc grey
name = if top then "lhs" else s
lhs = beside (-unitY) (
beside unitY
(circle 0.5 # named name # lc grey)
(if top then (text' 0.9 s === strutY 0.1) else mempty))
(strutY 0.1 === text' 0.9 name)
{-# NOINLINE lin2' #-}
lin2' = unsafePerformIO lin2
text' :: AGBackend b =>
Double -> String -> AGDiagram b
text' d s = (textSVG_ (TextOpts lin2' INSIDE_H KERN False d d) s) # lw none # fc black # centerX
line :: (IsName n1, IsName n2, AGBackend b) =>
n1 -> n2 -> AGDiagram b -> AGDiagram b
line a b = connectOutside' (with & arrowHead .~ noHead) a b
getName :: AGDiagram b -> Name
getName = fst . head . names