packages feed

INblobs-0.1.1: src/Network.hs

module Network
    (
    -- * Types
      Network, Node, Edge
    , NodeNr, EdgeNr, ViaNr
    , networkNodes  -- dangerous
    , networkEdges  -- dangerous

    -- * Creating and printing a network
    , Network.empty
    , Network.isEmpty
    , dumpNetwork
    -- ** Set\'s e get\'s
    , getNodeNrs
    , getNodeAssocs,    setNodeAssocs
    , getEdgeAssocs,    setEdgeAssocs
    , getCanvasSize,    setCanvasSize
    , getGlobalInfo,    setGlobalInfo
    -- ** Get usefull information from 'Network'
    , getNode
    , getEdge
    , getNodes
    , getEdges
    , getChildren
    , getParents
    , getParentMap, ParentMap
    -- ** Search\'s
    , nodeExists, edgeExists
    , findEdge, findNodeNrsByName
    , edgeConnectedOnPort
    , otherExtremeOfEdgeConnectedOnPort
    -- ** update network elements
    , updateNode
    , updateEdge
    , updateVia

    , mapNodeNetwork
    -- ** add and remove network elements
    , addNode,      addNodes,    removeNode, addNodeEx, addExistingNode
    , addEdge,      addEdges,    removeEdge
    , removeAllEdges
    , newViaEdge,   removeVia

    -- * Node
    , constructNode
    -- ** get and set node data in a network by 'NodeNr' 
    , getNodeInfo, getNodeName, getNodePosition, getNodeNameAbove, getNodeShape
    , setNodeInfo, setNodeName, setNodePosition, setNodeNameAbove, setNodeShape
    -- ** get\'s and set\'s of a node
    , getInfo, getName, getPosition, getNameAbove, getShape
    , setInfo, setName, setPosition, setNameAbove, setShape
    -- * Edge
    , constructEdge
    , getEdgeFrom, getPortFrom, getEdgeTo, getPortTo, getEdgeVia, getEdgeInfo, getFullPortFrom, getFullPortTo
    , setEdgeFrom, setPortFrom, setEdgeTo, setPortTo, setEdgeVia, setEdgeInfo
    -- * Interaction Nets functionality
    , getPorts
    , getPort
    , isPrincipalPort
    , showEdge
    ) where

import Common
import Math
import InfoKind
import Shape
import Ports
import Palette (Palette, shapes, ShapeName, getSymbol)

import Data.IntMap as IntMap hiding (map)
import qualified Data.List as List


data Network g n e = Network
    { networkNodes      :: !(IntMap (Node n)) -- ^ maps node numbers to nodes
    , networkEdges      :: !(IntMap (Edge e)) -- ^ maps edge numbers to edges
    , networkCanvasSize :: (Double, Double)
    , networkInfo       :: g
    } deriving Show

data Edge e = Edge
    { edgeFrom :: !NodeNr -- ^ the number of the node where the edge starts
    , portFrom :: PortName --
    , edgeTo   :: !NodeNr -- ^ the number of the node the edge points to
    , portTo   :: PortName --
    , edgeVia  :: [DoublePoint] -- ^ intermediate vertices when drawing
    , edgeInfo :: e
    } deriving (Show, Read, Eq)

showEdge :: Edge e -> String
showEdge edge =
    "Node " ++ show (edgeFrom edge) ++ showPort (portFrom edge)
    ++ " |-> " ++
    "Node " ++ show (edgeTo edge) ++ showPort (portTo edge)
    where
          showPort name =  '@' : name

data Node n = Node
    { nodePosition  :: DoublePoint  -- ^ the position of the node on screen
    , nodeName      :: !String
    , nodeNameAbove :: Bool         -- ^ should the name be displayed above (True) of below (False)
    , nodeShape     :: ShapeName       -- ^ symbol name from palette,
    , nodeInfo      :: n
    } deriving (Show, Read)

type NodeNr = Int
type EdgeNr = Int
type ViaNr  = Int

-- | Create an empty network
empty :: (InfoKind n g, InfoKind e g) => g -> n -> e -> Network g n e
empty g _ _ = Network
    { networkNodes      = IntMap.empty
    , networkEdges      = IntMap.empty
    , networkCanvasSize = (15, 9)
    , networkInfo       = g
    }

-- | Map a function over the nodes, possibly changes the type
--   of the Network (i.e. the kind of values stored in the
--   probability tables)
mapNodeNetwork :: InfoKind m g =>
                  (Node n->Node m) -> Network g n e -> Network g m e
mapNodeNetwork nodeFun network =
    let numberedNodes = getNodeAssocs network
        newNodes = [ (nr, nodeFun node) | (nr, node) <- numberedNodes ]
    in Network
        { networkNodes = IntMap.fromList newNodes
        , networkEdges = networkEdges network
        , networkCanvasSize = networkCanvasSize network
        , networkInfo = networkInfo network
        }

constructEdge :: NodeNr -> PortName -> NodeNr -> PortName -> [DoublePoint] -> e -> Edge e
constructEdge fromNr fromPort toNr toPort via info =
    Edge
        { edgeFrom = fromNr
        , portFrom = fromPort
        , edgeTo   = toNr
        , portTo   = toPort
        , edgeVia  = via
        , edgeInfo = info
        }

getEdgeFrom :: Edge e -> NodeNr
getEdgeFrom = edgeFrom

getPortFrom :: Edge e -> PortName
getPortFrom = portFrom

getFullPortFrom :: Edge e -> (NodeNr, PortName)
getFullPortFrom e = (edgeFrom e, portFrom e)

getEdgeTo :: Edge e -> NodeNr
getEdgeTo = edgeTo

getPortTo :: Edge e -> PortName
getPortTo = portTo

getFullPortTo :: Edge e -> (NodeNr, PortName)
getFullPortTo e = (edgeTo e, portTo e)

getEdgeVia :: Edge e -> [DoublePoint]
getEdgeVia = edgeVia

getEdgeInfo :: Edge e -> e
getEdgeInfo = edgeInfo

setEdgeFrom :: NodeNr -> Edge e -> Edge e
setEdgeFrom fromNr edge = edge { edgeFrom = fromNr }

setPortFrom :: NodeNr -> PortName -> Edge e -> Edge e
setPortFrom fromNr fromPort edge = edge { edgeFrom = fromNr, portFrom = fromPort }

setEdgeTo :: NodeNr -> Edge e -> Edge e
setEdgeTo toNr edge = edge { edgeTo = toNr }

setPortTo :: NodeNr -> PortName -> Edge e -> Edge e
setPortTo toNr toPort edge = edge { edgeTo = toNr, portTo = toPort }

setEdgeVia :: [DoublePoint] -> Edge e -> Edge e
setEdgeVia via edge = edge { edgeVia = via }

setEdgeInfo :: e -> Edge oldInfo -> Edge e
setEdgeInfo info edge =
    constructEdge (getEdgeFrom edge) (portFrom edge)
                  (getEdgeTo edge)   (portTo edge)
                  (getEdgeVia edge) info

constructNode :: (InfoKind n g) => String -> DoublePoint -> Bool
                                   -> ShapeName -> n -> Node n
constructNode name position nameAbove shape info =
       Node
          { nodeName      = name
          , nodePosition  = position
          , nodeNameAbove = nameAbove
          , nodeShape     = shape
          , nodeInfo      = info
          }

getNodeName :: Network g n e -> NodeNr -> String
getNodeName network nodeNr = nodeName (networkNodes network ! nodeNr)

setNodeName :: NodeNr -> String -> Network g n e -> Network g n e
setNodeName nodeNr name network =
    network { networkNodes = insert nodeNr (node { nodeName = name }) (networkNodes network) }
  where node = networkNodes network ! nodeNr

getNodePosition :: Network g n e -> NodeNr -> DoublePoint
getNodePosition network nodeNr = nodePosition (networkNodes network ! nodeNr)

setNodePosition :: NodeNr -> DoublePoint -> Network g n e -> Network g n e
setNodePosition nodeNr position network =
    network { networkNodes = insert nodeNr (node { nodePosition = position }) (networkNodes network) }
  where node = networkNodes network ! nodeNr

getNodeNameAbove :: Network g n e -> NodeNr -> Bool
getNodeNameAbove network nodeNr = nodeNameAbove (networkNodes network ! nodeNr)

setNodeNameAbove :: NodeNr -> Bool -> Network g n e -> Network g n e
setNodeNameAbove nodeNr nameAbove network =
    network { networkNodes = insert nodeNr (node { nodeNameAbove = nameAbove }) (networkNodes network) }
  where node = networkNodes network ! nodeNr

getNodeShape :: Network g n e -> NodeNr -> ShapeName
getNodeShape network nodeNr = nodeShape (networkNodes network ! nodeNr)

setNodeShape :: NodeNr -> ShapeName -> Network g n e -> Network g n e
setNodeShape nodeNr shape network =
    network { networkNodes = insert nodeNr (node { nodeShape = shape })
                                           (networkNodes network) }
  where node = networkNodes network ! nodeNr

getNodeInfo :: Network g n e -> NodeNr -> n
getNodeInfo network nodeNr = nodeInfo (networkNodes network ! nodeNr)

setNodeInfo :: NodeNr -> n -> Network g n e -> Network g n e
setNodeInfo nodeNr info network =
    network { networkNodes = insert nodeNr (node { nodeInfo = info }) (networkNodes network) }
  where node = networkNodes network ! nodeNr

getNameAbove :: Node a -> Bool
getNameAbove node = nodeNameAbove node

getName :: Node a -> String
getName node = nodeName node

getShape :: Node a -> ShapeName
getShape node = nodeShape node

getPosition :: Node a -> DoublePoint
getPosition node = nodePosition node

getInfo :: Node a -> a
getInfo node = nodeInfo node

-- | Set whether the name should appear above (True) or below (False) the node
setNameAbove :: Bool -> Node a -> Node a
setNameAbove above node = node { nodeNameAbove = above }

setName :: String -> Node a -> Node a
setName name node = node { nodeName = name }

setShape :: ShapeName -> Node a -> Node a
setShape s node = node { nodeShape = s }

setPosition :: DoublePoint -> Node a -> Node a
setPosition position node = node { nodePosition = position }

setInfo :: a -> Node a -> Node a
setInfo info node = node { nodeInfo = info }

-- | Get the next unused node number
getUnusedNodeNr :: Network g n e -> NodeNr
getUnusedNodeNr network | List.null used = 1
                        | otherwise = maximum used + 1
  where
    used = keys (networkNodes network)

-- | Get the next unused edge number
getUnusedEdgeNr :: Network g n e -> EdgeNr
getUnusedEdgeNr network | List.null used = 1
                        | otherwise = maximum used + 1
  where
    used = keys (networkEdges network)

-- | Get the node numbers of the parents of a given node
getParents :: Network g n e -> NodeNr -> [NodeNr]
getParents network child =
    [ parent
    | edge <- getEdges network
    , edgeTo edge == child
    , let parent = edgeFrom edge
    ]

type ParentMap = IntMap.IntMap [NodeNr]

-- | getParents is quite expensive (see above) and so
--   we store the parent relationship in an IntMap
getParentMap :: Network g n e -> ParentMap
getParentMap network =
    IntMap.fromList
        [ (nodeNr, getParents network nodeNr)
        | nodeNr <- getNodeNrs network
        ]

-- | Get the node numbers of the children of a given node
getChildren :: Network g n e -> NodeNr -> [NodeNr]
getChildren network parent =
    [ child
    | edge <- getEdges network
    , edgeFrom edge == parent
    , let child = edgeTo edge
    ]


-- | Get node with given index, raises exception if node number does not exist
getNode :: NodeNr -> Network g n e -> Node n
getNode nodeNr network
    | member nodeNr nodesMap = nodesMap ! nodeNr
    | otherwise = internalError "Network" "getNode" "illegal node number"
  where
    nodesMap = networkNodes network

-- | Get edge with given index, raises exception if edge number does not exist
getEdge :: EdgeNr -> Network g n e -> Edge e
getEdge edgeNr network = networkEdges network ! edgeNr

-- | Get all of the nodes in the network
getNodes :: Network g n e -> [Node n]
getNodes network = elems (networkNodes network)

-- | Get all of the edges in the network
getEdges :: Network g n e -> [Edge e]
getEdges network = elems (networkEdges network)

-- | Get all of the node numbers in the network
getNodeNrs :: Network g n e -> [NodeNr]
getNodeNrs network = keys (networkNodes network)

getCanvasSize :: Network g n e -> (Double, Double)
getCanvasSize network = networkCanvasSize network

getGlobalInfo :: Network g n e -> g
getGlobalInfo network = networkInfo network

-- | Find the number of an edge given start and end node number
findEdge :: NodeNr -> PortName -> NodeNr -> PortName -> Network g n e -> Maybe EdgeNr
findEdge fromNodeNr fromPort toNodeNr toPort network =
    let hits = IntMap.filter
                    (\edge ->
		     sameFromAndTo (Edge { edgeFrom = fromNodeNr
                                         , portFrom = fromPort
                                         , edgeTo   = toNodeNr
                                         , portTo   = toPort
                                         , edgeVia  = undefined
                                         , edgeInfo = undefined }) edge
		     || sameFromAndTo (Edge { edgeFrom = toNodeNr
                                         , portFrom = toPort
                                         , edgeTo   = fromNodeNr
                                         , portTo   = fromPort
                                         , edgeVia  = undefined
                                         , edgeInfo = undefined }) edge)
                    (networkEdges network)
    in case IntMap.keys hits of
        [key] -> Just key
        _ -> Nothing

-- | Find node numbers given a node name
findNodeNrsByName :: String -> Network g n e -> [NodeNr]
findNodeNrsByName theNodeName network =
    [ nodeNr
    | nodeNr <- getNodeNrs network
    , getNodeName network nodeNr == theNodeName
    ]

-- | Get a list of pairs where each pair contains a node number and the corresponding node
getNodeAssocs :: Network g n e -> [(NodeNr, Node n)]
getNodeAssocs network = assocs (networkNodes network)

setNodeAssocs :: [(NodeNr, Node n)] -> Network g n e -> Network g n e
setNodeAssocs nodeAssocs network =
    network { networkNodes = IntMap.fromList nodeAssocs }

-- | Get a list of pairs where each pair contains a edge number and the corresponding edge
getEdgeAssocs :: Network g n e -> [(EdgeNr, Edge e)]
getEdgeAssocs network = assocs (networkEdges network)

setEdgeAssocs :: [(EdgeNr, Edge e)] -> Network g n e -> Network g n e
setEdgeAssocs edgeAssocs network =
    network { networkEdges = IntMap.fromList edgeAssocs }

-- | Check if a network is empty. A network is empty is it has no nodes.
isEmpty :: Network g n e -> Bool
isEmpty = IntMap.null . networkNodes

-- | Create a string that describes the network
dumpNetwork :: InfoKind e g => Network g String e -> String
dumpNetwork network = show (getNodeAssocs network) ++ "\n" ++ show (getEdgeAssocs network)

-- | Test for existence of a node number
nodeExists :: NodeNr ->  Network g n e -> Bool
nodeExists nodeNr network =
    member nodeNr (networkNodes network)

-- | Test for existence of an edge number
edgeExists :: EdgeNr ->  Network g n e -> Bool
edgeExists edgeNr network =
    member edgeNr (networkEdges network)

{-----------------------------------
  Functions that change the network
 -----------------------------------}

-- | Add a node to the network
addNode :: InfoKind n g
        => ShapeName               -- ^ the current shape's name
        -> Palette n               -- ^ the palette
        -> Network g n e           -- ^ the network to add the node to
        -> (NodeNr, Network g n e) -- ^ the number of the new node and
                                   --   the extended network
addNode shape palette network =
    addNodeEx   ("Node " ++ show nodeNr)
                (DoublePoint 0.0 0.0)
                True
                shape
                blank
                network
  where
    nodeNr = getUnusedNodeNr network
    palette' = shapes palette


addNodes :: InfoKind n g => ShapeName -> Palette n -> Int -> Network g n e -> ([NodeNr], Network g n e)
addNodes _ _ 0 network = ([], network)
addNodes shapeName palette n network1 =
    let (nodeNr, network2) = addNode shapeName palette network1
        (nodeNrs, network3) = addNodes shapeName palette (n-1) network2
    in (nodeNr:nodeNrs, network3)

addNodeEx :: InfoKind n g =>
             String -> DoublePoint -> Bool -> ShapeName -> n
             -> Network g n e -> (NodeNr, Network g n e)
addNodeEx name position labelAbove shape info network =
    ( nodeNr
    , network { networkNodes = insert nodeNr node (networkNodes network) }
    )
  where
    nodeNr = getUnusedNodeNr network
    node = constructNode name position labelAbove shape info

addExistingNode :: InfoKind n g => Node n -> Network g n e -> (NodeNr, Network g n e)
addExistingNode node network = 
    ( nodeNr
    , network { networkNodes = insert nodeNr node' (networkNodes network) }
    )
  where
    nodeNr = getUnusedNodeNr network
    node' = setName ("Node " ++ show nodeNr) node

-- | Add an edge to the network. The probability table of the target node is updated:
--   a dimension is added and all values are zeroed.
addEdge :: InfoKind e g => Palette n -> NodeNr -> PortName -> NodeNr -> PortName -> Network g n e -> Network g n e
addEdge palette fromNodeNr fromPort toNodeNr toPort network
    | any (sameFromAndTo edge) edgesList || -- prohibit double edges 
      any (sameFromAndTo (reverseEdge edge)) edgesList || -- prohibit edges in opposite direction
      anyPortAlreadyUsed edge edgesList -- only one connection per port is allowed
      || (fromNodeNr, fromPort) == (toNodeNr, toPort) -- prohibit edges from a port to itself
        = network
    | otherwise =
        let edgeNr = getUnusedEdgeNr network
            networkPlusEdge = network { networkEdges = insert edgeNr edge (networkEdges network) }
        in networkPlusEdge
  where
    edge = Edge { edgeFrom = fromNodeNr, portFrom = fromPort
                , edgeTo = toNodeNr, portTo = toPort
                , edgeVia = if fromNodeNr /= toNodeNr then [] else [point2EdgeInNode palette fromNodeNr fromPort toPort network]
                , edgeInfo = blank }
    edgesList = elems (networkEdges network)

addEdges :: InfoKind e g => Palette n -> [( (NodeNr, PortName), (NodeNr, PortName) )]
                 -> Network g n e -> Network g n e
addEdges palette edgeTuples network =
  foldr (\((fromNr, fromMPort), (toNr, toMPort)) net -> addEdge palette fromNr fromMPort toNr toMPort net) 
        network edgeTuples

-- | Insert a new 'via' control point in the middle of an edge.
newViaEdge :: EdgeNr -> ViaNr -> DoublePoint
              -> Network g n e -> Network g n e
newViaEdge edgeNr viaNr point network =
    network { networkEdges = adjust (\e->e{ edgeVia= take viaNr (edgeVia e)
                                                     ++[point]
                                                     ++drop viaNr (edgeVia e) })
                                    edgeNr
                                    (networkEdges network) }

-- | Remove node with given index, raises exception if node number does not exist.
--   This function also removes all edges that start or end in this node.
removeNode :: NodeNr ->  Network g n e -> Network g n e
removeNode nodeNr network =
    let involvedEdges = [ i
                        | (i, edge) <- getEdgeAssocs network
                        , edgeFrom edge == nodeNr || edgeTo edge == nodeNr
                        ]
        networkWithoutEdges = foldr removeEdge network involvedEdges
        networkWithoutNode = networkWithoutEdges { networkNodes = delete nodeNr (networkNodes networkWithoutEdges) }
    in networkWithoutNode

-- | Remove an edge from the network. The probability table of the target node is updated:
--   the corresponding dimension is removed and all values are zeroed.
--   An exception is raised if edge number does not exist.
removeEdge :: EdgeNr -> Network g n e -> Network g n e
removeEdge edgeNr network =
    network { networkEdges = delete edgeNr (networkEdges network) }

-- | Remove all edges from the network. The probability tables of all node are zeroed.
removeAllEdges :: Network g n e -> Network g n e
removeAllEdges network =
    let networkWithoutEdges       = network { networkEdges = IntMap.empty }
    in  networkWithoutEdges

-- | Remove a control point from an edge.
removeVia :: EdgeNr -> ViaNr -> Network g n e -> Network g n e
removeVia edgeNr viaNr network =
    let remove n e = e { edgeVia = take n (edgeVia e)
                                   ++ drop (n+1) (edgeVia e) } in
    network { networkEdges = adjust (remove viaNr)
                                    edgeNr (networkEdges network) }

setCanvasSize :: (Double, Double) -> Network g n e -> Network g n e
setCanvasSize canvasSize network = network { networkCanvasSize = canvasSize }

setGlobalInfo :: g -> Network g n e -> Network g n e
setGlobalInfo info network = network { networkInfo = info }

{-----------------------------------
  Local functions
 -----------------------------------}

{- | Checks if two edges are equal. Equality in edges is: 
 *  if the edges have information of ports then the edges are equal when the pairs
 (node,port) from and to of the two edges are the same
 * otherwise the edges are equal if the from and to Nodes are the same
-}
sameFromAndTo :: Edge e -> Edge e -> Bool
sameFromAndTo edge1 edge2 =
    getFullPortFrom edge1 == getFullPortFrom edge2 && getFullPortTo edge1 == getFullPortTo edge2  -- equality on nodes and ports

reverseEdge :: Edge e -> Edge e
reverseEdge edge =
    edge { edgeFrom = edgeTo edge 
         , portFrom = portTo edge
         , edgeTo   = edgeFrom edge
         , portTo   = portFrom edge}

-- | Avoid the introduction of connections on ports that already are used.
-- So only one connection per port is allowed.
anyPortAlreadyUsed :: Edge e -> [Edge e] -> Bool
anyPortAlreadyUsed edge edgesList = 
     (edgeFrom edge, portFrom edge) `elem` portsList
     || (edgeTo edge, portTo edge)  `elem` portsList
     where portsList = concatMap f edgesList
           f e = [(edgeFrom e, portFrom e), (edgeTo e, portTo e)]

-- | Update node with given number by applying the function to it
--   Dangerous (wrt network consistency, do not export)
updateNode :: NodeNr -> (Node n -> Node n) -> Network g n e -> Network g n e
updateNode nodeNr nodeFunction network =
    let node = getNode nodeNr network in
    network { networkNodes = insert nodeNr (nodeFunction node)
                                           (networkNodes network) }

updateEdge :: EdgeNr -> (Edge e -> Edge e) -> Network g n e -> Network g n e
updateEdge edgeNr edgeFunction network =
    network { networkEdges = adjust edgeFunction edgeNr
                                    (networkEdges network) }

updateVia :: EdgeNr -> ViaNr -> DoublePoint -> Network g n e -> Network g n e
updateVia edgeNr viaNr v network =
    network { networkEdges =
                  adjust (\e-> e { edgeVia = take viaNr (edgeVia e)
                                             ++[v]++drop (viaNr+1) (edgeVia e) })
                         edgeNr (networkEdges network) }

edgeConnectedOnPort :: Network g n e -> NodeNr -> PortName -> Maybe EdgeNr
edgeConnectedOnPort network nodeNr port = 
   case IntMap.keys (IntMap.filter f (networkEdges network) ) of
      [edgeNr] -> Just edgeNr
      _        -> Nothing
   where f :: Edge e -> Bool
         f edge = 
             getFullPortFrom edge == (nodeNr, port)
             || getFullPortTo edge == (nodeNr, port)

otherExtremeOfEdgeConnectedOnPort :: Network g n e -> NodeNr -> PortName
                                     -> Maybe (NodeNr, PortName)
otherExtremeOfEdgeConnectedOnPort network nodeNr port =
    case edgeConnectedOnPort network nodeNr port of
     Just edgeNr | getFullPortFrom (getEdge edgeNr network) == (nodeNr, port) -> Just $ getFullPortTo   (getEdge edgeNr network)
     Just edgeNr | getFullPortTo   (getEdge edgeNr network) == (nodeNr, port) -> Just $ getFullPortFrom (getEdge edgeNr network)
     _ -> Nothing

point2EdgeInNode :: Palette n -> NodeNr -> PortName -> PortName -> Network g n e -> DoublePoint
point2EdgeInNode palette nodeNr fromPort toPort network =
	case getPorts palette network nodeNr of
	  Just ports ->
	     case (Prelude.lookup fromPort ports, Prelude.lookup toPort ports) of
		(Just fromPortPos, Just toPortPos) ->
		    g (portZone (fromPort,fromPortPos)) (portZone (toPort,toPortPos))
		_ -> error "unexpected situation"
	  Nothing -> f 1 1
        where  g Ztop    Ztop    = top 
               g Zbottom Zbottom = bottom 
               g Zleft   Zleft   = left
               g Zright  Zright  = right
               g Ztop    Zbottom = right
               g Zbottom Ztop    = right
               g Zleft   Zright  = top
               g Zright  Zleft   = top
               g Ztop    Zleft   = q2
               g Ztop    Zright  = q1
               g Zbottom Zleft   = q3
               g Zbottom Zright  = q4
               g Zleft   Ztop    = q2
               g Zleft   Zbottom = q3
               g Zright  Ztop    = q1
               g Zright  Zbottom = q4

               param = 2.0
               f x y = translate (getNodePosition network nodeNr) $ DoublePoint (x * param) (y * param)

               top    = f 0 (-1)  
               bottom = f 0 1
               left   = f (-1) 0
               right  = f 1 0
               q1     = f 1 (-1)
               q2     = f (-1) (-1)
               q3     = f (-1) 1
               q4     = f 1 1

isPrincipalPort :: Palette n -> Network g n e -> NodeNr -> PortName -> Bool
isPrincipalPort palette network nodeNr port =
  maybe False f $ getPorts palette network nodeNr
  where
        f ((port2,_):_) = port2 == port
        f _ = False

getPorts :: Palette n -> Network g n e -> NodeNr -> Maybe Ports
getPorts palette network nodeNr =
     case getSymbol (getNodeShape network nodeNr) palette of
        Just (_,ports,_) -> Just ports
        _ -> Nothing

getPort :: Palette n -> Network g n e -> (NodeNr,PortName) -> Maybe Port
getPort palette network (nodeNr,portName) =
     case getSymbol (getNodeShape network nodeNr) palette of
        Just (_,ports,_) ->
	     case Prelude.lookup portName ports of
		Just portPosition -> Just (portName, portPosition)
		_ -> Nothing
        _ -> Nothing