123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372(**************************************************************************)(* *)(* This file is part of OcamlGraph. *)(* *)(* Copyright (C) 2009-2010 *)(* CEA (Commissariat � l'�nergie Atomique) *)(* *)(* you can redistribute it and/or modify it under the terms of the GNU *)(* Lesser General Public License as published by the Free Software *)(* Foundation, version 2.1, with a linking exception. *)(* *)(* It 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 Lesser General Public License for more details. *)(* *)(* See the file ../LICENSE for more details. *)(* *)(* Authors: *)(* - Julien Signoles (Julien.Signoles@cea.fr) *)(* - Jean-Denis Koeck (jdkoeck@gmail.com) *)(* - Benoit Bataille (benoit.bataille@gmail.com) *)(* *)(**************************************************************************)(** Reading XDot files *)openDot_astopenPrintf(* Layout types *)(* This file is responsible for converting the coordinates from dot coordinates
to GnomeCanvas world coordinates.
The matrix transformation to apply is:
(1 0)
(0 -1)
Care must be taken to exchange max and min values on the y axis.
Outside this module all coordinates are assumed to be in canvas
world coordinates.
*)typepos=float*float(* coordinates *)typebounding_box=pos*pos(* bounding box *)typenode_layout={n_name:string;n_pos:pos;n_bbox:bounding_box;n_draw:XDotDraw.operationlist;n_ldraw:XDotDraw.operationlist;}typecluster_layout={c_pos:pos;c_bbox:bounding_box;c_draw:XDotDraw.operationlist;c_ldraw:XDotDraw.operationlist;}typeedge_layout={e_draw:XDotDraw.operationlist;e_ldraw:XDotDraw.operationlist;e_hdraw:XDotDraw.operationlist;e_tdraw:XDotDraw.operationlist;e_hldraw:XDotDraw.operationlist;e_tldraw:XDotDraw.operationlist;}letmk_node_layout~name~pos~bbox~draw~ldraw={n_name=name;n_pos=pos;n_bbox=bbox;n_draw=draw;n_ldraw=ldraw}letmk_cluster_layout~pos~bbox~draw~ldraw={c_pos=pos;c_bbox=bbox;c_draw=draw;c_ldraw=ldraw}letmk_edge_layout~draw~ldraw~hdraw~tdraw~hldraw~tldraw={e_draw=draw;e_ldraw=ldraw;e_hdraw=hdraw;e_tdraw=tdraw;e_hldraw=hldraw;e_tldraw=tldraw;}exceptionParseErrorofstring(* MISCELLANEOUS FUNCTIONS *)letread_poss=Scanf.sscanfs"%f,%f"(funxy->x,-.y)letbounding_box(x,y)wh=letlower_left=x-.w,y-.hinletupper_right=x+.w,y+.hinlower_left,upper_rightletget_dot_string=function|Dot_ast.Strings->s|Dot_ast.Idents->s|Dot_ast.Numbers->s|Dot_ast.Htmls->s(* READING VERTEX LAYOUTS *)(** Finds the attributes [pos], [width] and [height] of a node
in the attribute list *)letread_common_layoutmk_layoutattr_list=(* Iter on the attributes *)(* shape, position, width, height, color, filled *)letfold((p,w,h,draw,ldraw)asattrs)=function|(Dot_ast.Ident"pos"),Some(Dot_ast.Strings)->(Somes),w,h,draw,ldraw|(Dot_ast.Ident"width"),Some(Dot_ast.Strings)->p,(Somes),h,draw,ldraw|(Dot_ast.Ident"height"),Some(Dot_ast.Strings)->p,w,(Somes),draw,ldraw|(Dot_ast.Ident"_draw_"),Some(Dot_ast.Stringdraw)->p,w,h,XDotDraw.parsedraw,ldraw|(Dot_ast.Ident"_ldraw_"),Some(Dot_ast.Stringldraw)->p,w,h,draw,XDotDraw.parseldraw|_->attrsinletfold_attraccattr_list=List.fold_leftfoldaccattr_listinletattrs=List.fold_leftfold_attr(None,None,None,[],[])attr_listin(* Check if we have position, width and height *)matchattrswith|Somepos,Somew,Someh,draw,ldraw->letpos=read_posposinletcoord=bounding_boxpos(float_of_stringw)(-.(float_of_stringh))in(* Return the node model *)mk_layout~pos~bbox:coord~draw~ldraw|_,_,_,draw,ldraw->letpos=(0.,0.)inletbbox=(0.,0.),(0.,0.)inmk_layout~pos~bbox~draw~ldrawletread_node_layout(id,_)attrs=letf=read_common_layout(fun~pos~bbox~draw~ldraw->mk_node_layout~pos~bbox~draw~ldraw)attrsinf~name:(get_dot_stringid)letread_cluster_layout=read_common_layoutmk_cluster_layout(* READING EDGE LAYOUTS *)(** Reads the spline control points of a curve in an xdot file
example : "c 5 -black B 4 65 296 65 288 65 279 65 270 "
*)(* The edge drawing operations are in the following attributes :
_hdraw_ Head arrowhead
_tdraw_ Tail arrowhead
_hldraw_ Head label
_tldraw_ Tail label
*)(** Gets the layout of an edge out of the dot ast *)letread_edge_layoutattr_list=letdraw=ref[]inletldraw=ref[]inlethdraw=ref[]inlettdraw=ref[]inlethldraw=ref[]inlettldraw=ref[]inletfill_draw_ops=function|(Dot_ast.Ident"_draw_"),Some(Dot_ast.Strings)->draw:=XDotDraw.parses|(Dot_ast.Ident"_ldraw_"),Some(Dot_ast.Strings)->ldraw:=XDotDraw.parses|(Dot_ast.Ident"_hdraw_"),Some(Dot_ast.Strings)->hdraw:=XDotDraw.parses|(Dot_ast.Ident"_tdraw_"),Some(Dot_ast.Strings)->tdraw:=XDotDraw.parses|(Dot_ast.Ident"_hldraw_"),Some(Dot_ast.Strings)->hldraw:=XDotDraw.parses|(Dot_ast.Ident"_tldraw_"),Some(Dot_ast.Strings)->tldraw:=XDotDraw.parses|_->()inList.iter(List.iterfill_draw_ops)attr_list;letdraw,ldraw=!draw,!ldrawinlethdraw,tdraw,hldraw,tldraw=!hdraw,!tdraw,!hldraw,!tldrawinmk_edge_layout~draw~ldraw~hdraw~tdraw~hldraw~tldraw(* Computes the bounding box *)letread_bounding_boxstr=letx1,y1,x2,y2=Scanf.sscanfstr"%f,%f,%f,%f"(funabcd->a,b,c,d)in(* Convert coordinates to the world canvas coordinates *)letlower_left=(x1,-.y2)andupper_right=x2,-.y1inlower_left,upper_rightmoduleMake(G:Graphviz.GraphWithDotAttrs)=structmoduleHV=Hashtbl.Make(G.V)(* cannot use an hashtable because no hash function for edges *)moduleHE=Map.Make(structtypet=G.E.tletcompare=G.E.compareend)moduleHT=Hashtbl.Make(Util.HTProduct(Util.HTProduct(G.V)(G.V))(structtypet=stringletequal=(=)lethash=Hashtbl.hashend))typegraph_layout={vertex_layouts:node_layoutHV.t;edge_layouts:edge_layoutHE.t;cluster_layouts:(string,cluster_layout)Hashtbl.t;bbox:bounding_box}exceptionFoundofstringletget_edge_commente=letal=G.edge_attributeseintryList.iter(function`Commentc->raise(Foundc)|_->())al;NonewithFoundc->Somecletget_dot_comment(al:Dot_ast.attrlist)=tryList.iter(List.iter(function|Ident"comment",Somec->raise(Found(get_dot_stringc))|_->()))al;""withFoundc->cletstrip_quotes=function|""->""|s->letlen=String.lengthsinifs.[0]='"'&&s.[len-1]='"'thenString.subs1(len-2)elses(* Parses the graph attribute named id, and converts it with conv *)letparse_graph_attridconvstmts=letread_attr=function|Identident,Some(Stringattr)whenident=id->raise(Foundattr)|_->()inletread_stmt=function|Attr_graphattrs->List.iter(List.iterread_attr)attrs|_->()intryList.iterread_stmtstmts;failwith("Could not find the graph attribute named "^id)withFoundattr->convattrletparse_bounding_box=parse_graph_attr"bb"read_bounding_box(*let parse_bgcolor = parse_graph_attr "bgcolor" XDotDraw.normalize_color*)letparse_layoutsgstmts=letname_to_vertex=Hashtbl.create97inletvertices_comment_to_edge=HT.create97inletvertex_layouts=HV.create97inletedge_layouts=refHE.emptyinletcluster_layouts=Hashtbl.create97inG.iter_vertex(funv->letname=strip_quotes(G.vertex_namev)inHashtbl.addname_to_vertexnamev)g;G.iter_edges_e(fune->letcomment=matchget_edge_commentewith|Somec->strip_quotesc|None->""inletvs=G.E.srce,G.E.dsteinHT.addvertices_comment_to_edge(vs,comment)e)g;letfind_vertex(id,_)=letname=get_dot_stringidintryHashtbl.findname_to_vertexnamewithNot_found->failwith("Could not find vertex named "^name)inletfind_edgevv'comment=tryHT.findvertices_comment_to_edge((v,v'),comment)withNot_found->(* Printf.printf "Did not find edge from %s to %s with comment %s\n"
(G.vertex_name v) (G.vertex_name v')
(match comment with Some c -> c | None -> "none");*)raiseNot_foundinletreccollect_layoutsclusterstmt=trymatchstmtwith|Node_stmt(node_id,al)->letv=find_vertexnode_idinHV.addvertex_layoutsv(read_node_layoutnode_idal)|Edge_stmt(NodeIdid,[NodeIdid'],al)->letv=find_vertexidinletv'=find_vertexid'inletcomment=get_dot_commentalinlete=find_edgevv'commentinedge_layouts:=HE.adde(read_edge_layoutal)!edge_layouts|Subgraph(SubgraphDef(Someid,stmts))->letcluster=get_dot_stringidinList.iter(collect_layouts(Somecluster))stmts(* Anonymous subgraph *)|Subgraph(SubgraphDef(_,stmts))->List.iter(collect_layoutscluster)stmts|Attr_graphal->(matchclusterwith|Somec->Hashtbl.addcluster_layoutsc(read_cluster_layoutal)|None->())|_->()withNot_found->()inList.iter(collect_layoutsNone)stmts;vertex_layouts,edge_layouts,cluster_layoutsletparsegdot_ast=letv_layouts,e_layouts,c_layouts=parse_layoutsgdot_ast.stmtsinletbbox=parse_bounding_boxdot_ast.stmtsin(* let bgcolor = parse_bgcolor dot_ast.stmts in*){vertex_layouts=v_layouts;edge_layouts=!e_layouts;cluster_layouts=c_layouts;bbox=bbox}exceptionDotErrorofstringletlayout_of_xdot~xdot_fileg=letdot_ast=Dot.parse_dot_astxdot_fileinparsegdot_astletlayout_of_dot?(cmd="dot")~dot_fileg=letbase_name=tryFilename.basename(Filename.chop_extensiondot_file)withInvalid_argument_->dot_fileinletxdot_file=Filename.temp_filebase_name".xdot"in(* Run graphviz to get xdot file *)letdot_cmd=sprintf"%s -Txdot %s > %s"cmddot_filexdot_fileinmatchSys.commanddot_cmdwith|0->letl=layout_of_xdot~xdot_fileginSys.removexdot_file;l|_->Sys.removexdot_file;raise(DotError"Error during dot execution")end