module Discokitty.Models.Diagrams
( tikzDiagrams
)
where
import Discokitty.HasCups
import Discokitty.Words
data Node = Node
{ idNumber :: Int
, xPos :: Double
, yPos :: Double
, label :: String
, style :: Style
}
deriving (Show)
data Style = Copoint | None
instance Show Style where
show Copoint = "wide copoint"
show None = "none"
type NodeId = Int
data Wire = Wire
{ looseness :: Double
, from :: NodeId
, to :: NodeId
}
deriving (Show)
data Diagram = Diagram
{ wires :: [Wire]
, nodes :: [Node]
}
deriving (Show)
data Schema = Schema
{ schemaNodes :: [Node]
, schemaWires :: [Wire]
, nWords :: Int
}
deriving (Show)
nodeWidth :: Double
nodeWidth = 4
generateNodes :: Int -> Words m -> Words Schema
generateNodes offset w = Words
{ meaning = Schema
{ schemaNodes = createNode <$> [0..(len-1)]
, schemaWires = []
, nWords = 1
}
, grammar = grammar w
, text = ""
}
where
createNode :: Int -> Node
createNode n = Node
{ idNumber = offset + n
, xPos = ((fromIntegral n + 1) * nodeWidth / (fromIntegral len + 1)) - (nodeWidth / 2.0)
, yPos = 0
, label = ""
, style = None
}
len :: Int
len = length $ grammar w
schemaWords :: [Words m] -> [Words Schema]
schemaWords ws = (generateNodes . length) ws <$> ws
shiftNodeId :: Int -> Schema -> Schema
shiftNodeId n b = b
{ schemaNodes = fmap (\ p -> p
{ idNumber = n + idNumber p
}) $ schemaNodes b
}
shiftNodePos :: Int -> Schema -> Schema
shiftNodePos p b = b
{ schemaNodes = fmap (\q -> q
{ xPos = 4.0 * (fromIntegral p) + xPos q
}) $ schemaNodes b
}
shiftWiresId :: Int -> Schema -> Schema
shiftWiresId n b = b
{ schemaWires = fmap (\q -> q
{ from = n + from q
, to = n + to q
}) $ schemaWires b
}
schemaCup :: Int -> Schema -> Schema -> Schema
schemaCup n a b = joinSchemas a (shifted b)
where
shifted :: Schema -> Schema
shifted =
shiftNodePos (nWords a)
. shiftWiresId (length (schemaNodes a))
. shiftNodeId (length (schemaNodes a))
joinSchemas :: Schema -> Schema -> Schema
joinSchemas u v = Schema
{ schemaNodes = schemaNodes u ++ schemaNodes v
, nWords = nWords u + nWords v
, schemaWires =
schemaWires u ++ schemaWires v ++
(fmap (\ (m , x , y) -> Wire
{ looseness = (fromIntegral m + 1.25) :: Double
, from = x
, to = y
})
$ zip3 [0..(n-1)] (reverse (idNumber <$> schemaNodes u)) (idNumber <$> schemaNodes v))
}
schemaUnit :: Schema
schemaUnit = Schema
{ schemaNodes = []
, schemaWires = []
, nWords = 0
}
instance HasCups Schema where
cup = schemaCup
cunit = schemaUnit
tikzDiagrams :: [Words m] -> String
tikzDiagrams = unlines . fmap generateTikz . textDiagrams
textDiagrams :: [Words m] -> [Diagram]
textDiagrams ws = do
solution <- sentence $ schemaWords ws
let textWires = schemaWires $ meaning solution
let textNodes = schemaNodes $ meaning solution
let openWires = danglingWires $ meaning solution
let openNodes = danglingNodes $ meaning solution
return Diagram
{ nodes = allWordNodes ++ textNodes ++ openNodes
, wires = textWires ++ openWires
}
where
allWordNodes :: [Node]
allWordNodes = numberedNode <$> zip [0..] ws
numberedNode :: (Int , Words m) -> Node
numberedNode (n,w) = Node
{ idNumber = n
, xPos = fromIntegral n * 4
, yPos = 0
, label = text w
, style = Copoint
}
danglingWires :: Schema -> [Wire]
danglingWires s = do
(f , t) <- zip (idNumber <$> preDanglingNodes s) (idNumber <$> danglingNodes s)
return Wire
{ from = f
, to = t
, looseness = 0
}
danglingNodes :: Schema -> [Node]
danglingNodes s = (\p -> p { yPos = yPos p - 3 , idNumber = idNumber p + 100 }) <$> preDanglingNodes s
preDanglingNodes :: Schema -> [Node]
preDanglingNodes s = filter (not . (`elem` occupiedIds s) . idNumber) (schemaNodes s)
occupiedIds :: Schema -> [NodeId]
occupiedIds s =
(from <$> schemaWires s) ++
(to <$> schemaWires s)
generateTikz :: Diagram -> String
generateTikz diagram = unlines $
[ "\\begin{tikzpicture}"
, "\\begin{pgfonlayer}{nodelayer}"
]
++ fmap generateNode (nodes diagram) ++
[ "\\end{pgfonlayer}{nodelayer}"
, "\\begin{pgfonlayer}{edgelayer}"
]
++ fmap generateWire (wires diagram) ++
[ "\\end{pgfonlayer}"
, "\\end{tikzpicture}"
]
where
generateNode :: Node -> String
generateNode node =
"\\node "
++ "[style=" ++ show (style node) ++ "] "
++ "(" ++ show (idNumber node) ++ ") "
++ "at (" ++ show (xPos node) ++ ", " ++ show (yPos node) ++ ") "
++ "{" ++ label node ++ "};"
generateWire :: Wire -> String
generateWire wire =
"\\draw ["
++ "bend right=90, "
++ "looseness=" ++ (show . looseness) wire ++ "] "
++ "("
++ (show . from) wire
++ ".center) to ("
++ (show . to) wire
++ ".center);"