| 1 | | % (c) 2009-2024 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(state_as_dot_graph,[ |
| 6 | | print_cstate_graph/2, print_cstate_graph/1]). |
| 7 | | |
| 8 | | :- use_module(probsrc(module_information)). |
| 9 | | :- module_info(group,dot). |
| 10 | | :- module_info(description,'Display canonical form of state graph using dot.'). |
| 11 | | |
| 12 | | :- use_module(extrasrc(state_graph_canon),[state_graph_for_dot/2]). |
| 13 | | |
| 14 | | |
| 15 | | /* ---------------------------- */ |
| 16 | | /* DOT generation */ |
| 17 | | /* ---------------------------- */ |
| 18 | | |
| 19 | | |
| 20 | | :- use_module(probsrc(error_manager)). |
| 21 | | :- use_module(probsrc(preferences)). |
| 22 | | :- use_module(probsrc(state_space),[current_expression/2]). |
| 23 | | :- use_module(probsrc(tools),[ajoin/2, string_escape/2]). |
| 24 | | |
| 25 | | print_cstate_graph(File) :- |
| 26 | | current_expression(_,State), |
| 27 | | print_cstate_graph(State,File). |
| 28 | | print_cstate_graph(State,File) :- |
| 29 | | state_graph_for_dot(State,GCE), |
| 30 | | open(File,write,Stream,[encoding(utf8)]), |
| 31 | | (graph2dot(Stream,GCE) -> close(Stream) |
| 32 | | ; close(Stream), |
| 33 | | add_error(graph_canon,'Dot conversion failed: ',graph2dot(File,GCE)) |
| 34 | | ). |
| 35 | | |
| 36 | | |
| 37 | | graph2dot(Stream,G) :- reset_dot, |
| 38 | | (get_preference(dot_horizontal_layout,true) -> RDir='LR'; RDir='TB'), |
| 39 | | PSize='', %(get_preference(dot_with_page_size,true) -> PSize='page="8.5, 11",ratio=fill,size="7.5,10",' ; PSize=''), |
| 40 | | format(Stream,'digraph state {\n graph [~wfontsize=12]\nrankdir=~w;~n',[PSize,RDir]), |
| 41 | | edges2dot(G,Stream), |
| 42 | | nodes2dot(Stream), |
| 43 | | findall(cluster(Ty,El),cluster(Ty,El),T), |
| 44 | | clusters2dot(T,Stream), |
| 45 | | format(Stream,'}~n',[]). |
| 46 | | |
| 47 | | :- use_module(probsrc(b_global_sets)). |
| 48 | ? | cluster(Type,Elements) :- b_global_set(Type), |
| 49 | | % compute all elements of global sets which are used in current state graph |
| 50 | | findall(Translation, (enum_global_type_limited(El,Type), node_col(El,Translation,_,_)),Elements). |
| 51 | | % maybe add boolean ?? |
| 52 | | edges2dot([],_). |
| 53 | | edges2dot([(A,B,C)|T],Stream) :- /* edge from A -> C with label B */ |
| 54 | | translate_node_value(A,AID), |
| 55 | | (B = abs_cn(BB,ColBB) |
| 56 | | -> translate_abs_cn_node(BB,ColBB,EdgeLabel) |
| 57 | | ; EdgeLabel = B), |
| 58 | | translate_node_value(C,CID), |
| 59 | | get_col_assoc(edge,EdgeLabel,EdgCol), |
| 60 | | format(Stream,'\"~w\" -> \"~w\" [label = \"~w\", color = \"~w\"];\n', |
| 61 | | [AID,CID,EdgeLabel,EdgCol]), |
| 62 | | edges2dot(T,Stream). |
| 63 | | |
| 64 | | reset_dot :- retractall(col_association(_,_,_)), |
| 65 | | retractall(node_col(_,_,_,_)), |
| 66 | | retractall(cur_col(_,_)), |
| 67 | | assertz(cur_col(node,0)), assertz(cur_col(edge,99)). |
| 68 | | |
| 69 | | :- use_module(probsrc(translate),[translate_bvalue_for_dot/2]). |
| 70 | | translate_node_value(abs_cn(AA,ColAA), Translation) :- !, |
| 71 | | translate_abs_cn_node(AA,ColAA,Translation). |
| 72 | | translate_node_value(sg_root,Translation) :- !, |
| 73 | | Translation = 'ROOT-NODE', add_node(sg_root,Translation). |
| 74 | | translate_node_value(Val,Translation) :- |
| 75 | | translate_bvalue_for_dot(Val,Translation), |
| 76 | | add_node(Val,Translation). |
| 77 | | |
| 78 | ? | nodes2dot(Stream) :- node_col(_,Translation,Col,Shape), |
| 79 | | (Shape=record(Label) |
| 80 | | -> format(Stream,'\"~w\" [shape=record, label=\"~w\", color = \"~w\", style = \"filled, solid\"]\n', |
| 81 | | [Translation,Label,Col]) |
| 82 | | ; format(Stream,'\"~w\" [color = \"~w\", style = \"filled, solid\", shape = \"~w\"]\n',[Translation,Col,Shape]) |
| 83 | | ), |
| 84 | | fail. |
| 85 | | nodes2dot(_). |
| 86 | | |
| 87 | | :- use_module(probsrc(kernel_reals),[is_real/2]). |
| 88 | | :- dynamic node_col/4. |
| 89 | ? | add_node(X,_) :- node_col(X,_,_,_),!. % already processed |
| 90 | | add_node(sg_root,Translation) :- |
| 91 | | assertz(node_col(sg_root,Translation,lightblue,diamond)). |
| 92 | | add_node(fd(X,Type),Translation) :- !, |
| 93 | | get_col_assoc(node,Type,Col), |
| 94 | | get_preference(dot_normal_node_shape,Shape), |
| 95 | | assertz(node_col(fd(X,Type),Translation,Col,Shape)). |
| 96 | | add_node(int(X),Translation) :- !, |
| 97 | | Col = 'wheat3', %get_col_assoc(node,integer,Col), |
| 98 | | get_preference(dot_normal_node_shape,Shape), |
| 99 | | assertz(node_col(int(X),Translation,Col,Shape)). |
| 100 | | add_node(string(X),Translation) :- !, |
| 101 | | Col = 'khaki1', %'lavender' 'burlywood', %get_col_assoc(node,integer,Col), |
| 102 | | get_preference(dot_normal_node_shape,Shape), |
| 103 | | assertz(node_col(string(X),Translation,Col,Shape)). |
| 104 | | add_node(pred_true,T) :- !, assertz(node_col(pred_true,T,'OliveDrab4',ellipse)). |
| 105 | | add_node(pred_false,T) :- !, assertz(node_col(pred_false,T,brown,ellipse)). |
| 106 | | add_node(RecordOrPair,Translation) :- get_fields(RecordOrPair,Fields),!, |
| 107 | | translate_fields(Fields,Atoms), |
| 108 | | ajoin(['|{ ' | Atoms], RecTranslation), |
| 109 | | assertz(node_col(RecordOrPair,Translation,burlywood,record(RecTranslation))). % TO DO: maybe use dot record |
| 110 | | add_node(Val,Translation) :- is_set(Val),!, |
| 111 | | Col = 'LightSteelBlue1', %get_col_assoc(node,integer,Col), |
| 112 | | get_preference(dot_normal_node_shape,Shape), |
| 113 | | assertz(node_col(Val,Translation,Col,Shape)). |
| 114 | | add_node(Term,Translation) :- is_real(Term,_),!, |
| 115 | | Col = 'wheat2', %get_col_assoc(node,integer,Col), |
| 116 | | get_preference(dot_normal_node_shape,Shape), |
| 117 | | assertz(node_col(Term,Translation,Col,Shape)). |
| 118 | | add_node(_Val,_Translation). |
| 119 | | |
| 120 | | % copied from state_graph_canon: |
| 121 | | list_set([]). |
| 122 | | list_set([_|_]). |
| 123 | | is_set(avl_set(_)). |
| 124 | | is_set(X) :- list_set(X). |
| 125 | | |
| 126 | | get_fields(rec(Fields),Fields). |
| 127 | | get_fields((A,B),Fields) :- get_pair_fields((A,B),Fields,[]). |
| 128 | | |
| 129 | | get_pair_fields((A,B)) --> !, get_pair_fields(A), get_pair_fields(B). |
| 130 | | get_pair_fields(A) --> [field(prj,A)]. |
| 131 | | |
| 132 | | translate_fields([],[' }|']). |
| 133 | | translate_fields([field(Name,Value)],Res) :- !, |
| 134 | | trans_field(Name,VS,Res,[' }|']), |
| 135 | | translate_bvalue_for_dot(Value,VS). |
| 136 | | translate_fields([field(Name,Value)|T],Res) :- |
| 137 | | trans_field(Name,VS,Res,['|' | Rest]), |
| 138 | | translate_bvalue_for_dot(Value,VS), |
| 139 | | translate_fields(T,Rest). |
| 140 | | |
| 141 | | trans_field(prj,VS, [' { ', VS, ' } ' | Rest], Rest) :- !. |
| 142 | | trans_field(Name,VS, [' { ',Name,' | ', VS, ' } ' | Rest], Rest). |
| 143 | | |
| 144 | | :- dynamic col_association/3. % store colours used for types, edge labels,... |
| 145 | | get_col_assoc(Domain,T,Col) :- col_association(Domain,T,Col),!. |
| 146 | | get_col_assoc(Domain,T,Col) :- get_next_col(Domain,Col), |
| 147 | | assertz(col_association(Domain,T,Col)). |
| 148 | | |
| 149 | | get_next_col(Type,Col) :- retract(cur_col(Type,N)),!, N1 is N+1, |
| 150 | | (default_colours(N1,Col) -> NX=N1 |
| 151 | | ; NX = 1, default_colours(1,Col)), |
| 152 | | assertz(cur_col(Type,NX)). |
| 153 | | get_next_col(_T,red). |
| 154 | | |
| 155 | | :- dynamic cur_col/2. |
| 156 | | cur_col(node,0). |
| 157 | | cur_col(edge,99). |
| 158 | | default_colours(1,'#efdf84'). |
| 159 | | default_colours(2,'#bdef6b'). |
| 160 | | default_colours(3,'#5863ee'). |
| 161 | | default_colours(4,'LightSteelBlue1'). |
| 162 | | default_colours(5,gray). |
| 163 | | |
| 164 | | default_colours(100,firebrick). |
| 165 | | default_colours(101,sienna). |
| 166 | | default_colours(102,'SlateBlue4'). |
| 167 | | default_colours(103,black). |
| 168 | | |
| 169 | | translate_abs_cn_node(AA,ColAA,Translation) :- |
| 170 | | tools:string_concatenate('_',AA,AID1), |
| 171 | | tools:string_concatenate(ColAA,AID1,AID2), |
| 172 | | tools:string_concatenate('abs',AID2,Translation). |
| 173 | | |
| 174 | | clusters2dot([],_). |
| 175 | | clusters2dot([cluster(Name,Elements)|T],Stream) :- |
| 176 | | string_escape(Name,EscName), |
| 177 | | format(Stream,'subgraph \"cluster_~w\" {node [style=filled,color=white]; label=\"~w\"; style=filled;color=lightgrey; ',[EscName,EscName]), |
| 178 | | cnodes2dot(Elements,Stream), |
| 179 | | clusters2dot(T,Stream). |
| 180 | | |
| 181 | | :- use_module(probsrc(tools),[string_escape/2]). |
| 182 | | cnodes2dot([],Stream) :- format(Stream,'}~n',[]). |
| 183 | | cnodes2dot([H|T],Stream) :- |
| 184 | | translate:translate_params_for_dot([H],HP), |
| 185 | | string_escape(HP,HPE), |
| 186 | | format(Stream,'~w; ',[HPE]), |
| 187 | | cnodes2dot(T,Stream). |