# # dotty_layout: layout functions and data structures # dotty.grablserver = function (lserver) { local fd; if (~dotty.lservers[lserver] | tablesize (dotty.lservers[lserver]) == 0) { if (~((fd = openio ('pipe', lserver, 'r+')) >= 0)) { dotty.message (0, concat ('cannot start ', lserver)); return null; } dotty.lservers[lserver][fd] = [ 'fd' = fd; 'count' = 0; ]; } for (fd in dotty.lservers[lserver]) { dotty.lservers[lserver][fd].count = dotty.lservers[lserver][fd].count + 1; dotty.lservers.inuse[fd] = dotty.lservers[lserver][fd]; remove (fd, dotty.lservers[lserver]); return fd; } }; dotty.releaselserver = function (lserver, fd, state) { if (state == 'bad' | dotty.lservers.inuse[fd].count > 40) { closeio (fd, 'kill'); remove (fd, dotty.lservers.inuse); return; } dotty.lservers[lserver][fd] = dotty.lservers.inuse[fd]; remove (fd, dotty.lservers.inuse); }; dotty.protogt.startlayout = function (gt) { local lpt, fd; if (gt.layoutpending >= 1) { lpt = dotty.layoutpending[gt.gtid]; if (gt.layoutmode == 'async') monitor ('off', lpt.fd); dotty.releaselserver (gt.lserver, lpt.fd, 'bad'); remove (gt.gtid, dotty.layoutpending); gt.layoutpending = 0; gt.haveinput = 0; dotty.popbusy (gt, gt.views); } if (~((fd = dotty.grablserver (gt.lserver)) >= 0)) return null; dotty.pushbusy (gt, gt.views); writegraph (fd, gt.graph, 1); gt.layoutpending = 1; dotty.layoutpending[gt.gtid] = [ 'fd' = fd; 'gtid' = gt.gtid; ]; if (gt.layoutmode == 'async') monitor ('on', fd); return 1; }; dotty.protogt.finishlayout = function (gt) { local graph, lpt, fd; if (~(gt.layoutpending >= 1)) { dotty.message (0, concat ('no layout pending for graph ', gt.gtid)); return null; } lpt = dotty.layoutpending[gt.gtid]; if (~(graph = readgraph (lpt.fd))) { if (gt.layoutmode == 'async') monitor ('off', lpt.fd); dotty.releaselserver (gt.lserver, lpt.fd, 'bad'); if (gt.layoutpending == 2) { dotty.message (0, concat ('giving up on ', gt.lserver)); if ((fd = openio ('file', 'dottybug.dot', 'w+')) >= 0) { writegraph (fd, gt.graph, 0); closeio (fd); dotty.message (0, concat ('graph that causes ', gt.lserver)); dotty.message (0, 'to fail has been saved in file dottybug.dot'); dotty.message (0, 'please fill out a bug report at http://www.research.att.com/~erg/graphviz/bugform.html'); } dotty.popbusy (gt, gt.views); gt.layoutpending = 0; gt.haveinput = 0; return 1; } dotty.message (1, concat ('lost connection to ', gt.lserver, ', restarting...')); lpt.fd = dotty.grablserver (gt.lserver); writegraph (lpt.fd, gt.graph, 1); if (gt.layoutmode == 'async') monitor ('on', lpt.fd); gt.layoutpending = 2; gt.haveinput = 0; return null; } if (gt.layoutmode == 'async') monitor ('off', lpt.fd); dotty.releaselserver (gt.lserver, lpt.fd, null); remove (gt.gtid, dotty.layoutpending); gt.layoutpending = 0; gt.haveinput = 0; gt.unpacklayout (gt, graph); dotty.popbusy (gt, gt.views); return 1; }; dotty.protogt.cancellayout = function (gt) { local lpt, vid; if (gt.layoutpending >= 1) { lpt = dotty.layoutpending[gt.gtid]; if (gt.layoutmode == 'async') monitor ('off', lpt.fd); dotty.releaselserver (gt.lserver, lpt.fd, 'bad'); remove (gt.gtid, dotty.layoutpending); gt.layoutpending = 0; gt.haveinput = 0; dotty.popbusy (gt, gt.views); } }; dotty.protogt.unpacklayout = function (gt, graph2) { local graph, gid, sgraph1, sgraph2, nid, node1, node2; local t1, t2, t3, n2, i, j, k, l, m, eid, edge1, edge2, points; local pa1, pa2, pb1, pb2, la, lb; graph = gt.graph; for (gid in graph2.graphdict) { if (~(sgraph1 = graph.graphs[graph.graphdict[gid]])) continue; sgraph2 = graph2.graphs[graph2.graphdict[gid]]; if (sgraph2.graphattr.bb & sgraph2.graphattr.bb ~= '') { t1 = split (sgraph2.graphattr.bb, ','); sgraph1.rect = [ 0 = ['x' = ston (t1[0]); 'y' = ston (t1[1]);]; 1 = ['x' = ston (t1[2]); 'y' = ston (t1[3]);]; ]; } else sgraph1.rect = []; if (sgraph2.graphattr.lp & sgraph2.graphattr.lp ~= '') { t1 = split (sgraph2.graphattr.lp, ','); sgraph1.lp = ['x' = ston (t1[0]); 'y' = ston (t1[1]);]; } else sgraph1.lp = []; } for (nid in graph2.nodedict) { if (~(node1 = graph.nodes[graph.nodedict[nid]])) continue; node2 = graph2.nodes[graph2.nodedict[nid]]; t1 = split (node2.attr.pos, ','); node1.pos = ['x' = ston (t1[0]); 'y' = ston (t1[1]);]; node1.size.x = ston (node2.attr.width) * 72; node1.size.y = ston (node2.attr.height) * 72; if (node2.attr.rects) node1.fields = parsegraphlabel (node2.attr.label, node2.attr.rects); } for (eid in graph2.edges) { edge2 = graph2.edges[eid]; if (edge2.attr.id) { if (~(edge1 = graph.edges[ston (edge2.attr.id)])) continue; } else if (graph == graph2) edge1 = edge2; if (edge2.attr.pos) { points = []; remove ('sp', edge1); remove ('ep', edge1); t2 = split (edge2.attr.pos, ';'); for (k = 0; t2[k]; k = k + 1) { t3 = split (t2[k], ' '); n2 = tablesize (t3); j = 0; i = 0; t1 = split (t3[0], ','); while (t1[0] == 's' | t1[0] == 'e') { if (t1[0] == 's') edge1.sp = ['x' = ston (t1[1]); 'y' = ston (t1[2]);]; else # (t1[0] == 'e') edge1.ep = ['x' = ston (t1[1]); 'y' = ston (t1[2]);]; i = i + 1; t1 = split (t3[i], ','); } points[k][j] = ['x' = ston (t1[0]); 'y' = ston (t1[1]);]; i = i + 1; j = j + 1; while (i < n2) { t1 = split (t3[i], ','); points[k][j] = ['x' = ston (t1[0]); 'y' = ston (t1[1]);]; j = j + 1; i = i + 1; } } if (k > 1) { # concentrators l = k; while (l > 1) { la = tablesize (points[0]); pa1 = points[0][0]; pa2 = points[0][la - 1]; for (k = 1; points[k]; k = k + 1) { lb = tablesize (points[k]); pb1 = points[k][0]; pb2 = points[k][lb - 1]; if (pa1.x == pb2.x & pa1.y == pb2.y) { for (m = 1; m < la; m = m + 1) { points[k][lb] = points[0][m]; lb = lb + 1; } points[0] = points[l - 1]; remove (l - 1, points); break; } else if (pa2.x == pb1.x & pa2.y == pb1.y) { for (m = 1; m < lb; m = m + 1) { points[0][la] = points[k][m]; la = la + 1; } points[k] = points[l - 1]; remove (l - 1, points); break; } } if (points[l - 1]) { dotty.message (1, 'failed to match edge points'); break; } l = l - 1; } } edge1.points = points[0]; } if (edge2.attr.lp) { t1 = split (edge2.attr.lp, ','); edge1.lp = ['x' = ston (t1[0]); 'y' = ston (t1[1]);]; } } t1 = split (graph2.graphattr.bb, ','); graph.rect[0].x = ston (t1[0]); graph.rect[0].y = ston (t1[1]); graph.rect[1].x = ston (t1[2]); graph.rect[1].y = ston (t1[3]); if (graph2.graphattr.lp & graph2.graphattr.lp ~= '') { t1 = split (graph2.graphattr.lp, ','); graph.lp = ['x' = ston (t1[0]); 'y' = ston (t1[1]);]; } else graph.lp = []; if (gt.graph ~= graph2) return; # strip position and size info from the attributes for (gid in graph2.graphdict) { sgraph2 = graph2.graphs[graph2.graphdict[gid]]; if (sgraph2.graphattr.bb) remove ('bb', sgraph2.graphattr); } for (nid in graph2.nodedict) { node2 = graph2.nodes[graph2.nodedict[nid]]; if (node2.attr.rects) remove ('rects', node2.attr); remove ('pos', node2.attr); remove ('width', node2.attr); remove ('height', node2.attr); } for (eid in graph2.edges) { edge2 = graph2.edges[eid]; if (edge2.attr.pos) remove ('pos', edge2.attr); if (edge2.attr.lp) remove ('lp', edge2.attr); } remove ('bb', graph2.graphattr); if (graph2.graphattr.lp) remove ('lp', graph2.graphattr); };