# Copyright 2016, Massimo Santini <santini@di.unimi.it> # # This file is part of "GraphvizAnim". # # "GraphvizAnim" is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # # "GraphvizAnim" is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # "GraphvizAnim". If not, see <http://www.gnu.org/licenses/>. from __future__ import absolute_import from email.utils import quote import shlex from gvanim import action class ParseException( Exception ): pass class Step( object ): def __init__( self, step = None ): if step: self.V = step.V.copy() self.E = step.E.copy() self.lV = step.lV.copy() self.lE = step.lE.copy() else: self.V = set() self.E = set() self.lV = dict() self.lE = dict() self.hV = dict() self.hE = dict() def node_format( self, v ): fmt = [] if v in self.lV: fmt.append( 'label="{}"'.format( quote( str( self.lV[ v ] ) ) ) ) if v in self.hV: fmt.append( 'color={}'.format( self.hV[ v ] ) ) elif v not in self.V: fmt.append( 'style=invis' ) if fmt: return '[{}]'.format( ', '.join( fmt ) ) return '' def edge_format( self, e ): fmt = [] if e in self.lE: fmt.append('label="{}"'.format( quote( str( self.lE[ e ] ) ) ) ) if e in self.hE: fmt.append('color={}'.format( self.hE[ e ] ) ) elif e not in self.E: fmt.append('style=invis') if fmt: return '[{}]'.format( ', '.join( fmt ) ) return '' def __repr__( self ): return '{{ V = {}, E = {}, hV = {}, hE = {}, L = {}, lE = {} }}'.format( self.V, self.E, self.hV, self.hE, self.lV, self.lE ) class Animation( object ): def __init__( self ): self._actions = [] def next_step( self, clean = False ): self._actions.append( action.NextStep( clean ) ) def add_node( self, v ): self._actions.append( action.AddNode( v ) ) def highlight_node( self, v, color = 'red' ): self._actions.append( action.HighlightNode( v, color = color ) ) def label_node( self, v, label ): self._actions.append( action.LabelNode( v, label ) ) def unlabel_node( self, v ): self._actions.append( action.UnlabelNode( v ) ) def remove_node( self, v ): self._actions.append( action.RemoveNode( v ) ) def add_edge( self, u, v ): self._actions.append( action.AddEdge( u, v ) ) def highlight_edge( self, u, v, color = 'red' ): self._actions.append( action.HighlightEdge( u, v, color = color ) ) def label_edge( self, u, v, label ): self._actions.append( action.LabelEdge( u, v, label ) ) def unlabel_edge( self, u, v ): self._actions.append( action.UnlabelEdge( u, v ) ) def remove_edge( self, u, v ): self._actions.append( action.RemoveEdge( u, v ) ) def parse( self, lines ): action2method = { 'ns' : self.next_step, 'an' : self.add_node, 'hn' : self.highlight_node, 'ln' : self.label_node, 'un' : self.unlabel_node, 'rn' : self.remove_node, 'ae' : self.add_edge, 'he' : self.highlight_edge, 'le' : self.label_edge, 'ue' : self.unlabel_edge, 're' : self.remove_edge, } for line in lines: parts = shlex.split( line.strip(), True ) if not parts: continue action, params = parts[ 0 ], parts[ 1: ] try: action2method[ action ]( *params ) except KeyError: raise ParseException( 'unrecognized command: {}'.format( action ) ) except TypeError: raise ParseException( 'wrong number of parameters: {}'.format( line.strip() ) ) return def steps( self ): steps = [ Step() ] for action in self._actions: action( steps ) return steps def graphs( self ): steps = self.steps() V, E = set(), set() for step in steps: V |= step.V E |= step.E graphs = [] for n, s in enumerate( steps ): graph = [ 'digraph G {' ] for v in V: graph.append( '"{}" {};'.format( quote( str( v ) ), s.node_format( v ) ) ) for e in E: graph.append( '"{}" -> "{}" {};'.format( quote( str( e[ 0 ] ) ), quote( str( e[ 1 ] ) ), s.edge_format( e ) ) ) graph.append( '}' ) graphs.append( '\n'.join( graph ) ) return graphs