twentefp-0.4: examples/CreateGraph.hs
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE Rank2Types #-}
module CreateGraph where
import Prelude
import FPPrac.Graphics
import FPPrac.Events
import Graphics
import Data.Char
import Data.List(sort,(\\))
import Debug.Trace
data Process = MakingNode | MakingEdge | DoingNothing
deriving (Eq,Show)
data Store a = Store
{ graph :: Graph
, startNode :: Node
, endNode :: Node
, process :: Process
, userHandler :: a -> Input -> (a,[Output])
, userHandlerEnabled :: Bool
, userState :: a
, userStateInit :: Graph -> a
, userGraphLens :: a -> Graph
, userBottomLine :: Graph -> Picture
}
startGraph = Graph {name="",directed=False,weighted=False,nodes=[],edges=[]}
nullNode = ('-',white,(0,0))
clearStore s = s {startNode=nullNode, endNode=nullNode, process=DoingNothing, userHandlerEnabled = False}
startStore h i l b = Store
{ graph = startGraph
, startNode = nullNode
, endNode = nullNode
, process = DoingNothing
, userHandler = h
, userHandlerEnabled = False
, userState = i startGraph
, userStateInit = i
, userGraphLens = l
, userBottomLine = b
}
distance (x0,y0) (x1,y1) = sqrt ((x1-x0)^2 + (y1-y0)^2)
freespace graph p = nodes==[] || minimum [distance p (x,y) | (_,_,(x,y)) <- nodes] > distfactor*nodeRadius
where
Graph {directed=directed,weighted=weighted,nodes=nodes}=graph
distfactor | directed && weighted = 7
| directed || weighted = 5
| otherwise = 3
node2text (lbl,col,(x,y)) = lbl : (show $ rgbaOfColor col) ++ ":" ++ show x ++ "," ++ show y ++ "\n"
edge2text (lbl1,lbl2,col,wght)
= [lbl1,lbl2] ++ show (rgbaOfColor col) ++ ":" ++ show wght ++ "\n"
text2node :: String -> Node
text2node line = (lbl, col, (read x, read (tail y)))
where
lbl = head line
col = (\(r,g,b,a) -> makeColor r g b a) $ read $ takeWhile (/=':') (tail line)
(x,y) = span (/=',') (tail (dropWhile (/=':') line))
text2edge :: String -> Edge
text2edge line = (lbl1,lbl2, col, read (tail awght))
where
(lbl1,lbl2) = (head line, head (tail line))
(acol,awght) = span (/=':') (drop 2 line)
col = (\(r,g,b,a) -> makeColor r g b a) $ read acol
createGraph ::
forall a
. Store a
-> Input
-> (Store a,[Output])
createGraph store@(Store {userHandlerEnabled=True,..}) (KeyIn 'q')
= (s', [DrawPicture $ drawBottomLine graph'])
where
graph' = userGraphLens userState
userState' = userStateInit graph'
s' = clearStore $ store {graph=graph',userState=userState'}
createGraph store@(Store {userHandlerEnabled=True, ..}) i
= (store {userState = userState'}, o)
where
(userState',o) = userHandler userState i
createGraph store (MouseDown (x,y))
| nodeClicked /= Nothing = (newStoreE, [DrawOnBuffer False])
| otherwise = (newStoreN, [])
where
Store {graph=graph} = store
Graph {nodes=nodes} = graph
nodeClicked = onNode nodes (x,y)
Just newNode = nodeClicked
newStoreN = store {process=MakingNode}
newStoreE = store {startNode=newNode, process=MakingEdge}
createGraph store (MouseMotion (x,y))
| process == MakingEdge = (store,[DrawOnBuffer False, DrawPicture $ drawEdgeTemp black (startPoint,(x,y))])
| otherwise = (store,[])
where
Store {startNode=(_,_,startPoint),process=process} = store
createGraph store (MouseUp (x,y))
| process == MakingNode
, freespace graph (x,y)
, freeLabels /= ""
= (newStoreN, [DrawPicture $ drawNode newNode])
| process == MakingEdge
, nodeClicked == Nothing
= (storeCl, [DrawOnBuffer True, DrawPicture $ Pictures [drawGraph graph, drawBottomLine graph]])
| process == MakingEdge
, weighted
, lblE /= lblS
, not alreadyConnected
= (newStoreE1, [DrawOnBuffer True, GraphPrompt ("weight", "maximum 99")])
| process == MakingEdge
, not weighted
, lblE /= lblS
, not alreadyConnected
= (newStoreE2, [DrawOnBuffer True, DrawPicture $ Pictures [drawGraph newGraphE2, drawBottomLine newGraphE2]])
| otherwise
= (storeCl, [DrawOnBuffer True, DrawPicture $ Pictures [drawGraph graph, drawBottomLine graph]])
where
Store {startNode=(lblS,colS,crdsS),..} = store
Graph {..} = graph
-- New node
freeLabels = alphabet \\ [lbl | (lbl,_,_) <- nodes]
newLabel = head (sort freeLabels)
newNode = (newLabel, white, (x,y))
newGraphN = graph {nodes=newNode:nodes}
newStoreN = clearStore $ store {graph=newGraphN}
-- New edge
nodeClicked = onNode nodes (x,y)
Just (lblE,colE,crdsE) = nodeClicked
alreadyConnected | directed = elem lblE [ lbl2 | (lbl1,lbl2,col,wght) <- edges
, lbl1 == lblS ]
| otherwise = not $ null
[lbl1 | (lbl1,lbl2,coll,wght) <- edges
, (lbl1 == lblS && lbl2 == lblE)
|| (lbl1 == lblE && lbl2 == lblS)
]
newStoreE1 = store {endNode = (lblE,colE,crdsE)}
newEdge = (lblS, lblE, black, 0)
newGraphE2 = graph {edges = newEdge:edges}
newStoreE2 = clearStore $ store {graph = newGraphE2}
-- Clear store
storeCl = clearStore store
createGraph store (MouseDoubleClick (x,y))
| nodeClicked /= Nothing
= (newStore, [DrawPicture $ Pictures [drawGraph newGraph, drawBottomLine newGraph]])
| otherwise
= (store, [])
where
Store {graph=graph} = store
Graph {nodes=nodes,edges=edges} = graph
nodeClicked = onNode nodes (x,y)
Just (lblClicked,_,_) = nodeClicked
newNodes = [ (lbl,col,(x,y)) | (lbl,col,(x,y)) <- nodes , lbl /= lblClicked ]
newEdges = [ (lbl1,lbl2,c,w) | (lbl1,lbl2,c,w) <- edges
, lbl1 /= lblClicked
, lbl2 /= lblClicked
]
newGraph = graph {nodes=newNodes, edges=newEdges}
newStore = store {graph = newGraph}
createGraph store (Prompt ("weight", weight))
| weight /= ""
, all isDigit weight
= (newStoreE, [DrawPicture $ Pictures [drawGraph newGraph, drawBottomLine newGraph]])
| otherwise
= (newStore, [DrawPicture $ Pictures [drawGraph graph, drawBottomLine graph]])
where
Store {graph=graph, startNode=(lblS,_,_), endNode=(lblE,_,_)} = store
Graph {edges=edges} = graph
newEdge = (lblS, lblE, black, read weight)
newGraph = graph {edges = newEdge:edges}
newStoreE = store {graph=newGraph, startNode = nullNode, endNode = nullNode, process=DoingNothing}
newStore = store {startNode = nullNode, endNode = nullNode, process = DoingNothing}
createGraph store (KeyIn 'n')
= (store,[newPanel,newPanelVisible])
createGraph store (Panel 1 [(60,dir), (70,wghtd)])
= (newStore,[newPanelInvisible,DrawPicture $ Pictures [drawGraph newGraph,drawBottomLine newGraph]])
where
newGraph = Graph
{ name = ""
, directed = dir == "Y"
, weighted = wghtd == "Y"
, nodes = []
, edges = []
}
newStore = store {graph=newGraph}
createGraph store (KeyIn 'r')
= (store,[GraphPrompt ("Read graph", "filename")])
createGraph store (Prompt ("Read graph", fileName))
| fileName /= "" = (store, [ReadFile fileName (TXTFile "")])
| otherwise = (store, [])
createGraph store (File fileName (TXTFile input))
| input /= ""
= (newStore, [DrawPicture $ Pictures [drawGraph newGraph, drawBottomLine newGraph]])
| otherwise
= (store,[])
where
(ns,bs) = (span (/="--") . drop 2 . lines) input
directed = (head . lines) input == "directed:yes"
weighted = (head . tail . lines) input == "weighted:yes"
newGraph = Graph
{ name = fileName
, directed = directed
, weighted = weighted
, nodes = map text2node ns
, edges = map text2edge (tail bs)
}
newStore = clearStore $ store {graph=newGraph}
createGraph store (KeyIn 's')
| nm /= "" = (store, [SaveFile nm (TXTFile savetext)])
| otherwise = (store, [GraphPrompt ("save as", "filename")])
where
Graph {name=nm,directed=directed,weighted=weighted,nodes=nodes,edges=edges} = graph store
dirweight | directed && weighted = "directed:yes\nweighted:yes\n"
| directed = "directed:yes\nweighted:no\n"
| weighted = "directed:no\nweighted:yes\n"
| otherwise = "directed:no\nweighted:no\n"
savetext = dirweight
++ concat (map node2text nodes) ++ "--\n"
++ concat (map edge2text edges)
createGraph store (KeyIn 'a')
= (store,[GraphPrompt ("save as", "filename")])
createGraph store (Prompt ("save as", nm))
= (newStore, [SaveFile nm (TXTFile savetext), DrawPicture $ drawBottomLine newGraph])
where
Graph {name=nm0,directed=directed,weighted=weighted,nodes=nodes,edges=edges} = graph store
dirweight | directed && weighted = "directed:yes\nweighted:yes\n"
| directed = "directed:yes\nweighted:no\n"
| weighted = "directed:no\nweighted:yes\n"
| otherwise = "directed:no\nweighted:no\n"
savetext = dirweight
++ concat (map node2text nodes) ++ "--\n"
++ concat (map edge2text edges)
newGraph = (graph store) {name=nm}
newStore = store {graph=newGraph}
createGraph store@(Store {..}) (KeyIn '6')
= (store {userHandlerEnabled = True, userState = userStateInit graph}, [DrawPicture $ userBottomLine graph])
createGraph store _ = (store,[])
newPanel
= PanelCreate
("New Graph", 160, 45
,[]
,[ (60, "directed", CheckButton, -65, 20, 10, 10)
, (70, "weighted", CheckButton, 5 , 20, 10, 10)
, (1 , "OK" , Button , 0 , 0 , 60, 20)
]
)
newPanelVisible = PanelUpdate True []
newPanelInvisible = PanelUpdate False []
onNode :: [Node] -> Point -> Maybe Node
onNode [] p = Nothing
onNode (n@(_,_,q):ns) p | distance p q <= nodeRadius = Just n
| otherwise = onNode ns p
doGraph h i l b = installEventHandler "createGraph" createGraph (startStore h i l b) startPic 10
where
startPic = Pictures
[ drawGraph startGraph
, drawBottomLine startGraph
]