| 1 | % (c) 2009-2019 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, | |
| 2 | % Heinrich Heine Universitaet Duesseldorf | |
| 3 | % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) | |
| 4 | ||
| 5 | :- module(dot_graph_generator,[gen_dot_graph/6,gen_dot_graph/7, | |
| 6 | print_graph_header/1,print_graph_footer/0, | |
| 7 | translate_bvalue_to_colour/2, try_translate_bvalue_to_colour/2, | |
| 8 | translate_int_col/2]). | |
| 9 | ||
| 10 | :- use_module(module_information). | |
| 11 | :- module_info(group,dot). | |
| 12 | :- module_info(description,'This a few tools for generating dot graphs.'). | |
| 13 | ||
| 14 | :- use_module(library(lists)). | |
| 15 | :- use_module(preferences). | |
| 16 | ||
| 17 | :- use_module(debug). | |
| 18 | :- use_module(self_check). | |
| 19 | ||
| 20 | /* --------------------------------------------------- */ | |
| 21 | /* MAIN ENTRY POINTS FOR TCL */ | |
| 22 | /* --------------------------------------------------- */ | |
| 23 | ||
| 24 | % note: one can provide none for some of the predicates | |
| 25 | ||
| 26 | gen_dot_graph(F,Module,NodePredicate,TransPredicate,SameRankPred,SubGraphPred) :- | |
| 27 | gen_dot_graph(F,[with_page_size],Module,NodePredicate,TransPredicate,SameRankPred,SubGraphPred). | |
| 28 | ||
| 29 | gen_dot_graph(F,WithSize,Module,NodePredicate,TransPredicate,SameRankPred,SubGraphPred) :- | |
| 30 | print('% Generating Dot File: '), print(F),nl, | |
| 31 | reset_ids, | |
| 32 | open(F,write,FStream,[encoding('UTF-8')]), | |
| 33 | (fgen_dot_graph(FStream,WithSize,Module,NodePredicate,TransPredicate,SameRankPred,SubGraphPred) -> true ; true), | |
| 34 | close(FStream), | |
| 35 | print('% Done'),nl. | |
| 36 | ||
| 37 | ||
| 38 | node_id(Module,P,NodeID) :- | |
| 39 | node_predicate_call(Module,P,NodeID,_Sub,_NodeDesc,_Shape,_Style,_Color). | |
| 40 | node_predicate_call(Module,P,TNodeID,SubGraph,NodeDesc,Shape,Style,Color) :- | |
| 41 | %Call =.. [P,NodeID,SubGraph,NodeDesc,Shape,Style,Color], | |
| 42 | call(Module:P,NodeID,SubGraph,NodeDesc,Shape,Style,Color), | |
| 43 | translate_id(NodeID,TNodeID). | |
| 44 | ||
| 45 | trans_predicate_call(Module,P,TNodeID,Label,TSuccNodeID,Color,Style) :- | |
| 46 | %Call =.. [P,NodeID,Label,SuccNodeID,Color], | |
| 47 | call(Module:P,NodeID,Label,SuccNodeID,Color,Style), | |
| 48 | translate_id(NodeID,TNodeID), | |
| 49 | translate_id(SuccNodeID,TSuccNodeID). | |
| 50 | ||
| 51 | same_rank_call(Module,P,TNodes) :- P \= none, | |
| 52 | %Call =.. [P,Nodes], | |
| 53 | call(Module:P,Nodes), | |
| 54 | Nodes \= [], % empty list provides no information | |
| 55 | maplist(translate_id,Nodes,TNodes). | |
| 56 | /* should succeed once for every set of NodeIDs which should be of same rank */ | |
| 57 | ||
| 58 | % should succeed once for every subgraph and generate a subgraphID which is passed to the node predicate | |
| 59 | subgraph_call(Module,P,SubGraphID,Style,Color) :- P \= none, | |
| 60 | %Call =.. [P,SubGraphID,Style,Color], | |
| 61 | call(Module:P,SubGraphID,Style,Color). | |
| 62 | /* Notes: SubGraphID: should be none if not in a subgraph; Style and Color can be none */ | |
| 63 | ||
| 64 | :- dynamic stored_id/2. | |
| 65 | :- dynamic next_id/1. | |
| 66 | next_id(0). | |
| 67 | ||
| 68 | reset_ids :- retractall(stored_id(_,_)), retractall(next_id(_)), | |
| 69 | assert(next_id(0)). | |
| 70 | ||
| 71 | % translate ids to numbers; ensure that dot can deal with them | |
| 72 | translate_id(ID,TransID) :- | |
| 73 | (number(ID) -> TransID=ID | |
| 74 | ; stored_id(ID,SID) -> TransID=SID | |
| 75 | ; retract(next_id(TransID)), | |
| 76 | assert(stored_id(ID,TransID)), | |
| 77 | N1 is TransID+1, | |
| 78 | assert(next_id(N1))). | |
| 79 | ||
| 80 | /* ---------------------------------------------------------------------- */ | |
| 81 | ||
| 82 | ||
| 83 | fgen_dot_graph(FStream,WithSize,Module,NodePredicate,TransPredicate,SameRankPred,SubGraphPred) :- | |
| 84 | print_graph_header(FStream,prob_graph,WithSize), | |
| 85 | (node_id(Module,NodePredicate,_) -> true | |
| 86 | ; (format(user_error,"No nodes in gen_dot_graph: ~w, ~w, ~w ~w ~w.~n~n", | |
| 87 | [Module,NodePredicate,TransPredicate,SameRankPred,SubGraphPred]),fail)), | |
| 88 | print_nodes(FStream,Module,NodePredicate,SubGraphPred), | |
| 89 | fail. | |
| 90 | fgen_dot_graph(FStream,_WithSize,Module,_NodePredicate,_TransPredicate,SameRankPred,_SubGraphPred) :- | |
| 91 | same_rank_call(Module,SameRankPred,Nodes), | |
| 92 | print_same_ranks(FStream,Nodes), | |
| 93 | fail. | |
| 94 | fgen_dot_graph(FStream,_WithSize,Module,_NodePredicate,TransPredicate,_SameRankPred,_SubGraphPred) :- | |
| 95 | print_transitions(FStream,_NodeID,Module,TransPredicate), | |
| 96 | fail. | |
| 97 | fgen_dot_graph(FStream,_,_,_,_,_,_) :- | |
| 98 | print_graph_footer(FStream). | |
| 99 | ||
| 100 | /* ---------------------------------------------------------------------- */ | |
| 101 | ||
| 102 | print_graph_header(Type) :- print_graph_header(user_output,Type,[with_page_size]). | |
| 103 | print_graph_header(FStream,Type,Opts) :- | |
| 104 | (member(with_page_size,Opts) -> PSize='page="8.5, 11",ratio=fill,size="7.5,10"' ; PSize=''), | |
| 105 | (member(horizontal,Opts) -> RDir = ' rankdir=LR;' ; RDir=''), | |
| 106 | format(FStream,'digraph ~w { graph [~w];~w~n',[Type,PSize,RDir]). | |
| 107 | % print('graph [orientation=landscape, page="8.5, 11",ratio=fill,size="7.5,10"];'),nl, | |
| 108 | ||
| 109 | print_graph_footer :- print_graph_footer(user_output). | |
| 110 | print_graph_footer(FStream) :- format(FStream,'}~n',[]). | |
| 111 | ||
| 112 | ||
| 113 | /* ---------------------------------------------------------------------- */ | |
| 114 | ||
| 115 | ||
| 116 | print_nodes(FStream,Module,NodePredicate,SubGraphPred) :- | |
| 117 | subgraph_call(Module,SubGraphPred,SubgraphID,Style,Color), | |
| 118 | format(FStream,' subgraph cluster_~w {~n',[SubgraphID]), | |
| 119 | (Style = none -> true ; format(FStream,' style="~w";~n',[Style])), | |
| 120 | (Color = none -> true ; format(FStream,' color="~w";~n',[Color])), | |
| 121 | format(FStream,' label="~w";~n',[SubgraphID]), | |
| 122 | print_nodes2(FStream,SubgraphID,Module,NodePredicate), | |
| 123 | write(FStream,' }'),nl(FStream), | |
| 124 | fail. | |
| 125 | print_nodes(FStream,Module,NodePredicate,_SubGraphPred) :- | |
| 126 | print_nodes2(FStream,none,Module,NodePredicate), | |
| 127 | nl(FStream). | |
| 128 | ||
| 129 | ||
| 130 | print_nodes2(FStream,SubGraph,Module,NodePredicate) :- | |
| 131 | node_predicate_call(Module,NodePredicate,NodeID,SubGraph,NodeDesc,Shape,Style,Color), | |
| 132 | format(FStream,' ~w [shape=~w',[NodeID,Shape]), /* options: triangle, .... */ | |
| 133 | (Style = none | |
| 134 | -> true | |
| 135 | ; format(FStream,', style="~w"',[Style]) /* options: filled */ | |
| 136 | ), | |
| 137 | (Color = none | |
| 138 | -> true | |
| 139 | ; format(FStream,', color="~w"',[Color]) /* options: red,green,... */ | |
| 140 | ), | |
| 141 | get_preference(dot_node_font_size,FSize), | |
| 142 | format(FStream,', fontsize=~w, label="',[FSize]), | |
| 143 | ((preference(dot_print_node_ids,true), | |
| 144 | \+ stored_id(_,_), % no translations performed | |
| 145 | NodeID \= NodeDesc) % otherwise we already print it as description | |
| 146 | -> format(FStream,'~w:\\n',[NodeID]) ; true), | |
| 147 | format(FStream,'~w"];~n',[NodeDesc]), | |
| 148 | fail. | |
| 149 | print_nodes2(FStream,_Subgraph,_,_) :- nl(FStream). | |
| 150 | ||
| 151 | print_transitions(FStream,NodeID,Module,TransPredicate) :- | |
| 152 | trans_predicate_call(Module,TransPredicate,NodeID,Label,SuccID,Color,Style), | |
| 153 | ||
| 154 | (NodeID=root -> preference(dot_print_root,true) ; true), | |
| 155 | ||
| 156 | (NodeID \= SuccID -> true ; preference(dot_print_self_loops,true)), | |
| 157 | ||
| 158 | format(FStream,' ~w -> ~w [',[NodeID,SuccID]), | |
| 159 | (preference(dot_print_arc_colors,true) | |
| 160 | -> format(FStream,'color="~w",',[Color]) | |
| 161 | ; true | |
| 162 | ), | |
| 163 | % acceptable styles Style ::= solid, bold, dotted, dashed, invis, arrowhead(none,Style) | |
| 164 | print_style(Style,FStream), | |
| 165 | get_preference(dot_edge_font_size,FSize), | |
| 166 | (Label='' -> format(FStream,'];~n',[]) | |
| 167 | ; format(FStream,' label="~w", fontsize=~w];~n',[Label,FSize])), | |
| 168 | fail. | |
| 169 | print_transitions(FStream,_NodeID,_,_) :- nl(FStream). | |
| 170 | ||
| 171 | print_style(solid,_) :- !. | |
| 172 | print_style(arrowhead(AS,S),FStream) :- !, format(FStream,'arrowhead=~w,',[AS]), print_style(S,FStream). | |
| 173 | print_style(arrowtail(AS,S),FStream) :- !, format(FStream,'dir=both,arrowtail=~w,',[AS]), print_style(S,FStream). | |
| 174 | print_style(Style,FStream) :- format(FStream,'style="~w",',[Style]). | |
| 175 | ||
| 176 | print_same_ranks(FStream,L) :- | |
| 177 | write(FStream,' { rank=same; '), | |
| 178 | print_same_ranks2(FStream,L), | |
| 179 | write(FStream,' }'),nl(FStream). | |
| 180 | ||
| 181 | print_same_ranks2(_FStream,[]). | |
| 182 | print_same_ranks2(FStream,[H|T]) :- | |
| 183 | write(FStream,H), write(FStream,'; '), | |
| 184 | print_same_ranks2(FStream,T). | |
| 185 | ||
| 186 | ||
| 187 | % utilities for converting values into colours: | |
| 188 | ||
| 189 | ||
| 190 | ||
| 191 | translate_bvalue_to_colour(Val,Col) :- | |
| 192 | (try_translate_bvalue_to_colour(Val,C) -> Col=C; Col=lightgray). | |
| 193 | ||
| 194 | try_translate_bvalue_to_colour(int(X),Res) :- !, (translate_int_col(X,Colour) -> Res=Colour ; Res=black). | |
| 195 | try_translate_bvalue_to_colour(string(X),Colour) :- is_of_type(X,rgb_color),!, X=Colour. | |
| 196 | try_translate_bvalue_to_colour(fd(X,GS),Colour) :- !, translate_fd_col(X,GS,Colour). | |
| 197 | try_translate_bvalue_to_colour(pred_true,Colour) :- !, Colour=olivedrab2. | |
| 198 | try_translate_bvalue_to_colour(pred_false,Colour) :- !, Colour=tomato. | |
| 199 | try_translate_bvalue_to_colour((A,_),Colour) :- !, try_translate_bvalue_to_colour(A,Colour). | |
| 200 | ||
| 201 | % TO DO : add string conversions, rgb values , ... | |
| 202 | :- use_module(b_global_sets,[is_b_global_constant/3]). | |
| 203 | ||
| 204 | translate_fd_col(X,GS,Res) :- | |
| 205 | is_b_global_constant(GS,X,Colour), | |
| 206 | is_of_type(Colour,rgb_color),!, | |
| 207 | Res=Colour. | |
| 208 | translate_fd_col(X,_,Res) :- number(X), translate_int_col(X,Colour),!, Res=Colour. | |
| 209 | translate_fd_col(_,_,lightgray). | |
| 210 | ||
| 211 | translate_int_col(-1,Colour) :- !,Colour=tomato. % special case | |
| 212 | translate_int_col(Int,Colour) :- | |
| 213 | Y is abs(Int) mod 15, translate_int_col_aux(Y,Colour). | |
| 214 | translate_int_col_aux(0,gray95). | |
| 215 | translate_int_col_aux(1,blue). | |
| 216 | translate_int_col_aux(2,red). | |
| 217 | translate_int_col_aux(3,green). | |
| 218 | translate_int_col_aux(4,lightgray). | |
| 219 | translate_int_col_aux(5,orange). | |
| 220 | translate_int_col_aux(6,yellow). | |
| 221 | translate_int_col_aux(7,brown). | |
| 222 | translate_int_col_aux(8,violet). | |
| 223 | translate_int_col_aux(9,tomato). | |
| 224 | translate_int_col_aux(10,darkslateblue). | |
| 225 | translate_int_col_aux(11,maroon2). | |
| 226 | translate_int_col_aux(12,olivedrab2). | |
| 227 | translate_int_col_aux(13,chartreuse3). | |
| 228 | translate_int_col_aux(14,grey20). | |
| 229 | % red,green,blue,yellow,orange,black,white,gray,brown,violet,darkred,tomato,darkblue, | |
| 230 | % 'DarkGray',darkviolet,darkslateblue,lightblue,lightgray,maroon2,olivedrab2, | |
| 231 | % steelblue,chartreuse3,chartreuse4 |