Your IP : 216.73.216.86


Current Path : /home/emeraadmin/public_html/4d695/
Upload File :
Current File : /home/emeraadmin/public_html/4d695/graphviz.tar

gvpr/addedges000064400000000443151705127100007205 0ustar00/* Add edges from input graph to argument graph
 * Does not add nodes.
 */
BEGIN{
  graph_t g = readG(ARGV[0]);
  node_t h, t;
  edge_t e;
}
E {
  if ((h = isNode(g,head.name)) && (t = isNode(g,tail.name))) {
    if (!isEdge(t,h,"")) {
      e = copy(g,$);
    }
  }
}
END {
  write(g);
}


gvpr/addranks000064400000001476151705127100007243 0ustar00/* Assuming nodes have been positioned by dot, this adds a rank
 * attribute by placing all nodes with the same y value on a specific
 * integer rank.
 * If the graph has rankdir=LR, x and y are flipped.
 */
BEG_G {
  double x,y;
  int lv[double];
  int r, rk[double];
  int flip;
  if (isAttr($,"G","rankdir") && $.rankdir=="LR") flip = 1;
}
N {
  sscanf($.pos,"%f,%f",&x,&y);
  if (flip) lv[x] = 1;
  else lv[y] = 1;
}
BEG_G {
  r = 0;
  if (flip)
    forr (lv[x]) {
      rk[x] = r++;
      /* printf (2, "rk[%f] = %d\n", y, rk[y]); */
    }
  else
    forr (lv[y]) {
      rk[y] = r++;
      /* printf (2, "rk[%f] = %d\n", y, rk[y]); */
    }
}
N {
  sscanf($.pos,"%f,%f",&x,&y);
  /* printf(2, "node %s y %f rk %d\n", $.name, y, rk[y]); */
  if (flip) $.rank = sprintf("%d", rk[x]); 
  else $.rank = sprintf("%d", rk[y]); 
}
gvpr/addrings000064400000002447151705127100007246 0ustar00/* Takes a graph laid out by twopi and adds rings.
 * Assumes ARGV[] = "root" "=" <rootname>, as output by twopi -v.
 * Usage:
 *   twopi -v foo.dot > out 2> log
 *   gvpr -f addrings.g -a"`grep root log`" out | neato -n2 ...
 */
BEG_G {
  graph_t og;
  edge_t e;
  node_t ctr = node($, ARGV[0]);
  double rs = 1.0; /* min. slack between the squares of two consecutive radii */
  int cx, cy;
  int x, y;
  node_t n;
  int i, n_r;
  int d;
  int rads[int];
  char* ctr_s = ctr.pos;
  sscanf (ctr_s, "%d,%d", &cx, &cy);
  if (hasAttr($, "ranksep")) {
    sscanf ($.ranksep, "%f", &rs);
    if (rs == 0.0) rs = 1.0;
  }
  rs *= 72;
  rs = 1.5*rs*rs;
}
N [$ != ctr] {
  sscanf ($.pos, "%d,%d", &x, &y);
  d = (x-cx)*(x-cx) + (y-cy)*(y-cy);
  for (rads[i]) {
    if ((rads[i]-rs <= d) && (d <= rads[i]+rs)) return;
  }
  n_r++;
  rads[n_r] = d;
}
END_G {
  og = copy (NULL, $);
  og.outputorder = "nodesfirst";
  setDflt (og, "N", "label", "\\N");
  for (rads[i]) {
    n = node(og, "ring_"+((string)i));
    n.shape = "circle";
    n.pos = ctr_s;
    n.style = "";
    n.label = "";
    d = rads[i];
    n.width = sprintf("%f", sqrt(d)/36.0);
  }
  for (n=fstnode($);n;n = nxtnode(n))
    clone (og, n);
  for (n=fstnode($);n;n = nxtnode(n))
    for (e=fstedge(n);e;e = nxtedge(e,n))
      clone (og, e);
  write(og);
  
}
gvpr/anon000064400000000646151705127100006405 0ustar00/* anonymize the graph */
BEG_G {
  node_t map[node_t];
  graph_t dup;
  int id = 0;
  char* gtype;
  node_t n;
  edge_t e;
  char* l;

  if ($.directed) gtype = "D";
  else gtype = "U";
  if ($.strict) gtype = gtype + "S";
  dup = graph ($.name, gtype);
  $tvtype = TV_ne;
}

N {
  n = node(dup, (char*)id);
  n.label=$.name;
  map[$] = n; 
  id++;
}

E {
  edge (map[$.tail],map[$.head],"");
}

END_G {
  write (dup);
}
gvpr/attr000064400000000227151705127100006417 0ustar00/* List known node attributes */
BEG_G {
  char* attr;
  for (attr = fstAttr($,"N"); attr != ""; attr = nxtAttr($,"N",attr)) {
    print (attr);
  }
}
gvpr/bb000064400000001745151705127100006036 0ustar00/* computes the bounding box of a graph based on its nodes 
   taking into account clusters and node sizes.
 */
BEGIN {
  double x, y, w2, h2;
  double llx, lly, urx, ury;
  double llx0, lly0, urx0, ury0;

  graph_t clustBB (graph_t G) {
    graph_t sg;
    for (sg = fstsubg(G); sg; sg = nxtsubg(sg)) {
	  sg = clustBB(sg);
    }
	if (G.name == "cluster*") {
      sscanf (G.bb, "%lf,%lf,%lf,%lf", &llx0, &lly0, &urx0, &ury0);
      if (llx0 < llx) llx = llx0;
      if (lly0 < lly) lly = lly0;
      if (urx0 > urx) urx = urx0;
      if (ury0 > ury) ury = ury0;
    }
    return G;
  }
}
BEG_G {
  llx = 1000000; lly = 1000000; urx = -1000000; ury = -1000000;
}
N {
  sscanf ($.pos, "%lf,%lf", &x, &y);
  w2 = (36.0*(double)$.width);
  h2 = (36.0*(double)$.height);
  if ((x - w2) < llx) llx = x - w2;
  if ((x + w2) > urx) urx = x + w2;
  if ((y - h2) < lly) lly = y - h2;
  if ((y + h2) > ury) ury = y + h2;
}
END_G {
  clustBB ($);
  $.bb = sprintf ("%lf,%lf,%lf,%lf", llx, lly, urx, ury);
}
gvpr/bbox000064400000001024151705127100006373 0ustar00/* computes the bounding box of a graph based on node positions */
BEGIN {
  double xmin, ymin, xmax, ymax;
  xmin = ymin = 1.7976931348623157e+308;
  xmax = ymax = -ymin;
  double x, y;
}
N {
  if (sscanf ($.pos, "%f,%f", &x, &y) == 2) {
    if (x < xmin) xmin = x;
    if (y < ymin) ymin = y;
    if (x > xmax) xmax = x;
    if (y > ymax) ymax = y;
  }
}

END {
  printf ("(%f,%f) (%f,%f)\n", xmin,  ymin, xmax, ymax);
  if (ARGC) printf ("area = %f aspect = %f\n", ((xmax-xmin)*(ymax-ymin))/1000000., (xmax-xmin)/(ymax-ymin));
}
gvpr/binduce000064400000002216151705127100007056 0ustar00/* Given a bipartite graph, induce a non-bipartite graph.
 * argv[0]="name=value" This is used to identify the nodes used
 * to induce edges. If aget(n,name) == value, 
 *  if deg(n) == 1, delete
 *  if deg(n) == 2, delete and connect to neighbor with edge
 *  if deg(n) > 2, delete and add edge between all pairs of neighbors
 *  Add weights to edge.
 */
BEGIN{
  int i, cnt;
  int wt[edge_t];
  string values[int];
  node_t nbrs[int];
  edge_t e;
  tokens(ARGV[0],values,"=");
  string aname = values[0];
  string value = values[1];
  printf(2, "%s=%s\n", aname, value);
}
N[aget($,aname)==value] {
  if ($.degree > 1) {
    cnt = 0;
    for (e = fstedge($); e; e = nxtedge(e, $))
      nbrs[cnt++] = opp(e,$);
    for (i = 0; i < cnt-1; i++) {
      if ((e = isEdge(nbrs[i],nbrs[i+1],"")) != NULL) {
        wt[e] += 1;
      }
      else if ($G.directed && (e = isEdge(nbrs[i+1],nbrs[i],""))) {
        wt[e] += 1;
      }
      else if (nbrs[i] != nbrs[i+1]) { // avoid loops
        e = edge(nbrs[i],nbrs[i+1],"");
        wt[e] = 1;
      }
    }
    unset(nbrs);
  }
  delete($G,$);
}
END_G{
  for (wt[e]) {
    e.multiplicity = sprintf ("%d", wt[e]);
  }
}
gvpr/bipart000064400000000707151705127100006731 0ustar00/* Determine if a graph is bipartite or not.
 */
BEG_G{
  int vc, c, color[node_t];
  node_t v;
  edge_t e;
  $tvtype = TV_dfs;
  $tvroot = fstnode($);
}
N{
  if ($tvedge == NULL)
    color[$] = 1;
  if (color[$] == 1)
    c = 2;
  else
    c = 1;
  for (e = fstedge($); e; e = nxtedge(e,$)) {
    v = opp(e,$);
    vc = color[v];
    if (vc == 0)
      color[v] = c;
    else if (vc != c) {
      printf(2, "Not bipartite\n");
      exit(1);
    }
  }
}
gvpr/chkclusters000064400000001024151705127100007773 0ustar00/* Report if graph has non-hierarchical clusters */
BEGIN {
  graph_t c[node_t];
  node_t n;

  void proc (graph_t h, graph_t p, graph_t g)
  {
    if (h.name == "cluster*") {
      for (n = fstnode(h); n; n = nxtnode_sg(h,n)) {
		g = c[n];
		if (g) {
		  if (g != p) {
            printf(2,"node %s in %s and %s\n", n.name, h.name, g.name);
		    exit(1);
          }
        }
        c[n] = h;
      }
      p = h;
    }
    for (g = fstsubg(h); g; g = nxtsubg(g)) {
      proc(g,p,NULL);
    }
  }
}
BEG_G {
  proc($,$,NULL);
}
gvpr/chkedges000064400000001624151705127100007224 0ustar00/* Looks for multiedges and loops, and output
 * those found along with counts. If the -d flag
 * is given, edge direction is taken into account.
 */
BEGIN{
  char* ename; 
  char* n; 
  int doDir, cnt[]; 
  int loopcnt[];
  int nloops, nmulti;
  if ((ARGC > 0) && (ARGV[0] == "-d"))
    doDir = 1;
  else
    doDir = 0;
}
BEG_G{unset(cnt); unset(loopcnt); nloops = nmulti = 0;}
E{
  if (doDir || (tail.name <= head.name)) ename=tail.name+"_"+head.name;
  else ename = head.name+"_"+tail.name;
  if (tail == head) {
    loopcnt[ename] += 1;
    if (loopcnt[ename] == 1) nloops += 1;
  }
  else {
    cnt[ename] += 1;
    if (cnt[ename] == 2) nmulti += 1;
  }
}
END_G{
  printf ("graph %s: %d loops %d multiedges\n", $.name, nloops, nmulti);
  for (cnt[n]) {
    if (cnt[n] > 1)
      printf ("%s : %d\n", n, cnt[n]);
  }
  for (loopcnt[n]) {
    if (loopcnt[n] > 0)
      printf ("%s : %d\n", n, loopcnt[n]);
  }
}

gvpr/cliptree000064400000000262151705127100007253 0ustar00/* Construct subgraph reachable from node ARGV[0] by forward edges */
BEG_G {
  node_t r = node($,ARGV[0]);

  $tvroot = r;
  $tvtype = TV_fwd;
}
N{$tvroot=NULL; subnode($T,$);}
gvpr/col000064400000001131151705127100006215 0ustar00# test
/* color nodes using output of dijkstra */
BEG_G {
    double h, hn, hf, d, sat, md = maxdist; 
    if (hue_near =="") hue_near = 0.8;
    if (hue_far =="") hue_far = 1.2;
    hn = hue_near;
    hf = hue_far;
}
# test
N {
    d = dist;
    sat = (md - d + 1.0) /( md + 1.0);
    h = hn + ((hf - hn) * d)/md;
    while (h < 0.0) h = h + 1.0;
    while (h > 1.0) h = h - 1.0;
    color = sprintf("%lf %lf %lf",hue,sat,1.0);
      /* make sure the shape is filled */
    if (!match(style,"filled")>=0) {
      if (style != "") style=sprintf("%s,filled",style);
      else style="filled";
    }
}

gvpr/collapse000064400000000725151705127100007252 0ustar00/* Collapse all edges with same group attribute into a single edge */
BEG_G {
  int seen[string];
  $O = $;            // Use the input graph as output.
}
E {
  if (collapse == "") return;          // If no collapse, ignore.
  if (seen[collapse]) delete ($G, $);  // If already seen an edge with this collapse value, 
                                       // delete the edge.
  else seen[collapse] = 1;             // Else mark collapse value as seen and keep edge.
}
gvpr/color000064400000002640151705127100006564 0ustar00/* color edges based on normalized length alpha.
 * If we have lim[i-1] <= alpha <= lim[i], the color
 * is linearly interpolated between color[i-1] and color[i].
 * Edges already having a color attribute are left unchanged.
 */
BEGIN {
  double lens [edge_t];
  double maxlen, len, minlen=1.7976931348623157e+308;
  double alpha, x0, y0, x1, y1;
  double h0, s0, v0;
  int i;
 
  int ncolors;     /* number of colors: ncolors >= 2 */
  /* start of color i.
   * lim[0]=0 < lim[1] < ... < lim[ncolors-1]=1
   */
  double lim[int]; 
  /* HSV values for color i */
  double h[int];
  double s[int];
  double v[int];

  /* simple default */
  ncolors = 2;
  lim[0] = 0;
  lim[1] = 1;
  h[0] = 0;
  h[1] = 0.67;
  s[0] = 1;
  s[1] = 1;
  v[0] = 1;
  v[1] = 1;
}
E{
    sscanf ($.tail.pos, "%f,%f", &x0, &y0);
    sscanf ($.head.pos, "%f,%f", &x1, &y1);

    len = sqrt ((x0-x1)*(x0-x1) + (y0-y1)*(y0-y1));

    lens[$] = len;
    if (len > maxlen) maxlen = len;
    if (len < minlen) minlen = len;
}
BEG_G {
  if (!isAttr($,"E","color"))
    setDflt($,"E","color","");
}
E{
  if ($.color != "") return; 
  alpha = (lens[$]-minlen)/(maxlen-minlen);
  for (i = 1; i < ncolors; i++) {
    if (alpha < lim[i]) break;
  }
  alpha = (alpha - lim[i-1])/(lim[i] - lim[i-1]);
  h0 = (1-alpha)*h[i-1] + alpha*h[i];  
  s0 = (1-alpha)*s[i-1] + alpha*s[i];  
  v0 = (1-alpha)*v[i-1] + alpha*v[i];  
  $.color = sprintf ("%.02f %.02f %.02f", h0, s0, v0);
}
gvpr/cycle000064400000001060151705127100006540 0ustar00/* Detect directed cycle and print one if found */
BEG_G{
  node_t tp, hp;
  node_t stk[node_t];
  $tvtype = TV_prepostfwd;
  $tvroot = fstnode($);
}

N {
  if (stk[$]) {
      stk[$] = NULL;
  }
  else if ($tvedge == NULL) {  /* current root */
    stk[$] = $;
  }
  else {
    stk[$] = $tvedge.tail;
  }
}
E {
  if (stk[$.head]) {
     tp = $.tail;
     hp = $.head;
     while (tp != $.head) {
        printf ("%s -> %s\n", tp.name, hp.name);
        hp = tp;
        tp = stk[tp];
      }
     printf ("%s -> %s\n", tp.name, hp.name);
     exit(0);
  }
}

gvpr/dechain000064400000000410151705127100007032 0ustar00/* Remove peninsulas - chains hanging off the main graph */
BEGIN {
  edge_t  e;
  node_t  v, n;
}
N [degree == 1] {
  n = $;
  while (n.degree == 1) {
    e = fstedge (n);
    if (e.head == n) v = e.tail;
    else v = e.head;
    delete($G,n);
    n = v;
  }  
}
gvpr/deghist000064400000000537151705127100007100 0ustar00/* print histogram of node degrees */
BEGIN {
  int degrees[];
  int maxd = 0;
  int i, d;
  char* maxn;
}
N{ 
  degrees[degree]++;
  if (degree > maxd) {
    maxn = $.name;
    maxd = degree;
  }
}
END {
  printf ("max node %s (%d)\n", maxn, maxd);
  for (i = 1; i <= maxd; i++) {
    d = degrees[i];
    if (d > 0) printf ("[%d] %d\n", i, d);
  }
}
gvpr/deledges000064400000000164151705127100007221 0ustar00/* delete all edges */
BEGIN {
  int map[edge_t];
  edge_t e;
}
E {map[$]=1}
END_G {
  for (map[e]) delete ($,e);
}
gvpr/delmulti000064400000000615151705127100007265 0ustar00/* create a copy of the input graph with no multiedges */
BEG_G { 
  graph_t g = graph ("merge", "S");
} 
E {
  int wt;
  node_t h = node(g,$.head.name);
  node_t t = node(g,$.tail.name);
  edge_t e = isEdge(t,h,"");
  wt = $.weight;
  if (wt <= 0) wt = 1;
  if (e) {
    e.weight = e.weight + wt;
  }
  else if (h != t) {
    e = edge(t,h,"");
    e.weight = wt;
  }
}
END_G { 
  fwriteG(g,1);
}
gvpr/delnodes000064400000000371151705127100007242 0ustar00/* Delete nodes whose names are given in ARGV */
BEG_G {
  int names[char*];
  int nodes[node_t];
  node_t n;
  int i;

  for (i = 0; i < ARGC; i++)
    names[ARGV[i]] = 1;
}
N[names[name]]{nodes[$] = 1}
END_G {
  for (nodes[n])
    delete ($,n);
}
gvpr/depath000064400000001043151705127100006707 0ustar00/* Replace paths a -> b -> ... -> c with a -> c */
BEGIN {
  edge_t  e;
  node_t  n, prv, nxt;
}

N [(indegree == 1) && (outdegree == 1)] {
  e = fstin ($);
  prv = e.tail;
  e = fstout ($);
  nxt = e.head;
  delete ($G,$);

  while ((prv.indegree == 1) && (prv.outdegree == 0)) {
    e = fstin (prv);
    n = e.tail;
    delete ($G,prv);
    prv = n;
  }

  while ((nxt.indegree == 0) && (nxt.outdegree == 1)) {
    e = fstout (nxt);
    n = e.head;
    delete ($G,nxt);
    nxt = n;
  }
 
  if (!isEdge (prv,nxt,""))
    edge (prv,nxt,"");
  
}
gvpr/dijkstra000064400000001207151705127100007257 0ustar00/* Given graph processed by dijkstra, and node,
 * color shortest path
 * Assumes path has been computed by dijkstra
 */
BEG_G {
  node_t n = isNode($,ARGV[0]);
  node_t nxt;
  edge_t e;
  double d, totd = 0;

  if (n == NULL) {
    printf(2, "no node named \"%s\"\n", ARGV[0]);
    exit(1);
  }
  while (n.prev != "") {
	nxt = isNode($,n.prev);
    /* printf(2, "nxt \"%s\"\n", nxt.name); */
	e = isEdge (n, nxt, "");
    if (e == NULL) {
      printf(2, "no edge between %s and %s\n", n.name, nxt.name);
    }
    e.color = "blue";
    /* printf(2, "len %s\n", e.len); */
    /* sscanf (e.len, "%f", &d); */
	/* totd += d; */
    n = nxt;
  }
}
gvpr/flatten000064400000000130151705127100007073 0ustar00/* Wipe out any cluster structure */
BEG_G { $O = copy (NULL,$); }
E { clone ($O, $); }
gvpr/get-layers-list000064400000000425151705127100010472 0ustar00BEG_G {
  char* larr[int];
  int i;
  if (!isAttr($,"G","layers")) return;
  if (isAttr($,"G","layersep"))
    tokens($.layers,larr,$.layersep);
  else
    tokens($.layers,larr," :\t");
  for (larr[i]) {
    if (i>0) printf(" %s",larr[i]);
    else printf("%s",larr[i]);
  }
}
gvpr/group000064400000001427151705127100006604 0ustar00/* Collapse all nodes with group = X into a single node */
BEG_G {
  node_t metaN; 
  graph_t g = graph ("metagraph", "S");
  $tvtype = TV_ne;
  $O = g;
}

  /* create only one node with given name/value */
N[group == "X"] {
  if (!metaN) {
    metaN = node (g, $.name);
  }
}

  /* duplicate all others */
N[group != "X"] { node (g, $.name); }

  /* Create an edge only if at least one of the nodes
   * is not a collapsed node */
E {
  node_t t;
  node_t h;
  if ($.tail.group == "X") { 
    if ($.head.group == "X") return;
    t = metaN;
    h = node (g, $.head.name);
  }
  else if ($.head.group == "X") {
    t = node (g, $.tail.name);
    h = metaN;
  }
  else {
    t = node (g, $.tail.name);
    h = node (g, $.head.name);
  }
  edge (t, h, "");
}

  /* set g to be output graph */
gvpr/histogram000064400000000477151705127100007451 0ustar00/* print histogram of integer attribute */
BEGIN {
  int count[];
  int maxd = 0;
  int i, d, v;
  char* attrname = ARGV[0];
}
N{ 
  v = (int)(aget($,attrname));
  count[v]++;
  if (v > maxd) {
    maxd = v;
  }
}
END {
  for (i = 1; i <= maxd; i++) {
    d = count[i];
    if (d > 0) printf ("[%d] %d\n", i, d);
  }
}
gvpr/indent000064400000000572151705127100006731 0ustar00/* Print the depth-first traversal of nodes
 * as an indented list
 */
BEGIN {
  int i, indent;
  int seen[string];
  void prInd () {
    for (i = 0; i < indent; i++) printf ("  ");
  } 
}
BEG_G {

   $tvtype = TV_prepostfwd;
   $tvroot = node($,ARGV[0]);
}
N {
  if (seen[$.name]) indent--;
  else {
    prInd();
      print ($.name);
    seen[$.name] = 1;
    indent++;
  }
}
gvpr/knbhd000064400000002312151705127100006530 0ustar00/* knbhd - Return the k-neighborhood of a node, i.e., allnodes
 * whose path length from the given node is <= k.
 * ARGV[] = k node_name
 */
BEG_G {
  node_t  ctr;
  int     maxlen;

  graph_t comp = subg($, "kcomp");
  int     sid = 0, eid = 0;
  int     curlen;
  node_t  curnode;
  int     nlen[node_t];
  node_t  stk[int];
  node_t  other;
  edge_t  e;

  if (ARGC != 2) {
    printf (2, "Two arguments required\n");
    exit(1);
  }

  if (!sscanf(ARGV[0],"%d",&maxlen)) {
    printf (2, "Improper length parameter \"%s\"\n", ARGV[0]);
    exit(1);
  }
  maxlen++; /* length of 0 means unset */

  ctr = isNode ($, ARGV[1]);
  if (!ctr) {
    printf (2, "node %s not found\n", ARGV[1]);
    exit(1);
  }

  subnode (comp,ctr);
  nlen[ctr] = 1;
  curnode = ctr;
  while (curnode) {
    curlen = nlen[curnode];
    if (curlen == maxlen) break;
    
    for (e = fstedge(curnode); e; e = nxtedge(e,curnode)) {
      other = e.head;
      if (other == curnode) other = e.tail;
      if (nlen[other]) continue; /* already seen */
      subnode(comp,other);
      nlen[other] = curlen+1;
      stk[eid++] = other;
    }

    if (sid < eid) curnode = stk[sid++];
    else curnode = NULL;
  }
  
  induce(comp);
  write(comp);
}
gvpr/maxdeg000064400000000463151705127100006714 0ustar00/* find nodes of max and min degree */
BEG_G {node_t mxn, mnn; int maxd = -1; int mind = 1000000;}
N { 
if (degree > maxd) 
{maxd = degree; mxn = $;} 
if (degree < mind) 
{mind = degree; mnn = $;} 
}
END_G {printf ("max degree = %d, node %s, min degree = %d, node %s\n", 
  maxd, mxn.name, mind, mnn.name)}
gvpr/path000064400000000701151705127100006376 0ustar00/* Report the distance from src = ARGV[0] to dst = ARGV[1]
 */
BEG_G {
  int dist[node_t];
  node_t n, curn;
  node_t src = node($G, ARGV[0]);
  node_t dst = node($G, ARGV[1]);
  $tvroot = src;
  $tvtype = TV_bfs;
}

N {
  curn = $;
  if ($ == dst) {
    printf ("dist from %s to %s is %d\n", src.name, dst.name, dist[dst]);
    exit(0);
  }
}

E {
  if ($.head == curn) n = $.tail; 
  else n = $.head;
  if (dist[n] == 0) dist[n] = dist[curn]+1;
}
gvpr/rotate000064400000002252151705127100006743 0ustar00/* Given node name and angle, rotate a layout using the given
 * node as origin.
 */

BEGIN {
  double x,y;
  double x0,y0;
  double x1,y1;
  double angle, cosa, sina;
  int cnt, sz;

  void rotate (double a, double b) {
    a -= x0;
    b -= y0;
    x1 = a*cosa - b*sina;
    y1 = a*sina + b*cosa;
  }
  char* rotateE (char* p) {
    char* newpos = "";
    cnt = sscanf (p, "e,%lf,%lf%n", &x, &y, &sz);
    if (cnt == 2) {
      rotate (x,y);
      newpos = newpos + sprintf ("e%lf,%lf ", x1, y1);
      p = substr(p, sz);
    }
    cnt = sscanf (p, "s,%lf,%lf%n", &x, &y, &sz);
    if (cnt == 2) {
      rotate (x,y);
      newpos = newpos + sprintf ("s%lf,%lf ", x1, y1);
      p = substr(p, sz);
    }

    while (sscanf (p, "%lf,%lf%n", &x, &y, &sz) == 2) {
      rotate (x,y);
      newpos = newpos + sprintf ("%lf,%lf ", x1, y1);
      p = substr(p, sz);
    }

    return newpos;
  }
}

BEG_G {
  node_t ctr = node ($, ARGV[0]);

  sscanf (ARGV[1], "%f", &angle);
  cosa = cos(angle);
  sina = sin(angle);

  sscanf (ctr.pos, "%f,%f", &x0, &y0);
  $.bb ="";
}
N {
  sscanf ($.pos, "%f,%f", &x, &y);
  rotate (x,y);
  $.pos = sprintf ("%f,%f", x1, y1);
}
E {
  $.pos = rotateE($.pos);
}
gvpr/scale000064400000002173151705127100006536 0ustar00/* finds node n with root attribute 
 * finds distance minr of closest node
 * the layout is then scaled out from n so that 
 * a node is put on the smallest circle of radius x*minr
 * containing n
 */
BEG_G {
  node_t ctr;
  int cx, cy;
  int x, y;
  double delx, dely;
  int newx, newy;
  node_t n;
  edge_t e;
  int i, sc, d, mind = -1;
  double fact, newr, ang, minr;
  
  ctr = node($,aget($,"root"));
  sscanf (ctr.pos, "%d,%d", &cx, &cy);
  for (e = fstedge(ctr); e; e = nxtedge(e, ctr)) {
    if (e.head == ctr) n = e.tail;
    else n = e.head;
    sscanf (n.pos, "%d,%d", &x, &y);
    d = (x-cx)*(x-cx) + (y-cy)*(y-cy);
    if ((mind == -1) || (d < mind)) mind = d;
  } 
  minr = (int)sqrt((double)mind);
}

N [$ != ctr] {
  
    sscanf ($.pos, "%d,%d", &x, &y);
    dely = y - cy;
    delx = x - cx;
    d = delx*delx + dely*dely;
    sc = (int)sqrt((double)(d/mind));
    if (sc > 1) {
      fact = 2.0;
      for (i=1; i<sc-1;i++) fact *= 2.0;
      newr = minr*(2.0 - (1.0/fact));
      ang = atan2 (dely, delx);
      newx = newr*cos(ang) + cx; 
      newy = newr*sin(ang) + cy; 
      $.pos = sprintf ("%d,%d", newx, newy);
    }
}
gvpr/scalexy000064400000002143151705127100007114 0ustar00/* finds root node of graph.
 * scales the x and y position of all other nodes using, first,
 * ARGV[0], or $G.scale.
 * 
 * The expected syntax is "x,y" where at least one of x or y must
 * be given. If only one is given, the other is taken as 1.
 */
BEGIN {
  double scalex, scaley;
  int r, done;

  int setScale (char* s) 
  {
    if ((sscanf (s, ",%f",&scaley))) {
        scalex = 1;
        return 1;
    }
    else {
      r = sscanf (s, "%f,%f",&scalex,&scaley);
      if (r) {
        if (r == 1) scaley = 1;
        return 1;
      }
    }
    return 0;
  }

}

BEG_G {
  node_t ctr = node($,aget($,"root"));
  double cx, cy, x, y, delx;
  
  /* get scale argument */
  done = 0;
  if (ARGC == 1)
    done = setScale (ARGV[0]);
  if (!done && isAttr($,"G","scale"))
    done = setScale ($.scale);
  if (!done)
    scalex = scaley = 1.0;

  if ((scalex == 1.0) && (scaley == 1.0))
    exit (0);

  $.bb = "";
  sscanf (ctr.pos, "%f,%f", &cx, &cy);
}
N [$ != ctr] {
    sscanf ($.pos, "%f,%f", &x, &y);
    delx = scalex*(x - cx) + cx;
    dely = scaley*(y - cy) + cy;
    $.pos = sprintf ("%f,%f", delx, dely);
}
gvpr/span000064400000000213151705127100006401 0ustar00/* derive simple spanning tree */
BEG_G { $tvtype = TV_dfs; $tvroot = node($,ARGV[0]);}
E [!isSubnode($T,$.head) || !isSubnode($T,$.tail)]
gvpr/topon000064400000000655151705127100006611 0ustar00/* Generate copy of topology of input graph
 * Replace names with numbers
 */
BEGIN {
int id = 0;
char* names[char*];

char* mapn (char* inname)
{
   char* s = names[inname];
   if (s == "") {
     s = id++;
     names[inname] = s;
   }
   return s;
}

}

BEG_G {
  graph_t g = graph ($.name, "U");
}
N { node (g, mapn($.name)); }
E { edge (node (g, mapn($.tail.name)), node (g, mapn($.head.name)), ""); }
END_G {
  write (g);
}
gvpr/treetoclust000064400000001734151705127100010026 0ustar00/* Convert a rooted tree to a hierarchy of clusters for patchwork.
 * ARGV[0] is desired root
 */
BEG_G {
  node_t rt;
  node_t n;
  graph_t cg;
  graph_t sg;
  int depth;
  int mark[node_t];
  graph_t stk[int];

  if (! $.directed) {
    printf(2,"Input graph is not directed\n");
    exit (1);
  }
  rt = isNode($,ARGV[0]);
  if (rt == NULL) {
    printf(2,"Root node \"%s\" not found\n", ARGV[0]);
    exit (1);
  }
  $tvroot = rt;
  $tvtype = TV_prepostfwd;
  cg = graph(rt.name,"U");
}

N {
  if (mark[$]) {
    depth--;
  }
  else {
    mark[$] = 1;
    if (depth > 0) {
      if (fstout($)) {
        sg = subg(stk[depth-1], "cluster_" + $.name); 
        if (($.style == "filled") && ($.fillcolor != ""))
          sg.bgcolor = $.fillcolor;
      }
      else {
        sg = NULL;
        n = node(stk[depth-1], $.name);
        n.style = "filled";
        n.fillcolor = $.fillcolor;
      }
    }
    else sg = cg;
    stk[depth] = sg;
    depth++;
  }
}
END_G {
  write(cg);
}

lefty/box.lefty000064400000007035151705127100007530 0ustar00load ('def.lefty');
definit ();
#
# initialize window data
#
canvas = defcanvas;
wrect = [0 = ['x' = 0; 'y' = 0;]; 1 = ['x' = 400; 'y' = 500;];];
setwidgetattr (canvas, ['window' = wrect;]);
#
# data structures
#
objarray = [];
objnum = 0;
#
# misc functions
#
min = function (a, b) {
    if (a <= b)
        return a;
    return b;
};
max = function (a, b) {
    if (b <= a)
        return a;
    return b;
};
rectof = function (p1, p2) {
    return [
        0 = ['x' = min (p1.x, p2.x); 'y' = min (p1.y, p2.y);];
        1 = ['x' = max (p1.x, p2.x); 'y' = max (p1.y, p2.y);];
    ];
};
pointadd = function (p1, p2) {
    return ['x' = p2.x + p1.x; 'y' = p2.y + p1.y;];
};
pointsub = function (p1, p2) {
    return ['x' = p2.x - p1.x; 'y' = p2.y - p1.y;];
};
#
# rendering functions
#
drawbox = function (obj, color) {
    box (canvas, obj, obj.rect, ['color' = color;]);
};
redrawboxes = function () {
    local i;
    clear (canvas);
    for (i = 0; i < objnum; i = i + 1)
        drawbox (objarray[i], 1);
};
redraw = function (canvas) {
    redrawboxes ();
};
#
# editing functions
#
new = function (rect) {
    objarray[objnum] = [
        'rect' = rect;
        'id' = objnum;
    ];
    objnum = objnum + 1;
    return objarray[objnum - 1];
};
reshape = function (obj, rect) {
    obj.rect = rect;
    return obj;
};
move = function (obj, p) {
    obj.rect[0] = pointadd (obj.rect[0], p);
    obj.rect[1] = pointadd (obj.rect[1], p);
    return obj;
};
delete = function (obj) {
    if (obj.id ~= objnum - 1) {
        objarray[obj.id] = objarray[objnum - 1];
        objarray[obj.id].id = obj.id;
    }
    remove (objnum - 1, objarray);
    objnum = objnum - 1;
};
#
# user interface functions
#
# left mouse button creates new box
# middle button moves a box
# right button deletes a box
#
leftdown = function (data) {
    if (data.obj ~= null)
        return;
    leftbox = new (rectof (data.pos, data.pos));
    drawbox (leftbox, 1);
    setgfxattr (canvas, ['mode' = 'xor';]);
};
leftmove = function (data) {
    if (~leftbox)
        return;
    drawbox (leftbox, 1);
    clearpick (canvas, leftbox);
    reshape (leftbox, rectof (data.ppos, data.pos));
    drawbox (leftbox, 1);
};
leftup = function (data) {
    if (~leftbox)
        return;
    drawbox (leftbox, 1);
    clearpick (canvas, leftbox);
    reshape (leftbox, rectof (data.ppos, data.pos));
    setgfxattr (canvas, ['mode' = 'src';]);
    drawbox (leftbox, 1);
    remove ('leftbox');
};
middledown = function (data) {
    if (data.obj == null)
        return;
    middlebox = data.obj;
    middlepos = data.pos;
    setgfxattr (canvas, ['mode' = 'xor';]);
};
middlemove = function (data) {
    if (~middlebox)
        return;
    drawbox (middlebox, 1);
    clearpick (canvas, middlebox);
    move (middlebox, pointsub (middlepos, data.pos));
    middlepos = data.pos;
    drawbox (middlebox, 1);
};
middleup = function (data) {
    if (~middlebox)
        return;
    drawbox (middlebox, 1);
    clearpick (canvas, middlebox);
    move (middlebox, pointsub (middlepos, data.pos));
    setgfxattr (canvas, ['mode' = 'src';]);
    drawbox (middlebox, 1);
    remove ('middlepos');
    remove ('middlebox');
};
rightup = function (data) {
    if (data.pobj == null)
        return;
    drawbox (data.obj, 0);
    clearpick (canvas, data.obj);
    delete (data.obj);
};
dops = function () {
    local s;

    s = ['x' = 8 * 300; 'y' = 10.5 * 300;];
    canvas = createwidget (-1, ['type' = 'ps'; 'size' = s;]);
    setwidgetattr (canvas, ['window' = wrect;]);
    redraw (canvas);
    destroywidget (canvas);
    canvas=defcanvas;
};
lefty/def.lefty000064400000000713151705127100007472 0ustar00definit = function () {
    defview = createwidget (-1, [
        'type' = 'view';
        'name' = 'graphics view';
        'origin' = ['x' = 1; 'y' = 1;];
        'size' = ['x' = 400; 'y' = 500;];
    ]);
    defscroll = createwidget (defview, ['type' = 'scroll';]);
    defcanvas = createwidget (defscroll, [
        'type' = 'canvas';
        'origin' = ['x' = 1; 'y' = 1;];
        'size' = ['x' = 400; 'y' = 500;];
        'borderwidth' = 1;
    ]);
};
lefty/dotty.lefty000064400000054774151705127100010117 0ustar00#
# DOTTY
#
dotty = [
    'keys' = [
        'nid'    = 'nid';
        'eid'    = 'eid';
        'gid'    = 'gid';
        'name'   = 'name';
        'attr'   = 'attr';
        'gattr'  = 'graphattr';
        'eattr'  = 'edgeattr';
        'nattr'  = 'nodeattr';
        'edges'  = 'edges';
        'tail'   = 'tail';
        'tport'  = 'tport';
        'head'   = 'head';
        'hport'  = 'hport';
        'pos'    = 'pos';
        'size'   = 'size';
        'rect'   = 'rect';
        'fname'  = 'fontname';
        'fsize'  = 'fontsize';
        'fcolor' = 'fontcolor';
        'dcolor' = 'drawcolor';
        'bcolor' = 'fillcolor';
    ];
    'maps' = [
        'X11' = [
            'fontmap' = [
                'Times-Roman'    = '-*-times-medium-r-*--%d-*-*-*-*-*-*-1';
                'Times-Italic'   = '-*-times-medium-i-*--%d-*-*-*-*-*-*-1';
                'Times-Bold'     = '-*-times-bold-r-*--%d-*-*-*-*-*-*-1';
                'Courier'        = '-*-courier-bold-r-*--%d-*-*-*-*-*-*-1';
                'Courier-Bold'   = '-*-courier-bold-r-*--%d-*-*-*-*-*-*-1';
                'Helvetica'      = (
                     '-*-helvetica-medium-r-normal--%d-*-*-*-p-*-iso8859-1'
                );
                'Helvetica-Bold' = (
                    '-*-helvetica-bold-r-normal--%d-*-*-*-p-*-iso8859-1'
                );
            ];
            'psfontmap' = [
                'Times-Roman'    = 'Times-Roman';
                'Times-Italic'   = 'Times-Italic';
                'Times-Bold'     = 'Times-Bold';
                'Courier'        = 'Courier';
                'Courier-Bold'   = 'Courier-Bold';
                'Helvetica'      = 'Helvetica';
                'Helvetica-Bold' = 'Helvetica-Bold';
            ];
        ];
        'mswin' = [
            'fontmap' = [
                'Times-Roman'    = 'Times New Roman';
                'Times-Italic'   = 'Times New Roman Italic';
                'Times-Bold'     = 'Times New Roman Bold';
                'Courier'        = 'Courier New';
                'Courier-Bold'   = 'Courier New Bold';
                'Helvetica'      = 'Arial';
                'Helvetica-Bold' = 'Arial Bold';
            ];
            'psfontmap' = [
                'Times-Roman'    = 'Times New Roman';
                'Times-Italic'   = 'Times New Roman Italic';
                'Times-Bold'     = 'Times New Roman Bold';
                'Courier'        = 'Courier New';
                'Courier-Bold'   = 'Courier New Bold';
                'Helvetica'      = 'Arial';
                'Helvetica-Bold' = 'Arial Bold';
            ];
        ];
    ];
    'protogt' = [
        'graph' = [
            'graphattr' = [
                'fontsize' = '14';
                'fontname' = 'Times-Roman';
                'fontcolor' = 'black';
            ];
            'nodeattr' = [
                'shape' = 'ellipse';
                'fontsize' = '14';
                'fontname' = 'Times-Roman';
                'fontcolor' = 'black';
                'style' = 'solid';
            ];
            'edgeattr' = [
                'fontsize' = '14';
                'fontname' = 'Times-Roman';
                'fontcolor' = 'black';
                'style' = 'solid';
            ];
            'graphdict' = [];
            'nodedict' = [];
            'graphs' = [];
            'nodes' = [];
            'edges' = [];
            'maxgid' = 0;
            'maxnid' = 0;
            'maxeid' = 0;
            'type' = 'digraph';
        ];
        'layoutmode' = 'sync';
        'lserver' = 'dot';
        'edgehandles' = 1;
        'noundo' = 0;
    ];
    'lservers' = [];
    'mlevel' = 0;
    'graphs' = [];
    'views' = [];
    'protovt' = [
        'normal' = [
            'name' = 'DOTTY';
            'orig' = ['x' = 1; 'y' = 1;];
            'size' = ['x' = 420; 'y' = 520;];
            'wrect' = [
                0 = ['x' = 0; 'y' = 0;];
                1 = ['x' = 400; 'y' = 500;];
            ];
            'vsize' = ['x' = 400; 'y' = 500;];
            'w2v' = 1;
        ];
        'birdseye' = [
            'type' = 'birdseye';
            'name' = 'DOTTY birdseye view';
            'orig' = ['x' = 1; 'y' = 1;];
            'size' = ['x' = 220; 'y' = 260;];
            'wrect' = [
                0 = ['x' = 0; 'y' = 0;];
                1 = ['x' = 200; 'y' = 250;];
            ];
            'vsize' = ['x' = 200; 'y' = 250;];
            'w2v' = 1;
        ];
    ];
    'pagesizes' = [
        '8.5x11' = ['x' =    8; 'y' = 10.5;];
        '11x17'  = ['x' = 10.5; 'y' = 16.5;];
        '36x50'  = ['x' = 35.5; 'y' = 49.5;];
    ];
];
load ('dotty_draw.lefty');
load ('dotty_edit.lefty');
load ('dotty_layout.lefty');
load ('dotty_ui.lefty');
#
# initialization functions
#
dotty.init = function () {
    dotty.fontmap = dotty.maps[getenv ('LEFTYWINSYS')].fontmap;
    dotty.clipgt = dotty.protogt.creategraph (['noundo' = 1;]);
    dotty.inited = 1;
};
dotty.simple = function (file) {
    if (dotty.inited ~= 1)
        dotty.init ();
    dotty.createviewandgraph (file, 'file', null, null);
    txtview ('off');
};
#
# main operations
#
dotty.protogt.creategraph = function (protogt) {
    local gt, id, gtid;

    if (~protogt)
        protogt = dotty.protogt;
    for (gtid = 0; dotty.graphs[gtid]; gtid = gtid + 1)
        ;
    gt = (dotty.graphs[gtid] = []);
    if (protogt.mode ~= 'replace') {
        for (id in dotty.protogt)
            gt[id] = copy (dotty.protogt[id]);
    }
    for (id in protogt)
        gt[id] = copy (protogt[id]);
    gt.gtid = gtid;
    gt.views = [];
    gt.undoarray = ['level' = 0; 'entries' = [];];
    gt.busy = 0;
    return gt;
};
dotty.protogt.copygraph = function (ogt) {
    local gt, gtid, id;

    for (gtid = 0; dotty.graphs[gtid]; gtid = gtid + 1)
        ;
    gt = (dotty.graphs[gtid] = []);
    for (id in ogt)
        gt[id] = copy (ogt[id]);
    gt.gtid = gtid;
    gt.views = [];
    gt.undoarray = ['level' = 0; 'entries' = [];];
    gt.busy = 0;
    return gt;
};
dotty.protogt.destroygraph = function (gt) {
    local vid, vlist;

    if (gt.layoutpending > 0)
        gt.cancellayout (gt);
    for (vid in gt.views)
        vlist[vid] = gt.views[vid];
    for (vid in gt.views)
        gt.destroyview (gt, vlist[vid]);
    remove (gt.gtid, dotty.graphs);
};
dotty.protogt.loadgraph = function (gt, name, type, protograph, layoutflag) {
    local fd, vid, vt, graph, nid, eid, gid;

    if (gt.layoutpending > 0)
        gt.cancellayout (gt);
    if (~name)
        if (~(name = ask ('file name:', 'file', '')))
            return;
    dotty.pushbusy (gt, gt.views);
    dotty.message (1, 'loading');
    if (~protograph)
        protograph = dotty.protogt.graph;
    if (
        ~((fd = dotty.openio (name, type, 'r')) >= 0) |
        ~(graph = readgraph (fd, protograph))
    ) {
        dotty.message (0, 'cannot load graph');
        dotty.popbusy (gt, gt.views);
        return;
    }
    for (vid in gt.views) {
        vt = gt.views[vid];
        vt.colors = [];
        vt.colorn = 2;
    }
    gt.graph = graph;
    gt.name = name;
    gt.type = type;
    gt.undoarray = ['level' = 0; 'entries' = [];];
    if (~(type == 'file' & name == '-'))
        closeio (fd);
    graph.maxgid = tablesize (graph.graphs);
    graph.maxnid = tablesize (graph.nodes);
    graph.maxeid = tablesize (graph.edges);
    for (nid in graph.nodes)
        graph.nodes[nid][dotty.keys.nid] = nid;
    for (eid in graph.edges)
        graph.edges[eid][dotty.keys.eid] = eid;
    for (gid in graph.graphs)
        graph.graphs[gid][dotty.keys.gid] = gid;
    gt.unpackattr (gt);
    if (layoutflag) {
        dotty.message (1, 'generating layout');
        gt.layoutgraph (gt);
    }
    dotty.popbusy (gt, gt.views);
    return gt.graph;
};
dotty.protogt.savegraph = function (gt, name, type) {
    local fd;

    if (~name)
        if (~(name = ask ('file name:', 'file', '')))
            return;
    if (
        ~((fd = dotty.openio (name, type, 'w')) >= 0) |
        ~writegraph (fd, gt.graph, 0)
    ) {
        dotty.message (0, 'cannot save graph');
        return;
    }
    if (~(type == 'file' & name == '-'))
        closeio (fd);
};
dotty.protogt.setgraph = function (gt, graph) {
    local vid, vt, nid, eid, gid;

    if (gt.layoutpending > 0)
        gt.cancellayout (gt);
    for (vid in gt.views) {
        vt = gt.views[vid];
        vt.colors = [];
        vt.colorn = 2;
    }
    gt.graph = copy (graph);
    gt.undoarray = ['level' = 0; 'entries' = [];];
    gt.unpackattr (gt);
    gt.graph.maxgid = tablesize (graph.graphs);
    gt.graph.maxnid = tablesize (graph.nodes);
    gt.graph.maxeid = tablesize (graph.edges);
    for (nid in gt.graph.nodes)
        gt.graph.nodes[nid][dotty.keys.nid] = nid;
    for (eid in gt.graph.edges)
        gt.graph.edges[eid][dotty.keys.eid] = eid;
    for (gid in gt.graph.graphs)
        gt.graph.graphs[gid][dotty.keys.gid] = gid;
    gt.unpackattr (gt);
    dotty.message (1, 'generating layout');
    gt.layoutgraph (gt);
    return gt.graph;
};
dotty.protogt.erasegraph = function (gt, protogt, protovt) {
    local vid, vt;

    if (gt.layoutpending > 0)
        gt.cancellayout (gt);
    for (vid in gt.views) {
        vt = gt.views[vid];
        vt.colors = [];
        vt.colorn = 2;
        clear (vt.canvas);
    }
    if (~protogt)
        protogt = dotty.protogt;
    gt.graph = copy (protogt.graph);
    gt.undoarray = ['level' = 0; 'entries' = [];];
};
dotty.protogt.layoutgraph = function (gt) {
    if (gt.graph.graphattr.xdotversion) {
        gt.unpacklayout (gt, gt.graph);
        gt.setviewsize (gt.views, gt.graph.rect);
        gt.redrawgraph (gt, gt.views);
        return;
    }
    if (gt.layoutmode == 'async') {
        if (~gt.haveinput) {
            gt.startlayout (gt);
            return;
        }
        if (~gt.finishlayout (gt))
            return;
        gt.setviewsize (gt.views, gt.graph.rect);
        gt.redrawgraph (gt, gt.views);
    } else {
        if (~gt.startlayout (gt))
            return;
        else
            while (~gt.finishlayout (gt))
                ;
        gt.setviewsize (gt.views, gt.graph.rect);
        gt.redrawgraph (gt, gt.views);
    }
};
dotty.protogt.createview = function (gt, protovt) {
    local vt, ovt, id, t;

    vt = [];
    vt.colors = [];
    vt.colorn = 2;
    if (~protovt)
        protovt = dotty.protovt.normal;
    if (protovt.mode ~= 'replace') {
        for (id in dotty.protovt[protovt.type])
            vt[id] = copy (dotty.protovt[protovt.type][id]);
    }
    for (id in protovt)
        vt[id] = copy (protovt[id]);
    if (~(vt.parent >= 0)) {
        vt.view = createwidget (-1, [
            'type'   = 'view';
            'name'   = vt.name;
            'origin' = vt.orig;
            'size'   = vt.size;
        ]);
        vt.scroll = createwidget (vt.view, ['type' = 'scroll';]);
    } else {
        vt.view = -1;
        vt.scroll = createwidget (vt.parent, [
            'type' = 'scroll';
            'size' = vt.size;
        ]);
    }
    vt.canvas = createwidget (vt.scroll, [
        'type' = 'canvas';
        'color' = [0 = protovt.bgcolor; 1 = protovt.fgcolor;];
    ]);
    setwidgetattr (vt.canvas, [
        'window' = vt.wrect;
        'viewport' = vt.vsize;
    ]);
    clear (vt.canvas);
    dotty.views[vt.canvas] = vt;
    vt.vtid = vt.canvas;
    vt.gtid = gt.gtid;
    gt.views[vt.vtid] = vt;
    dotty.views[vt.scroll] = vt;
    if (vt.view ~= -1)
        dotty.views[vt.view] = vt;
    if (protovt.colors & tablesize (protovt.colors) > 0) {
        for (id in protovt.colors)
            if (id == '_bgcolor_')
                setwidgetattr (vt.canvas, [
                    'color' = [0 = protovt.colors[id];];
                ]);
            else if (setwidgetattr (vt.canvas, ['color' = [
                protovt.colors[id] = id;
            ];]) ~= 1) {
                t = split (id, ' ');
                if (tablesize (t) ~= 3 | setwidgetattr (vt.canvas, [
                    'color' = [protovt.colors[id] = [
                        'h' = ston (t[0]); 's' = ston (t[1]); 'v' = ston (t[2]);
                    ];];
                ]) ~= 1) {
                    dotty.message (
                        0, concat ('unknown color ', id, ' using #1')
                    );
                }
            }
        vt.colors = copy (protovt.colors);
        vt.colorn = protovt.colorn;
    } else if (tablesize (gt.views) > 1) {
        for (id in gt.views)
            if (gt.views[id] ~= vt)
                break;
        ovt = gt.views[id];
        for (id in ovt.colors)
            if (id == '_bgcolor_')
                setwidgetattr (vt.canvas, ['color' = [0 = ovt.colors[id];];]);
            else if (setwidgetattr (vt.canvas, ['color' = [
                ovt.colors[id] = id;
            ];]) ~= 1) {
                t = split (id, ' ');
                if (tablesize (t) ~= 3 | setwidgetattr (vt.canvas, [
                    'color' = [ovt.colors[id] = [
                        'h' = ston (t[0]); 's' = ston (t[1]); 'v' = ston (t[2]);
                    ];];
                ]) ~= 1) {
                    dotty.message (
                        0, concat ('unknown color ', id, ' using #1')
                    );
                }
            }
        vt.colors = copy (ovt.colors);
        vt.colorn = ovt.colorn;
    }
    if (gt.graph.rect)
        gt.setviewsize ([vt.vtid = vt;], gt.graph.rect);
    gt.drawgraph (gt, [vt.vtid = vt;]);
    for (id in vt.uifuncs)
        if (id == 'closeview')
            widgets[vt.view][id] = vt.uifuncs[id];
        else
            widgets[vt.canvas][id] = vt.uifuncs[id];
    return vt;
};
dotty.protogt.destroyview = function (gt, vt) {
    destroywidget (vt.canvas);
    destroywidget (vt.scroll);
    if (vt.view ~= -1) {
        destroywidget (vt.view);
        remove (vt.view, dotty.views);
    }
    remove (vt.scroll, dotty.views);
    remove (vt.canvas, dotty.views);
    if (vt.gtid >= 0)
        remove (vt.vtid, gt.views);
    if (tablesize (dotty.views) == 0)
        exit ();
};
dotty.protogt.zoom = function (gt, vt, factor, pos) {
    gt.setviewscale ([vt.vtid = vt;], factor);
    if (pos)
        gt.setviewcenter ([vt.vtid = vt;], pos);
    gt.redrawgraph (gt, [vt.vtid = vt;]);
};
dotty.protogt.findnode = function (gt, vt) {
    local key, node, node1, nid;

    if (~(key = ask ('give node name or label')))
        return;
    if (gt.graph.nodedict[key] >= 0)
        node = gt.graph.nodes[gt.graph.nodedict[key]];
    else if (gt.graph.nodedict[ston (key)] >= 0)
        node = gt.graph.nodes[gt.graph.nodedict[ston (key)]];
    else {
        for (nid in gt.graph.nodes) {
            node1 = gt.graph.nodes[nid];
            if (node1.attr.label == key | node1.attr.label == ston (key)) {
                node = node1;
                break;
            }
        }
    }
    if (~node) {
        dotty.message (0, concat ('cannot find node: ', key));
        return;
    }
    gt.setviewcenter ([vt.vtid = vt;], node.pos);
};
dotty.protogt.setattr = function (gt, obj) {
    local kv, t, attr, value, n, i, s;

    if (~(kv = ask ('give attr/value, eg. color=blue')))
        return;
    t = split (kv, '=');
    attr = t[0];
    value = t[1];
    if ((n = tablesize (t)) > 2)
        for (i = 2; i < n; i = i + 1)
            value = concat (value, '=', t[i]);
    # Check for HTML string and convert using lefty convention
    s = split (value, '');
    n = tablesize (s);
    if ((s[0] == '<') & (s[n-1] == '>')) {
        s[0] = '>';
        s[n-1] = '<';
        value = s[0]; 
        for (i = 1; i < n; i = i + 1)
            value = concat (value, s[i]);
    }
    if (
        obj.attr == gt.graph.graphattr |
        obj.attr == gt.graph.edgeattr |
        obj.attr == gt.graph.nodeattr
    ) {
        obj.attr[attr] = value;
        return;
    }
    if (obj.nid >= 0) {
        gt.undrawnode (gt, gt.views, obj);
        obj.attr[attr] = value;
        gt.unpacknodeattr (gt, obj);
        gt.drawnode (gt, gt.views, obj);
    } else if (obj.eid >= 0) {
        gt.undrawedge (gt, gt.views, obj);
        obj.attr[attr] = value;
        gt.unpackedgeattr (gt, obj);
        gt.drawedge (gt, gt.views, obj);
    }
};
dotty.protogt.getattr = function (gt, node) {
    local kv;

    if (~(kv.key = ask ('give attr name')))
        return null;
    if ((kv.val = node.attr[kv.key]))
        return kv;
    return null;
};
#
# utilities
#
dotty.createviewandgraph = function (name, type, protogt, protovt) {
    local vt, gt;

    if (~protogt)
        protogt = dotty.protogt;
    if (protogt.creategraph)
        gt = protogt.creategraph (protogt);
    else
        gt = dotty.protogt.creategraph (protogt);
    vt = gt.createview (gt, protovt);
    if (~protogt.graph)
        protogt.graph = copy (dotty.protogt.graph);
    if (name)
        gt.loadgraph (gt, name, type, protogt.graph, 1);
    return ['gt' = gt; 'vt' = vt;];
};
dotty.openio = function (name, type, mode) {
    local fd;

    if (~name)
        return null;
    if (type == 'file') {
        if (name == '-') {
            if (mode == 'r' | mode == 'r+')
                fd = 0;
            else
                fd = 1;
        } else if (~((fd = openio ('file', name, mode)) >= 0)) {
            dotty.message (0, concat ('cannot open file: ', name));
            return null;
        }
    } else if (type == 'pipe') {
        if (~((fd = openio (
            'pipe', 'ksh', mode, concat ("%e ", name)
        )) >= 0)) {
            dotty.message (0, concat ('cannot run command: ', name));
            return null;
        }
    } else
        return null;
    return fd;
};
dotty.pushbusy = function (gt, views) {
    local vid;

    if (gt.busy == 0)
        for (vid in gt.views)
            setwidgetattr (vid, ['cursor' = 'watch';]);
    gt.busy = gt.busy + 1;
};
dotty.popbusy = function (gt, views) {
    local vid;

    gt.busy = gt.busy - 1;
    if (gt.busy == 0)
        for (vid in gt.views)
            setwidgetattr (vid, ['cursor' = 'default';]);
};
dotty.message = function (level, text) {
    if (level <= dotty.mlevel)
        echo ('dotty.lefty: ', text);
};
#
# printing or saving to file
#
dotty.protogt.printorsave = function (gt, vt, otype, name, mode, ptype) {
    local pr, wrect, vsize, xy, psize, canvas, pscanvas, cid, cname, t;
    local graph, edgehandles, fontmap, eid, edge, nid, node, gid, sgraph;
    local did, draw, i;

    if (~otype)
        if (~(otype = ask ('print to', 'choice', 'file|printer')))
            return;
    if (otype == 'printer') {
        if (~getenv ('TMPDIR'))
            name = concat (getenv ('HOME'), '/.dottyout.ps');
        else
            name = concat (getenv ('TMPDIR'), '/.dottyout.ps', random (10000));
        if (getenv ('LEFTYWINSYS') ~= 'mswin' & ~pr)
            if (~(pr = ask ('printer command', 'string', 'lpr')))
                return;
    }
    if (~name)
        if (~(name = ask ('postscript file', 'file', 'out.ps')))
            return;
    if (~ptype)
        if (~(ptype = ask ('page size', 'choice', '8.5x11|11x17|36x50')))
            return;
    if (~mode)
        if (~(mode = ask ('mode', 'choice', 'portrait|landscape|best fit')))
            return;
    wrect = copy (vt.wrect);
    wrect[0].x = wrect[0].x - 1;
    wrect[1].x = wrect[1].x + 1;
    wrect[0].y = wrect[0].y - 1;
    wrect[1].y = wrect[1].y + 1;
    vsize = copy (vt.vsize);
    if (vsize.x == 0)
        vsize.x = 1;
    if (vsize.y == 0)
        vsize.y = 1;
    xy = vsize.x / vsize.y;
    if (mode == 'best fit') {
        if (xy < 1)
            mode = 'portrait';
        else
            mode = 'landscape';
    }
    psize = dotty.pagesizes[ptype];
    if (mode == 'portrait') {
        if (xy < psize.x / psize.y) {
            vsize.y = psize.y * 300;
            vsize.x = vsize.y * xy;
        } else {
            vsize.x = psize.x * 300;
            vsize.y = vsize.x / xy;
        }
    } else {
        if (xy < psize.y / psize.x) {
            vsize.y = psize.x * 300;
            vsize.x = vsize.y * xy;
        } else {
            vsize.x = psize.y * 300;
            vsize.y = vsize.x / xy;
        }
    }
    if (~((pscanvas = createwidget (-1, [
        'type'   = 'ps';
        'origin' = ['x' = 0; 'y' = 0;];
        'size'   = vsize;
        'mode'   = mode;
        'name'   = name;
    ])) >= 0)) {
        dotty.message (0, 'cannot open printer device');
        return;
    }
    for (cname in vt.colors) {
        cid = vt.colors[cname];
        if (cname == '_bgcolor_')
            setwidgetattr (pscanvas, ['color' = [0 = cid;];]);
        else if (setwidgetattr (pscanvas, ['color' = [cid = cname;];]) ~= 1) {
            t = split (cname, ' ');
            if (tablesize (t) ~= 3 | setwidgetattr (pscanvas, [
                'color' = [cid = [
                    'h' = ston (t[0]); 's' = ston (t[1]); 'v' = ston (t[2]);
                ];];
            ]) ~= 1) {
                dotty.message (
                    0, concat ('unknown color ', cname, ' using #1')
                );
            }
        }
    }
    setwidgetattr (pscanvas, ['window' = wrect;]);
    graph = copy (gt.graph);
    canvas = vt.canvas;
    vt.canvas = pscanvas;
    edgehandles = gt.edgehandles;
    gt.edgehandles = 0;
    fontmap = dotty.maps[getenv ('LEFTYWINSYS')].psfontmap;
    for (eid in graph.edges) {
        edge = graph.edges[eid];
        edge.fontname = fontmap[edge.attr.fontname];
        for (did in edge.draws) {
            if (did == 'ep')
                continue;
            draw = edge.draws[did];
            for (i = 0; draw[i]; i = i + 1)
                if (draw[i].type == 'F')
                    draw[i].fn = fontmap[draw[i].ofn];
        }
        gt.drawedge (gt, [0 = vt;], edge);
    }
    for (nid in graph.nodes) {
        node = graph.nodes[nid];
        node.fontname = fontmap[node.attr.fontname];
        for (did in node.draws) {
            if (did == 'ep')
                continue;
            draw = node.draws[did];
            for (i = 0; draw[i]; i = i + 1)
                if (draw[i].type == 'F')
                    draw[i].fn = fontmap[draw[i].ofn];
        }
        gt.drawnode (gt, [0 = vt;], node);
    }
    for (gid in graph.graphs) {
        sgraph = graph.graphs[gid];
        sgraph.fontname = fontmap[sgraph.graphattr.fontname];
        for (did in sgraph.draws) {
            if (did == 'ep')
                continue;
            draw = sgraph.draws[did];
            for (i = 0; draw[i]; i = i + 1)
                if (draw[i].type == 'F')
                    draw[i].fn = fontmap[draw[i].ofn];
        }
        gt.drawsgraph (gt, [0 = vt;], sgraph);
    }
    graph.fontname = fontmap[graph.graphattr.fontname];
    gt.drawsgraph (gt, [0 = vt;], graph);
    gt.edgehandles = edgehandles;
    vt.canvas = canvas;
    destroywidget (pscanvas);
    if (otype == 'printer' & getenv ('LEFTYWINSYS') ~= 'mswin')
        system (concat (pr, ' ', name, '; rm ',name));
};
lefty/dotty_draw.lefty000064400000046752151705127100011131 0ustar00#
# dotty_draw: drawing functions and data structures
#
dotty.protogt.drawgraph = function (gt, views) {
    local gid, eid, nid, graph;

    graph = gt.graph;
    gt.drawsgraph (gt, views, graph);
    for (gid in graph.graphs)
        gt.drawsgraph (gt, views, graph.graphs[gid]);
    for (eid in graph.edges)
        gt.drawedge (gt, views, graph.edges[eid]);
    for (nid in graph.nodes)
        gt.drawnode (gt, views, graph.nodes[nid]);
};
dotty.protogt.redrawgraph = function (gt, views) {
    local vid;

    for (vid in views)
        clear (views[vid].canvas);
    gt.drawgraph (gt, views);
};
dotty.protogt.setviewsize = function (views, r) {
    local vid, vt, w2v, scale, attr;

    for (vid in views) {
        vt = views[vid];
        vt.wrect = copy (r);
        if (r[1].x == 0 | r[1].y == 0) {
            attr = getwidgetattr (vt.scroll, [0 = 'size';]);
            vt.wrect[1] = copy (attr.size);
        }
        if (vt.type == 'birdseye') {
            attr = getwidgetattr (vt.scroll, [0 = 'size';]);
            scale.x = (vt.wrect[1].x - vt.wrect[0].x) / attr.size.x;
            scale.y = (vt.wrect[1].y - vt.wrect[0].y) / attr.size.y;
            if (scale.x > 1 & scale.x > scale.y)
                vt.w2v = scale.x;
            else if (scale.y > 1)
                vt.w2v = scale.y;
            else
                vt.w2v = 1;
        }
        w2v = vt.w2v;
        vt.vsize = [
            'x' = toint ((vt.wrect[1].x - vt.wrect[0].x) / w2v);
            'y' = toint ((vt.wrect[1].y - vt.wrect[0].y) / w2v);
        ];
        setwidgetattr (vt.canvas, [
            'window' = vt.wrect;
            'viewport' = vt.vsize;
        ]);
        attr = getwidgetattr (vt.canvas, [0 = 'viewport';]);
        vt.vsize = copy (attr.viewport);
    }
};
dotty.protogt.setviewscale = function (views, factor) {
    local vid, vt, w2v, attr;

    for (vid in views) {
        vt = views[vid];
        if ((w2v = vt.w2v * factor) < 0.01) {
            dotty.message (0, 'cannot zoom any closer');
            return;
        }
        vt.w2v = w2v;
        vt.vsize = [
            'x' = (vt.wrect[1].x - vt.wrect[0].x) / w2v;
            'y' = (vt.wrect[1].y - vt.wrect[0].y) / w2v;
        ];
        setwidgetattr (vt.canvas, ['viewport' = vt.vsize;]);
        attr = getwidgetattr (vt.canvas, [0 = 'viewport';]);
        vt.vsize = copy (attr.viewport);
    }
};
dotty.protogt.setviewcenter = function (views, center) {
    local vid, vt, pos;

    for (vid in views) {
        vt = views[vid];
        pos = [
            'x' = center.x * vt.vsize.x / (vt.wrect[1].x - vt.wrect[0].x);
            'y' = (
                (vt.wrect[1].y - center.y) * vt.vsize.y /
                (vt.wrect[1].y - vt.wrect[0].y)
            );
        ];
        setwidgetattr (vt.scroll, ['childcenter' = pos;]);
    }
};
#
# draw graph components
#
dotty.protogt.drawsgraph = function (gt, views, sgraph) {
    sgraph.draw = 1;
    if (~sgraph.draws)
        return;
    gt.execalldraw (gt, views, null, sgraph.draws, [
        'fontname' = sgraph.fontname;
        'fontsize' = sgraph.fontsize;
        'fontcolor' = sgraph.fontcolor;
        'drawcolor' = sgraph.drawcolor;
        'fillcolor' = sgraph.fillcolor;
    ]);
};
dotty.protogt.undrawsgraph = function (gt, views, sgraph) {
    sgraph.drawn = 0;
    if (~sgraph.draws)
        return;
    gt.execalldraw (gt, views, null, sgraph.draws, [
        'fontname' = sgraph.fontname;
        'fontsize' = sgraph.fontsize;
        'fontcolor' = sgraph.fontcolor;
        'drawcolor' = 0;
        'fillcolor' = 0;
    ]);
};
dotty.protogt.drawnode = function (gt, views, node) {
    local vid;

    node.drawn = 1;
    if (~node.draws)
        return;
    gt.execalldraw (gt, views, node, node.draws, [
        'fontname' = node.fontname;
        'fontsize' = node.fontsize;
        'fontcolor' = node.fontcolor;
        'drawcolor' = node.drawcolor;
        'fillcolor' = node.fillcolor;
    ]);
    for (vid in views)
        setpick (views[vid].canvas, node, node.rect);
};
dotty.protogt.undrawnode = function (gt, views, node) {
    local vid;

    if (~node.drawn)
        return;
    node.drawn = 0;
    if (~node.pos)
        return;
    gt.execalldraw (gt, views, node, node.draws, [
        'nooverride' = 1;
        'fontname' = node.fontname;
        'fontsize' = node.fontsize;
        'fontcolor' = 0;
        'drawcolor' = 0;
        'fillcolor' = 0;
    ]);
    for (vid in views)
        clearpick (views[vid].canvas, node);
};
dotty.protogt.movenode = function (gt, node, pos) {
    local dp, eid, edge;

    dp.x = pos.x - node.pos.x;
    dp.y = pos.y - node.pos.y;
    gt.undrawnode (gt, gt.views, node);
    node.pos.x = pos.x;
    node.pos.y = pos.y;
    gt.movenodedraw (node.draws, dp);
    for (eid in node.edges) {
        edge = node.edges[eid];
        gt.undrawedge (gt, gt.views, edge);
        gt.moveedgedraw (edge.draws, edge.tail.pos, edge.head.pos);
        gt.drawedge (gt, gt.views, edge);
    }
    gt.drawnode (gt, gt.views, node);
};
dotty.protogt.drawedge = function (gt, views, edge) {
    local vid, canvas;

    edge.drawn = 1;
    if (~edge.draws)
        return;
    gt.execalldraw (gt, views, edge, edge.draws, [
        'fontname' = edge.fontname;
        'fontsize' = edge.fontsize;
        'fontcolor' = edge.fontcolor;
        'drawcolor' = edge.drawcolor;
        'fillcolor' = edge.fillcolor;
    ]);
    for (vid in views) {
        canvas = views[vid].canvas;
        if (gt.edgehandles == 0 | ~edge.draws.ep)
            continue;
        arc (canvas, edge, edge.draws.ep, ['x' = 5; 'y' = 5;], ['color' = 1;]);
    }
};
dotty.protogt.undrawedge = function (gt, views, edge) {
    local vid, canvas;

    if (~edge.drawn)
        return;
    edge.drawn = 0;
    if (~edge.draws)
        return;
    gt.execalldraw (gt, views, edge, edge.draws, [
        'nooverride' = 1;
        'fontname' = edge.fontname;
        'fontsize' = edge.fontsize;
        'fontcolor' = 0;
        'drawcolor' = 0;
        'fillcolor' = 0;
    ]);
    for (vid in views) {
        canvas = views[vid].canvas;
        if (gt.edgehandles == 0 | ~edge.draws.ep)
            continue;
        arc (canvas, edge, edge.draws.ep, ['x' = 5; 'y' = 5;], ['color' = 0;]);
        clearpick (canvas, edge);
    }
};
#
# draw directives
#
dotty.protogt.execalldraw = function (gt, views, obj, draws, gc) {
    local vid, vt, did, draw, i, func;

    for (vid in views) {
        vt = views[vid];
        for (did in draws) {
            if (did == 'ep')
                continue;
            draw = draws[did];
            for (i = 0; draw[i]; i = i + 1)
                if ((func = gt.drawfunc[draw[i].type]))
                    func (gt, vt.canvas, obj, draw[i], gc);
        }
    }
};
dotty.protogt.drawfunc.E = function (gt, canvas, obj, data, gc) {
    arc (canvas, obj, data.c, data.s, [
        'color' = gc.fillcolor; 'style' = gc.style; 'width' = gc.width;
        'fill' = 'on';
    ]);
    arc (canvas, obj, data.c, data.s, [
        'color' = gc.drawcolor; 'style' = gc.style; 'width' = gc.width;
    ]);
};
dotty.protogt.drawfunc.e = function (gt, canvas, obj, data, gc) {
    arc (canvas, obj, data.c, data.s, [
        'color' = gc.drawcolor; 'style' = gc.style; 'width' = gc.width;
    ]);
};
dotty.protogt.drawfunc.P = function (gt, canvas, obj, data, gc) {
    polygon (canvas, obj, data.ps, [
        'color' = gc.fillcolor; 'style' = gc.style; 'width' = gc.width;
        'fill' = 'on';
    ]);
    polygon (canvas, obj, data.ps, [
        'color' = gc.drawcolor; 'style' = gc.style; 'width' = gc.width;
    ]);
};
dotty.protogt.drawfunc.p = function (gt, canvas, obj, data, gc) {
    polygon (canvas, obj, data.ps, [
        'color' = gc.drawcolor; 'style' = gc.style; 'width' = gc.width;
    ]);
};
dotty.protogt.drawfunc.L = function (gt, canvas, obj, data, gc) {
    polygon (canvas, obj, data.ps, [
        'color' = gc.drawcolor; 'style' = gc.style; 'width' = gc.width;
    ]);
};
dotty.protogt.drawfunc.b = function (gt, canvas, obj, data, gc) {
    splinegon (canvas, obj, data.ps, [
        'color' = gc.fillcolor; 'style' = gc.style; 'width' = gc.width;
        'fill' = 'on';
    ]);
};
dotty.protogt.drawfunc.B = function (gt, canvas, obj, data, gc) {
    splinegon (canvas, obj, data.ps, [
        'color' = gc.drawcolor; 'style' = gc.style; 'width' = gc.width;
    ]);
};
dotty.protogt.drawfunc.T = function (gt, canvas, obj, data, gc) {
    text (canvas, obj, data.p, data.s, gc.fontname, gc.fontsize, data.j, [
        'color' = gc.fontcolor; 'style' = gc.style; 'width' = gc.width;
    ]);
};
dotty.protogt.drawfunc.C = function (gt, canvas, obj, data, gc) {
    if (gc.nooverride ~= 1)
        gc.fillcolor = data.fillcolor;
};
dotty.protogt.drawfunc.c = function (gt, canvas, obj, data, gc) {
    if (gc.nooverride ~= 1) {
        gc.drawcolor = data.drawcolor;
        gc.fontcolor = data.drawcolor;
    }
};
dotty.protogt.drawfunc.F = function (gt, canvas, obj, data, gc) {
    gc.fontname = data.fn;
    gc.fontsize = data.fs;
};
dotty.protogt.drawfunc.S = function (gt, canvas, obj, data, gc) {
    gc.style = data.style;
    gc.width = data.width;
};
dotty.protogt.movenodedraw = function (draws, dp) {
    local did, draw, i, j;

    for (did in draws) {
        if (did == 'ep')
            continue;
        draw = draws[did];
        for (i = 0; draw[i]; i = i + 1) {
            if (draw[i].type == 'E' | draw[i].type == 'e') {
                draw[i].c.x = draw[i].c.x + dp.x;
                draw[i].c.y = draw[i].c.y + dp.y;
            } else if (draw[i].type == 'P' | draw[i].type == 'p') {
                for (j = 1; draw[i].ps[j]; j = j + 1) {
                    draw[i].ps[j].x = draw[i].ps[j].x + dp.x;
                    draw[i].ps[j].y = draw[i].ps[j].y + dp.y;
                }
            } else if (draw[i].type == 'L' | draw[i].type == 'B') {
                for (j = 0; draw[i].ps[j]; j = j + 1) {
                    draw[i].ps[j].x = draw[i].ps[j].x + dp.x;
                    draw[i].ps[j].y = draw[i].ps[j].y + dp.y;
                }
            } else if (draw[i].type == 'T') {
                draw[i].p.x = draw[i].p.x + dp.x;
                draw[i].p.y = draw[i].p.y + dp.y;
            }
        }
    }
};
dotty.protogt.moveedgedraw = function (draws, tp, hp) {
    local draws2, did;

    for (did in draws)
        draws2[did] = draws[did];
    for (did in draws2)
        remove (did, draws);
    draws[0] = [
        0 = [
            'type' = 'L';
            'n' = 2;
            'ps' = [
                0 = copy (tp);
                1 = copy (hp);
            ];
        ];
        'ep' = ['x' = (tp.x + hp.x) / 2; 'y' = (tp.y + hp.y) / 2;];
    ];
};
dotty.protogt.simplenodedraw = function (node, c, s) {
    local draws;

    if (node.attr.shape == 'ellipse')
        draws[0] = [
            0 = [
                'type' = 'e';
                'c' = copy (c);
                's' = ['x' = s.x / 2; 'y' = s.y / 2;];
            ];
        ];
    else
        draws[0] = [
            0 = [
                'type' = 'p';
                'n' = 5;
                'ps' = [
                    0 = ['x' = c.x - s.x / 2; 'y' = c.y - s.y / 2;];
                    1 = ['x' = c.x + s.x / 2; 'y' = c.y - s.y / 2;];
                    2 = ['x' = c.x + s.x / 2; 'y' = c.y + s.y / 2;];
                    3 = ['x' = c.x - s.x / 2; 'y' = c.y + s.y / 2;];
                    4 = ['x' = c.x - s.x / 2; 'y' = c.y - s.y / 2;];
                ];
            ];
        ];
    return draws;
};
dotty.protogt.simpleedgedraw = function (edge, tp, hp) {
    local draws;

    draws[0] = [
        0 = [
            'type' = 'L';
            'n' = 2;
            'ps' = [
                0 = copy (tp);
                1 = copy (hp);
            ];
        ];
        'ep' = ['x' = (tp.x + hp.x) / 2; 'y' = (tp.y + hp.y) / 2;];
    ];
    return draws;
};
#
# utilities
#
dotty.protogt.getcolor = function (views, name) {
    local vid, vt, color, t;

    for (vid in views) {
        vt = views[vid];
        if (~(color >= 0)) {
            if (~(vt.colors[name] >= 0))
                color = (vt.colors[name] = vt.colorn);
            else {
                color = vt.colors[name];
                break;
            }
        } else if (~(vt.colors[name] >= 0))
            vt.colors[name] = color;
        else if (vt.colors[name] ~= color)
            dotty.message (0, concat ('inconsistent color ids for ', name));
        if (setwidgetattr (vt.canvas, ['color' = [color = name;];]) ~= 1) {
            t = split (name, ' ');
            if (tablesize (t) ~= 3 |
                    setwidgetattr (vt.canvas, ['color' = [color = [
                        'h' = ston (t[0]); 's' = ston (t[1]); 'v' = ston (t[2]);
                    ];];]) ~= 1) {
                dotty.message (0, concat ('unknown color ', name, ' using #1'));
                return 1;
            }
        }
        vt.colorn = color + 1;
    }
    return color;
};
dotty.protogt.setbgcolor = function (views, name) {
    local vid, vt, t;

    for (vid in views) {
        vt = views[vid];
        if (setwidgetattr (vt.canvas, ['color' = [0 = name;];]) ~= 1) {
            t = split (name, ' ');
            if (tablesize (t) ~= 3 |
                    setwidgetattr (vt.canvas, ['color' = [0 = [
                        'h' = ston (t[0]); 's' = ston (t[1]); 'v' = ston (t[2]);
                    ];];]) ~= 1) {
                dotty.message (0, concat ('unknown bgcolor ', name));
                return;
            }
        }
        vt.colors['_bgcolor_'] = name;
    }
};
dotty.protogt.unpacksgraphattr = function (gt, sgraph) {
    local attr;

    attr = sgraph.graphattr;
    if (dotty.fontmap[attr.fontname])
        sgraph[dotty.keys.fname] = dotty.fontmap[attr.fontname];
    else
        sgraph[dotty.keys.fname] = attr.fontname;
    sgraph[dotty.keys.fsize] = ston (attr.fontsize);
    sgraph[dotty.keys.fcolor] = gt.getcolor (gt.views, attr.fontcolor);
    if (attr.color)
        sgraph[dotty.keys.dcolor] = gt.getcolor (gt.views, attr.color);
    else
        sgraph[dotty.keys.dcolor] = gt.getcolor (gt.views, 'black');
    if (attr.style == 'filled') {
        if (attr.fillcolor)
            sgraph[dotty.keys.bcolor] = gt.getcolor (gt.views, attr.fillcolor);
        else if (attr.color)
            sgraph[dotty.keys.bcolor] = gt.getcolor (gt.views, attr.color);
        else
            sgraph[dotty.keys.bcolor] = gt.getcolor (gt.views, 'lightgrey');
    }
};
dotty.protogt.unpacknodeattr = function (gt, node) {
    local attr;

    attr = node.attr;
    if (dotty.fontmap[attr.fontname])
        node[dotty.keys.fname] = dotty.fontmap[attr.fontname];
    else
        node[dotty.keys.fname] = attr.fontname;
    node[dotty.keys.fsize] = ston (attr.fontsize);
    node[dotty.keys.fcolor] = gt.getcolor (gt.views, attr.fontcolor);
    if (attr.color)
        node[dotty.keys.dcolor] = gt.getcolor (gt.views, attr.color);
    else
        node[dotty.keys.dcolor] = gt.getcolor (gt.views, 'black');
    if (attr.style == 'filled') {
        if (attr.fillcolor)
            node[dotty.keys.bcolor] = gt.getcolor (gt.views, attr.fillcolor);
        else if (attr.color)
            node[dotty.keys.bcolor] = gt.getcolor (gt.views, attr.color);
        else
            node[dotty.keys.bcolor] = gt.getcolor (gt.views, 'lightgrey');
    }
};
dotty.protogt.unpackedgeattr = function (gt, edge) {
    local attr;

    attr = edge.attr;
    if (dotty.fontmap[attr.fontname])
        edge[dotty.keys.fname] = dotty.fontmap[attr.fontname];
    else
        edge[dotty.keys.fname] = attr.fontname;
    edge[dotty.keys.fsize] = ston (attr.fontsize);
    edge[dotty.keys.fcolor] = gt.getcolor (gt.views, attr.fontcolor);
    if (attr.color)
        edge[dotty.keys.dcolor] = gt.getcolor (gt.views, attr.color);
    else
        edge[dotty.keys.dcolor] = gt.getcolor (gt.views, 'black');
    if (attr.style == 'filled') {
        if (attr.fillcolor)
            edge[dotty.keys.bcolor] = gt.getcolor (gt.views, attr.fillcolor);
        else if (attr.color)
            edge[dotty.keys.bcolor] = gt.getcolor (gt.views, attr.color);
        else
            edge[dotty.keys.bcolor] = gt.getcolor (gt.views, 'lightgrey');
    }
};
dotty.protogt.unpackattr = function (gt) {
    local gid, sgraph, nid, node, eid, edge, graph, attr;

    graph = gt.graph;
    attr = graph.graphattr;
    if (dotty.fontmap[attr.fontname])
        graph[dotty.keys.fname] = dotty.fontmap[attr.fontname];
    else
        graph[dotty.keys.fname] = attr.fontname;
    graph[dotty.keys.fsize] = ston (attr.fontsize);
    graph[dotty.keys.fcolor] = gt.getcolor (gt.views, attr.fontcolor);
    if (attr.color)
        graph[dotty.keys.dcolor] = gt.getcolor (gt.views, attr.color);
    else
        graph[dotty.keys.dcolor] = gt.getcolor (gt.views, 'black');
    if (attr.style == 'filled') {
        if (attr.fillcolor)
            graph[dotty.keys.bcolor] = gt.getcolor (gt.views, attr.fillcolor);
        else if (attr.color)
            graph[dotty.keys.bcolor] = gt.getcolor (gt.views, attr.color);
        else
            graph[dotty.keys.bcolor] = gt.getcolor (gt.views, 'lightgrey');
    }
    if (attr.bgcolor & attr.bgcolor ~= '')
        gt.setbgcolor (gt.views, attr.bgcolor);
    for (gid in graph.graphdict) {
        sgraph = graph.graphs[graph.graphdict[gid]];
        attr = sgraph.graphattr;
        if (dotty.fontmap[attr.fontname])
            sgraph[dotty.keys.fname] = dotty.fontmap[attr.fontname];
        else
            sgraph[dotty.keys.fname] = attr.fontname;
        sgraph[dotty.keys.fsize] = ston (attr.fontsize);
        sgraph[dotty.keys.fcolor] = gt.getcolor (gt.views, attr.fontcolor);
        if (attr.color)
            sgraph[dotty.keys.dcolor] = gt.getcolor (gt.views, attr.color);
        else
            sgraph[dotty.keys.dcolor] = gt.getcolor (gt.views, 'black');
        if (attr.style == 'filled') {
            if (attr.fillcolor)
                sgraph[dotty.keys.bcolor] = gt.getcolor (
                    gt.views, attr.fillcolor
                );
            else if (attr.color)
                sgraph[dotty.keys.bcolor] = gt.getcolor (gt.views, attr.color);
            else
                sgraph[dotty.keys.bcolor] = gt.getcolor (gt.views, 'lightgrey');
        }
    }
    for (nid in graph.nodedict) {
        node = graph.nodes[graph.nodedict[nid]];
        attr = node.attr;
        if (dotty.fontmap[attr.fontname])
            node[dotty.keys.fname] = dotty.fontmap[attr.fontname];
        else
            node[dotty.keys.fname] = attr.fontname;
        node[dotty.keys.fsize] = ston (attr.fontsize);
        node[dotty.keys.fcolor] = gt.getcolor (gt.views, attr.fontcolor);
        if (attr.color)
            node[dotty.keys.dcolor] = gt.getcolor (gt.views, attr.color);
        else
            node[dotty.keys.dcolor] = gt.getcolor (gt.views, 'black');
        if (attr.style == 'filled') {
            if (attr.fillcolor)
                node[dotty.keys.bcolor] = gt.getcolor (
                    gt.views, attr.fillcolor
                );
            else if (attr.color)
                node[dotty.keys.bcolor] = gt.getcolor (gt.views, attr.color);
            else
                node[dotty.keys.bcolor] = gt.getcolor (gt.views, 'lightgrey');
        }
    }
    for (eid in graph.edges) {
        edge = graph.edges[eid];
        attr = edge.attr;
        if (dotty.fontmap[attr.fontname])
            edge[dotty.keys.fname] = dotty.fontmap[attr.fontname];
        else
            edge[dotty.keys.fname] = attr.fontname;
        edge[dotty.keys.fsize] = ston (attr.fontsize);
        edge[dotty.keys.fcolor] = gt.getcolor (gt.views, attr.fontcolor);
        if (attr.color)
            edge[dotty.keys.dcolor] = gt.getcolor (gt.views, attr.color);
        else
            edge[dotty.keys.dcolor] = gt.getcolor (gt.views, 'black');
    }
};
lefty/dotty_edit.lefty000064400000043176151705127100011116 0ustar00#
# dotty_edit: editing functions and data structures
#
dotty.protogt.getnodesbyattr = function (gt, key, val) {
    local nid, node, nlist;

    nlist = [];
    for (nid in gt.graph.nodes) {
        node = gt.graph.nodes[nid];
        if (node.attr[key] == val)
            nlist[nid] = node;
    }
    return nlist;
};
dotty.protogt.reachablenodes = function (gt, node) {
    local nlist, stack, eid, edge, i;

    stack[0] = node;
    i = 1;
    while (i > 0) {
        node = stack[i - 1];
        i = i - 1;
        nlist[node.nid] = node;
        for (eid in node.edges) {
            edge = node.edges[eid];
            if (~nlist[edge.head.nid]) {
                nlist[edge.head.nid] = edge.head;
                stack[i] = edge.head;
                i = i + 1;
            }
        }
    }
    return nlist;
};
dotty.protogt.mergegraph = function (gt, graph, show) {
    local nameid, onode, pos, size, eid, eid2, tnode, hnode, oedge;

    if (~gt.noundo)
        gt.startadd2undo (gt);
    for (nameid in graph.nodedict) {
        pos = null;
        size = null;
        onode = graph.nodes[graph.nodedict[nameid]];
        if (onode.pos)
            pos = node.pos;
        if (onode.size)
            size = node.size;
        if (~(gt.graph.nodedict[nameid] >= 0)) {
            pos = null;
            size = null;
            if (onode.pos)
                pos = node.pos;
            if (onode.size)
                size = node.size;
            gt.insertnode (gt, pos, size, nameid, onode.attr, show);
        }
    }
    for (eid in graph.edges) {
        oedge = graph.edges[eid];
        tnode = gt.graph.nodes[gt.graph.nodedict[oedge.tail.name]];
        hnode = gt.graph.nodes[gt.graph.nodedict[oedge.head.name]];
        for (eid2 in tnode.edges)
            if (
                tnode.edges[eid2].tail == tnode &
                tnode.edges[eid2].head == hnode
            ) {
                oedge = null;
                break;
            }
        if (oedge)
            gt.insertedge (gt, tnode, null, hnode, null, oedge.attr, show);
    }
    if (~gt.noundo)
        gt.endadd2undo (gt);
};
dotty.protogt.insertsgraph = function (gt, name, attr, show) {
    local gid, sgraph, aid;

    if (~gt)
        return null;
    gid = gt.graph.maxgid;
    if (~name) {
        while (gt.graph.graphdict[(name = concat ('g', gid))] >= 0)
            gid = gid + 1;
    } else if (gt.graph.graphdict[name]) {
        dotty.message (0, concat ('graph: ', name, ' exists'));
        return null;
    }
    gt.graph.graphdict[name] = gid;
    gt.graph.maxgid = gid + 1;
    gt.graph.graphs[gid] = [
        dotty.keys.gid   = gid;
        dotty.keys.name  = name;
        dotty.keys.gattr = copy (gt.graph.graphattr);
        dotty.keys.nattr = copy (gt.graph.nodeattr);
        dotty.keys.eattr = copy (gt.graph.edgeattr);
    ];
    sgraph = gt.graph.graphs[gid];
    if (~attr)
        attr = [];
    if (~attr.label)
        attr.label = '\N';
    for (aid in attr)
        sgraph.graphattr[aid] = attr[aid];
    gt.unpacksgraphattr (gt, sgraph);
    if (show)
        gt.drawsgraph (gt, gt.views, sgraph);
    return sgraph;
};
dotty.protogt.removesgraph = function (gt, sgraph) {
    gt.undrawsgraph (gt, gt.views, sgraph);
    remove (sgraph.name, gt.graph.graphdict);
    remove (sgraph.gid, gt.graph.graphs);
};
dotty.protogt.insertnode = function (gt, pos, size, name, attr, show) {
    local nid, node, aid;

    nid = gt.graph.maxnid;
    if (~name) {
        while (gt.graph.nodedict[(name = concat ('n', nid))] >= 0)
            nid = nid + 1;
    } else if (gt.graph.nodedict[name] >= 0) {
        dotty.message (0, concat ('node: ', name, ' exists'));
        return null;
    }
    gt.graph.nodedict[name] = nid;
    gt.graph.maxnid = nid + 1;
    gt.graph.nodes[nid] = [
        dotty.keys.nid   = nid;
        dotty.keys.name  = name;
        dotty.keys.attr  = copy (gt.graph.nodeattr);
        dotty.keys.edges = [];
    ];
    node = gt.graph.nodes[nid];
    if (~attr)
        attr = [];
    if (~attr.label)
        attr.label = '\N';
    for (aid in attr)
        node.attr[aid] = attr[aid];
    gt.unpacknodeattr (gt, node);
    if (~pos)
        pos = ['x' = 10; 'y' = 10;];
    node[dotty.keys.pos] = copy (pos);
    if (~size)
        size = ['x' = strlen (attr.label) * 30; 'y' = 30;];
    if (size.x == 0)
        size.x = 30;
    node[dotty.keys.size] = copy (size);
    node[dotty.keys.rect] = [
        0 = ['x' = pos.x - size.x / 2; 'y' = pos.y - size.y / 2;];
        1 = ['x' = pos.x + size.x / 2; 'y' = pos.y + size.y / 2;];
    ];
    node.draws = gt.simplenodedraw (node, pos, size);
    if (show)
        gt.drawnode (gt, gt.views, node);
    if (~gt.noundo) {
        gt.startadd2undo (gt);
        gt.currundo.inserted.nodes[nid] = node;
        gt.endadd2undo (gt);
    }
    return node;
};
dotty.protogt.removenode = function (gt, node) {
    local eid, list, edge, gid;

    if (~gt.noundo)
        gt.startadd2undo (gt);
    for (eid in node.edges)
        list[eid] = node.edges[eid];
    for (eid in list)
        gt.removeedge (gt, list[eid]);
    gt.undrawnode (gt, gt.views, node);
    for (gid in gt.graph.graphs)
        remove (node.nid, gt.graph.graphs[gid].nodes);
    remove (node.name, gt.graph.nodedict);
    remove (node.nid, gt.graph.nodes);
    if (~gt.noundo) {
        gt.currundo.deleted.nodes[node.nid] = node;
        gt.endadd2undo (gt);
    }
};
dotty.protogt.insertedge = function (
    gt, nodea, porta, nodeb, portb, attr, show
) {
    local eid, edge, aid, tport, hport;

    if (~nodea | ~nodeb)
        return null;
    if (porta)
        tport = porta;
    if (portb)
        hport = portb;
    eid = gt.graph.maxeid;
    while (gt.graph.edges[eid])
        eid = eid + 1;
    gt.graph.maxeid = eid + 1;
    gt.graph.edges[eid] = [
        dotty.keys.eid   = eid;
        dotty.keys.tail  = nodea;
        dotty.keys.tport = porta;
        dotty.keys.head  = nodeb;
        dotty.keys.hport = portb;
        dotty.keys.attr  = copy (gt.graph.edgeattr);
    ];
    edge = gt.graph.edges[eid];
    if (~attr)
        attr = [];
    for (aid in attr)
        edge.attr[aid] = attr[aid];
    nodea.edges[eid] = edge;
    nodeb.edges[eid] = edge;
    gt.unpackedgeattr (gt, edge);
    edge.draws = gt.simpleedgedraw (edge, nodea.pos, nodeb.pos);
    if (show)
        gt.drawedge (gt, gt.views, edge);
    if (~gt.noundo) {
        gt.startadd2undo (gt);
        gt.currundo.inserted.edges[eid] = edge;
        gt.endadd2undo (gt);
    }
    return edge;
};
dotty.protogt.removeedge = function (gt, edge) {
    local head, tail;

    if (~gt.noundo)
        gt.startadd2undo (gt);
    if (edge.head.attr.support == 1)
        head = edge.head;
    if (edge.tail.attr.support == 1)
        if (head ~= edge.tail)
            tail = edge.tail;
    gt.undrawedge (gt, gt.views, edge);
    remove (edge.eid, edge.head.edges);
    remove (edge.eid, edge.tail.edges);
    remove (edge.eid, gt.graph.edges);
    if (head & tablesize (head.edges) == 0)
        gt.removenode (gt, head);
    if (tail & tablesize (tail.edges) == 0)
        gt.removenode (gt, tail);
    if (~gt.noundo) {
        gt.currundo.deleted.edges[edge.eid] = edge;
        gt.endadd2undo (gt);
    }
};
dotty.protogt.swapedgeids = function (gt, edge1, edge2) {
    local eid1, eid2;

    if (edge1.eid == edge2.eid)
        return;
    if (~gt.noundo)
        gt.startadd2undo (gt);
    eid1 = edge1.eid;
    eid2 = edge2.eid;
    gt.graph.edges[eid1] = edge2;
    gt.graph.edges[eid2] = edge1;
    remove (eid1, edge1.tail.edges);
    remove (eid1, edge1.head.edges);
    remove (eid2, edge2.tail.edges);
    remove (eid2, edge2.head.edges);
    edge1.tail.edges[eid2] = edge1;
    edge1.head.edges[eid2] = edge1;
    edge2.tail.edges[eid1] = edge2;
    edge2.head.edges[eid1] = edge2;
    edge1.eid = eid2;
    edge2.eid = eid1;
    if (~gt.noundo) {
        gt.currundo.swapped.edges[eid1] = edge1;
        gt.currundo.swapped.edges[eid2] = edge2;
        gt.endadd2undo (gt);
    }
};
dotty.protogt.removesubtree = function (gt, obj) {
    local nlist, node, head, nid, edge, eid;

    if (~gt.noundo)
        gt.startadd2undo (gt);
    if (obj.nid >= 0)
        node = obj;
    else if (obj.eid >= 0) {
        node = obj.head;
        gt.removeedge (gt, obj);
        if (~gt.graph.nodes[node.nid]) {
            if (~gt.noundo)
                gt.endadd2undo (gt);
            return;
        }
        for (eid in node.edges) {
            edge = node.edges[eid];
            if (edge.head == node & edge.tail ~= node) {
                if (~gt.noundo)
                    gt.endadd2undo (gt);
                return;
            }
        }
    } else {
        dotty.message (0, 'bad object type in gt.removesubtree');
        return;
    }
    nlist = [node.nid = node;];
    while (node) {
        for (eid in node.edges) {
            head = node.edges[eid].head;
            if (head ~= node)
                nlist[head.nid] = head;
        }
        gt.removenode (gt, node);
        remove (node.nid, nlist);
        node = null;
        for (nid in nlist) {
            node = nlist[nid];
            for (eid in node.edges) {
                edge = node.edges[eid];
                if (edge.head == node & edge.tail ~= node) {
                    node = null;
                    break;
                }
            }
            if (node)
                break;
        }
    }
    if (~gt.noundo)
        gt.endadd2undo (gt);
};
dotty.protogt.removenodesbyattr = function (gt, key, val) {
    local nlist, nid;

    if (~gt.noundo)
        gt.startadd2undo (gt);
    nlist = gt.getnodesbyattr (gt, key, val);
    for (nid in nlist)
        gt.removenode (gt, nlist[nid]);
    if (~gt.noundo)
        gt.endadd2undo (gt);
};
dotty.protogt.removesubtreesbyattr = function (gt, key, val) {
    local nlist, nid;

    if (~gt.noundo)
        gt.startadd2undo (gt);
    nlist = gt.getnodesbyattr (gt, key, val);
    for (nid in nlist)
        if (gt.graph.nodes[nid])
            gt.removesubtree (gt, nlist[nid]);
    if (~gt.noundo)
        gt.endadd2undo (gt);
};
dotty.protogt.groupnodes = function (
    gt, nlist, gnode, pos, size, attr, keepmulti, show
) {
    local nid, node, elist, eid, edge, nodea, nodeb, inlist, outlist;

    if (~nlist | tablesize (nlist) == 0)
        return;
    if (gnode.attr.support) {
        dotty.message (0, 'cannot group nodes in a support node');
        return;
    }
    if (~gt.noundo)
        gt.startadd2undo (gt);
    if (~gnode)
        gnode = gt.insertnode (gt, pos, size, null, attr, show);
    inlist = [];
    outlist = [];
    for (nid in nlist) {
        if ((node = nlist[nid]) == gnode)
            continue;
        elist = [];
        for (eid in node.edges)
            elist[eid] = node.edges[eid];
        for (eid in elist) {
            edge = elist[eid];
            if (edge.head == node) {
                nodea = edge.tail;
                nodeb = gnode;
                if (~keepmulti) {
                    if (inlist[nodea.nid])
                        continue;
                    inlist[nodea.nid] = nodea;
                    if (nodea == gnode)
                        outlist[nodea.nid] = nodea;
                }
            } else {
                nodea = gnode;
                nodeb = edge.head;
                if (~keepmulti) {
                    if (outlist[nodeb.nid])
                        continue;
                    outlist[nodeb.nid] = nodeb;
                    if (nodeb == gnode)
                        inlist[nodeb.nid] = nodeb;
                }
            }
            gt.insertedge (gt, nodea, null, nodeb, null, edge.attr, show);
        }
        gt.removenode (gt, node);
    }
    if (~gt.noundo)
        gt.endadd2undo (gt);
    return gnode;
};
dotty.protogt.groupnodesbyattr = function (
    gt, key, val, attr, keepmulti, show
) {
    local nlist, nid, pos, size;

    pos = null;
    size = null;
    nlist = gt.getnodesbyattr (gt, key, val);
    if (show)
        for (nid in nlist) {
            pos = nlist[nid].pos;
            size = nlist[nid].size;
            break;
        }
    return gt.groupnodes (gt, nlist, null, pos, size, attr, keepmulti, show);
};
dotty.protogt.cut = function (gt, obj, set, mode, op) {
    local clipgt, list, node, nid, edge, eid, clipnode;

    clipgt = dotty.clipgt;
    clipgt.graph = copy (dotty.protogt.graph);
    if (obj.eid >= 0) { # it's an edge
        list.edges[obj.eid] = obj;
        node = obj.head;
    } else if (obj.nid >= 0) {
        list.nodes[obj.nid] = obj;
        node = obj;
        for (eid in node.edges)
            list.edges[eid] = node.edges[eid];
    } else {
        dotty.message (0, 'unknown object type in gt.cut');
        return;
    }
    if (set == 'reachable') {
        list.nodes = gt.reachablenodes (gt, node);
        for (nid in list.nodes) {
            node = list.nodes[nid];
            for (eid in node.edges) {
                edge = node.edges[eid];
                list.edges[edge.eid] = edge;
            }
        }
    }
    if (mode == 'support') {
        for (eid in list.edges) {
            edge = list.edges[eid];
            if (~list.nodes[edge.tail.nid]) {
                list.support[edge.tail.nid] = edge.tail;
                list.nodes[edge.tail.nid] = edge.tail;
            }
            if (~list.nodes[edge.head.nid]) {
                list.support[edge.head.nid] = edge.head;
                list.nodes[edge.head.nid] = edge.head;
            }
        }
    }
    for (nid = 0; nid < gt.graph.maxnid; nid = nid + 1) {
        if (~list.nodes[nid])
            continue;
        node = list.nodes[nid];
        clipnode = gt.insertnode (clipgt, null, null, node.name, node.attr, 0);
        if (list.support[nid])
            clipnode.support = 1;
        list.clipnodes[nid] = clipnode;
    }
    for (eid = 0; eid < gt.graph.maxeid; eid = eid + 1) {
        if (~list.edges[eid])
            continue;
        edge = list.edges[eid];
        if (~list.nodes[edge.tail.nid] | ~list.nodes[edge.head.nid])
            continue;
        gt.insertedge (
            clipgt, list.clipnodes[edge.tail.nid], null,
            list.clipnodes[edge.head.nid], null, edge.attr, 0
        );
    }
    if (op ~= 'cut')
        return;
    if (~gt.noundo)
        gt.startadd2undo (gt);
    for (eid in list.edges)
        gt.removeedge (gt, list.edges[eid]);
    for (nid in list.nodes)
        if (~list.support[nid] & gt.graph.nodes[nid])
            gt.removenode (gt, list.nodes[nid]);
    if (~gt.noundo)
        gt.endadd2undo (gt);
};
dotty.protogt.paste = function (gt, pos, show) {
    local clipgt, offset, center, nid, node, eid, edge, nodes;

    if (~gt.noundo)
        gt.startadd2undo (gt);
    clipgt = dotty.clipgt;
    if (clipgt.graph.rect)
        center = [
            'x' = (clipgt.graph.rect[1].x + clipgt.graph.rect[0].x) / 2;
            'y' = (clipgt.graph.rect[1].y + clipgt.graph.rect[0].y) / 2;
        ];
    else
        center = pos;
    offset = [
        'x' = center.x - pos.x;
        'y' = center.y - pos.y;
    ];
    for (nid = 0; clipgt.graph.nodes[nid]; nid = nid + 1) {
        node = clipgt.graph.nodes[nid];
        if (node.attr.label == '\N' | ~node.attr.label)
            node.attr.label = node.name;
        if (node.support == 1)
            nodes[nid] = gt.insertnode (gt, [
                'x' = node.pos.x - offset.x;
                'y' = node.pos.y - offset.y;
            ], null, null, [
                'support' = 1; 'shape' = 'circle';
                'label' = ''; 'width' = 0.2;
            ], show);
        else
            nodes[nid] = gt.insertnode (gt, [
                'x' = node.pos.x - offset.x;
                'y' = node.pos.y - offset.y;
            ], node.size, null, node.attr, show);
    }
    for (eid = 0; clipgt.graph.edges[eid]; eid = eid + 1) {
        edge = clipgt.graph.edges[eid];
        gt.insertedge (
            gt, nodes[edge.tail.nid], null,
            nodes[edge.head.nid], null, edge.attr, show
        );
    }
    if (~gt.noundo)
        gt.endadd2undo (gt);
};
dotty.protogt.startadd2undo = function (gt) {
    if (~gt.undoarray.level)
        gt.currundo = (
            gt.undoarray.entries[tablesize (gt.undoarray.entries)] = []
        );
    gt.undoarray.level = gt.undoarray.level + 1;
};
dotty.protogt.endadd2undo = function (gt) {
    gt.undoarray.level = gt.undoarray.level - 1;
};
dotty.protogt.undo = function (gt, show) {
    local entry, n, eid, edge, nid, node, edges;

    if ((n = tablesize (gt.undoarray.entries)) < 1)
        return;
    entry = gt.undoarray.entries[n - 1];
    remove (n - 1, gt.undoarray.entries);
    remove ('currundo', gt);
    gt.noundo = 1;
    # hardwire nodes and edges back with the same id's as the originals
    for (nid in entry.deleted.nodes) {
        node = entry.deleted.nodes[nid];
        gt.graph.nodedict[node.name] = node.nid;
        gt.graph.nodes[node.nid] = node;
        node.edges = [];
        if (show)
            gt.drawnode (gt, gt.views, node);
    }
    for (eid in entry.deleted.edges) {
        edge = entry.deleted.edges[eid];
        gt.graph.edges[edge.eid] = edge;
        edge.head.edges[edge.eid] = edge;
        edge.tail.edges[edge.eid] = edge;
        if (show)
            gt.drawedge (gt, gt.views, edge);
    }
    if (entry.swapped.edges) {
        if (tablesize (entry.swapped.edges) == 2) {
            n = 0;
            for (eid in entry.swapped.edges) {
                edges[n] = entry.swapped.edges[eid];
                n = n + 1;
            }
            gt.swapedgeids (gt, edges[0], edges[1]);
        } else
            dotty.message (0, 'cannot handle undoing swap of > 2 edges');
    }
    for (eid in entry.inserted.edges) {
        edge = entry.inserted.edges[eid];
        gt.removeedge (gt, edge);
    }
    for (nid in entry.inserted.nodes) {
        node = entry.inserted.nodes[nid];
        gt.removenode (gt, node);
    }
    gt.noundo = 0;
};
lefty/dotty_layout.lefty000064400000034352151705127100011502 0ustar00#
# 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+', '%e -Txdot1.2')) >= 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.gv', '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.gv'
                );
                dotty.message (
                    0, 'please fill out a bug report at'
                );
                dotty.message (
                    0, 'http://www.graphviz.org/bugs/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, eid, edge1, edge2;
    local t1, pos, size;

    graph = gt.graph;
    for (gid in graph2.graphdict) {
        if (~(sgraph1 = graph.graphs[graph.graphdict[gid]]))
            continue;
        sgraph2 = graph2.graphs[graph2.graphdict[gid]];
        sgraph1.draws = gt.unpackalldraw (gt, sgraph2.graphattr);
    }
    for (nid in graph2.nodedict) {
        if (~(node1 = graph.nodes[graph.nodedict[nid]]))
            continue;
        node2 = graph2.nodes[graph2.nodedict[nid]];
        node1.draws = gt.unpackalldraw (gt, node2.attr);
        t1 = split (node2.attr.pos, ',');
        pos = ['x' = ston (t1[0]); 'y' = ston (t1[1]);];
        size = [
            'x' = ston (node2.attr.width) * 72;
            'y' = ston (node2.attr.height) * 72;
        ];
        node1.pos = pos;
        node1.size = size;
        node1.rect = [
            0 = ['x' = pos.x - size.x / 2; 'y' = pos.y - size.y / 2;];
            1 = ['x' = pos.x + size.x / 2; 'y' = pos.y + size.y / 2;];
        ];
    }
    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;
        edge1.draws = gt.unpackalldraw (gt, edge2.attr);
    }
    graph.draws = gt.unpackalldraw (gt, graph2.graphattr);
    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 (gt.graph ~= graph2)
        return;
    # strip position and size info from the attributes
    for (gid in graph2.graphdict) {
        sgraph2 = graph2.graphs[graph2.graphdict[gid]];
        gt.removealldraw (gt, sgraph2.graphattr);
        if (sgraph2.graphattr.bb)
            remove ('bb', sgraph2.graphattr);
    }
    for (nid in graph2.nodedict) {
        node2 = graph2.nodes[graph2.nodedict[nid]];
        gt.removealldraw (gt, node2.attr);
        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];
        gt.removealldraw (gt, edge2.attr);
        if (edge2.attr.pos)
            remove ('pos', edge2.attr);
        if (edge2.attr.lp)
            remove ('lp', edge2.attr);
    }
    gt.removealldraw (gt, graph2.graphattr);
    remove ('bb', graph2.graphattr);
    if (graph2.graphattr.lp)
        remove ('lp', graph2.graphattr);
};
#
# draw directive parsing
#
dotty.protogt.unpackalldraw = function (gt, attr) {
    local o, did;

    o = [];
    if (attr._draw_)
        o._draw_ = gt.unpackdraw (gt, attr._draw_);
    if (attr._background)
        o._background = gt.unpackdraw (gt, attr._background);
    if (attr._ldraw_)
        o._ldraw_ = gt.unpackdraw (gt, attr._ldraw_);
    if (attr._hdraw_)
        o._hdraw_ = gt.unpackdraw (gt, attr._hdraw_);
    if (attr._tdraw_)
        o._tdraw_ = gt.unpackdraw (gt, attr._tdraw_);
    if (attr._hldraw_)
        o._hldraw_ = gt.unpackdraw (gt, attr._hldraw_);
    if (attr._tldraw_)
        o._tldraw_ = gt.unpackdraw (gt, attr._tldraw_);
    for (did in o)
        if (o[did].ep) {
            o.ep = o[did].ep;
            break;
        }
    return o;
};
dotty.protogt.removealldraw = function (gt, attr) {
    if (attr._draw_)
        remove ('_draw_', attr);
    if (attr._ldraw_)
        remove ('_ldraw_', attr);
    if (attr._hdraw_)
        remove ('_hdraw_', attr);
    if (attr._tdraw_)
        remove ('_tdraw_', attr);
    if (attr._hldraw_)
        remove ('_hldraw_', attr);
    if (attr._tldraw_)
        remove ('_tldraw_', attr);
};
dotty.protogt.unpackdraw = function (gt, attr) {
    local oo, o, tt, t, n, i, j, s, l, ep;

    oo = [];
    t = split (attr, ' ', 0);
    n = tablesize (t);
    if (t[n - 1] == '') {
        remove (n - 1, t);
        n = n - 1;
    }
    i = 0;
    while (i < n) {
        o = [];
        if (t[i] == 'E') {
            o.type = t[i];
            o.c.x = ston (t[i + 1]);
            o.c.y = ston (t[i + 2]);
            o.s.x = ston (t[i + 3]);
            o.s.y = ston (t[i + 4]);
            i = i + 5;
        } else if (t[i] == 'e') {
            o.type = t[i];
            o.c.x = ston (t[i + 1]);
            o.c.y = ston (t[i + 2]);
            o.s.x = ston (t[i + 3]);
            o.s.y = ston (t[i + 4]);
            i = i + 5;
        } else if (t[i] == 'P') {
            o.type = t[i];
            o.n = ston (t[i + 1]);
            for (j = 0; j < o.n; j = j + 1) {
                o.ps[j].x = ston (t[i + 2 + j * 2]);
                o.ps[j].y = ston (t[i + 2 + j * 2 + 1]);
            }
            i = i + 2 + o.n * 2;
            o.ps[o.n] = o.ps[0];
            o.n = o.n + 1;
        } else if (t[i] == 'p') {
            o.type = t[i];
            o.n = ston (t[i + 1]);
            for (j = 0; j < o.n; j = j + 1) {
                o.ps[j].x = ston (t[i + 2 + j * 2]);
                o.ps[j].y = ston (t[i + 2 + j * 2 + 1]);
            }
            i = i + 2 + o.n * 2;
            o.ps[o.n] = o.ps[0];
            o.n = o.n + 1;
        } else if (t[i] == 'L') {
            o.type = t[i];
            o.n = ston (t[i + 1]);
            for (j = 0; j < o.n; j = j + 1) {
                o.ps[j].x = ston (t[i + 2 + j * 2]);
                o.ps[j].y = ston (t[i + 2 + j * 2 + 1]);
            }
            i = i + 2 + o.n * 2;
            if (~ep)
                ep = copy (o.ps[1]);
        } else if (t[i] == 'B') {
            o.type = t[i];
            o.n = ston (t[i + 1]);
            for (j = 0; j < o.n; j = j + 1) {
                o.ps[j].x = ston (t[i + 2 + j * 2]);
                o.ps[j].y = ston (t[i + 2 + j * 2 + 1]);
            }
            i = i + 2 + o.n * 2;
            if (~ep)
                ep = copy (o.ps[1]);
        } else if (t[i] == 'b') {
            o.type = t[i];
            o.n = ston (t[i + 1]);
            for (j = 0; j < o.n; j = j + 1) {
                o.ps[j].x = ston (t[i + 2 + j * 2]);
                o.ps[j].y = ston (t[i + 2 + j * 2 + 1]);
            }
            i = i + 2 + o.n * 2;
            if (~ep)
                ep = copy (o.ps[1]);
        } else if (t[i] == 'T') {
            o.type = t[i];
            o.p.x = ston (t[i + 1]);
            o.p.y = ston (t[i + 2]);
            o.j = ston (t[i + 3]);
            if (o.j == -1)
                o.j = 'lb';
            else if (o.j == 1)
                o.j = 'rb';
            else if (o.j == 0)
                o.j = 'cb';
            o.w = ston (t[i + 4]);
            o.n = ston (t[i + 5]);
            i = i + 6;
            s = t[i];
            i = i + 1;
            l = strlen (s) - 1;
            while (l < o.n) {
                s = concat (s, ' ', t[i]);
                l = l + 1 + strlen (t[i]);
                i = i + 1;
            }
            tt = split (s, '');
            l = tablesize (tt);
            s = '';
            for (j = 1; j < l; j = j + 1)
                s = concat (s, tt[j]);
            o.s = s;
        } else if (t[i] == 'C') {
            o.type = t[i];
            o.n = ston (t[i + 1]);
            i = i + 2;
            s = t[i];
            i = i + 1;
            l = strlen (s) - 1;
            while (l < o.n) {
                s = concat (s, ' ', t[i]);
                l = l + 1 + strlen (t[i]);
                i = i + 1;
            }
            tt = split (s, '');
            l = tablesize (tt);
            s = '';
            for (j = 1; j < l; j = j + 1)
                s = concat (s, tt[j]);
            o.fillcolor = gt.getcolor (gt.views, s);
        } else if (t[i] == 'c') {
            o.type = t[i];
            o.n = ston (t[i + 1]);
            i = i + 2;
            s = t[i];
            i = i + 1;
            l = strlen (s) - 1;
            while (l < o.n) {
                s = concat (s, ' ', t[i]);
                l = l + 1 + strlen (t[i]);
                i = i + 1;
            }
            tt = split (s, '');
            l = tablesize (tt);
            s = '';
            for (j = 1; j < l; j = j + 1)
                s = concat (s, tt[j]);
            o.drawcolor = gt.getcolor (gt.views, s);
        } else if (t[i] == 'F') {
            o.type = t[i];
            o.fs = ston (t[i + 1]);
            o.n = ston (t[i + 2]);
            i = i + 3;
            s = t[i];
            i = i + 1;
            l = strlen (s) - 1;
            while (l < o.n) {
                s = concat (s, ' ', t[i]);
                l = l + 1 + strlen (t[i]);
                i = i + 1;
            }
            tt = split (s, '');
            l = tablesize (tt);
            s = '';
            for (j = 1; j < l; j = j + 1)
                s = concat (s, tt[j]);
            o.ofn = s;
            o.fn = dotty.fontmap[s];
        } else if (t[i] == 'S') {
            o.type = t[i];
            o.n = ston (t[i + 1]);
            i = i + 2;
            s = t[i];
            i = i + 1;
            l = strlen (s) - 1;
            while (l < o.n) {
                s = concat (s, ' ', t[i]);
                l = l + 1 + strlen (t[i]);
                i = i + 1;
            }
            tt = split (s, '');
            l = tablesize (tt);
            s = '';
            for (j = 1; j < l; j = j + 1)
                s = concat (s, tt[j]);
            if (
                s == 'solid' | s == 'dashed' | s == 'dotted' |
                s == 'longdashed' | s == 'shortdashed'
            )
                o.style = s;
            else if (s == 'bold')
                o.width = 3;
            else {
                tt = split (s, '(');
                if (tt[0] == 'setlinewidth') {
                    tt = split (tt[1], ')');
                    o.width = ston (tt[0]);
                } else
                    continue;
            }
        } else if (t[i] == 'I') {
            i = i + 7;
        } else if (t[i] == 't') {
            i = i + 2;
        } else {
            dotty.message (0, concat ('draw language parser error: ', t[i]));
            return null;
        }
        oo[tablesize (oo)] = o;
    }
    oo.ep = ep;
    return oo;
};
lefty/dotty_ui.lefty000064400000033602151705127100010577 0ustar00#
# dotty_ui: user interface functions and data structures
#
dotty.protogt.doaction = function (data, s) {
    local vt, gt;

    vt = dotty.views[data.widget];
    gt = dotty.graphs[vt.gtid];
    data.menuitem = s;
    if (data.obj.nid >= 0) {
        if (gt.actions.node[s]) {
            gt.actions.node[s] (gt, vt, data.obj, data);
            return;
        }
    } else if (data.obj.eid >= 0) {
        if (gt.actions.edge[s]) {
            gt.actions.edge[s] (gt, vt, data.obj, data);
            return;
        }
    }
    if (gt.actions.general[s])
        gt.actions.general[s] (gt, vt, data);
};
dotty.protogt.actions.general = [
    "undo" = function (gt, vt, data) {
        gt.undo (gt, 1);
    };
    "paste" = function (gt, vt, data) {
        gt.paste (gt, data.pos, 1);
    };
    "do layout" = function (gt, vt, data) {
        gt.layoutgraph (gt);
    };
    "cancel layout" = function (gt, vt, data) {
        gt.cancellayout (gt);
    };
    "redraw" = function (gt, vt, data) {
        gt.redrawgraph (gt, [vt.vtid = vt;]);
    };
    "new graph" = function (gt, vt, data) {
        gt.erasegraph (gt, null, null);
    };
    "load graph" = function (gt, vt, data) {
        gt.loadgraph (gt, null, 'file', dotty.protogt.graph, 1);
    };
    "reload graph" = function (gt, vt, data) {
        gt.loadgraph (gt, gt.name, gt.type, gt.graph, 1);
    };
    "save graph" = function (gt, vt, data) {
        gt.savegraph (gt, gt.name, gt.type);
    };
    "save graph as" = function (gt, vt, data) {
        gt.savegraph (gt, null, 'file');
    };
    "open view" = function (gt, vt, data) {
        gt = dotty.protogt.creategraph (null);
        gt.createview (gt, null);
    };
    "copy view" = function (gt, vt, data) {
        gt = gt.copygraph (gt);
        gt.createview (gt, vt);
    };
    "birdseye view" = function (gt, vt, data) {
        gt.createview (gt, dotty.protovt.birdseye);
    };
    "clone view" = function (gt, vt, data) {
        gt.createview (gt, vt);
    };
    "close view" = function (gt, vt, data) {
        gt.destroyview (gt, vt);
        if (tablesize (gt.views) == 0)
            gt.destroygraph (gt);
    };
    "set graph attr" = function (gt, vt, data) {
        gt.setattr (gt, ['attr' = gt.graph.graphattr;]);
    };
    "set node attr" = function (gt, vt, data) {
        gt.setattr (gt, ['attr' = gt.graph.nodeattr;]);
    };
    "set edge attr" = function (gt, vt, data) {
        gt.setattr (gt, ['attr' = gt.graph.edgeattr;]);
    };
    "zoom in" = function (gt, vt, data) {
        gt.zoom (gt, vt, 0.5, data.pos);
    };
    "zoom out" = function (gt, vt, data) {
        gt.zoom (gt, vt, 2, data.pos);
    };
    "zoom in slowly" = function (gt, vt, data) {
        gt.zoom (gt, vt, 0.9, data.pos);
    };
    "zoom out slowly" = function (gt, vt, data) {
        gt.zoom (gt, vt, 1.1, data.pos);
    };
    "scroll horizontally" = function (gt, vt, data) {
        vt.scrollmode = 'h';
    };
    "scroll vertically" = function (gt, vt, data) {
        vt.scrollmode = 'v';
    };
    "find node" = function (gt, vt, data) {
        gt.findnode (gt, vt);
    };
    "print graph" = function (gt, vt, data) {
        gt.printorsave (gt, vt, null, null, null, null);
    };
    "text view" = function (gt, vt, data) {
        if (dotty.txtview == 'on')
            dotty.txtview = 'off';
        else
            dotty.txtview = 'on';
        txtview (dotty.txtview);
    };
    "quit" = function (gt, vt, data) {
        exit ();
    };
];
dotty.protogt.actions.node = [
    "cut" = function (gt, vt, obj, data) {
        gt.cut (gt, obj, 'one', 'support', 'cut');
        dotty.clipgt.layoutgraph (dotty.clipgt);
    };
    "Cut" = function (gt, vt, obj, data) {
        gt.cut (gt, obj, 'reachable', 'support', 'cut');
        dotty.clipgt.layoutgraph (dotty.clipgt);
    };
    "copy" = function (gt, vt, obj, data) {
        gt.cut (gt, obj, 'one', 'support', 'copy');
        dotty.clipgt.layoutgraph (dotty.clipgt);
    };
    "Copy" = function (gt, vt, obj, data) {
        gt.cut (gt, obj, 'reachable', 'support', 'copy');
        dotty.clipgt.layoutgraph (dotty.clipgt);
    };
    "group" = function (gt, vt, obj, data) {
        local kv;

        if ((kv = gt.getattr (gt, obj)))
            gt.groupnodesbyattr (gt, kv.key, kv.val, [
                'label' = kv.val; kv.key = kv.val;
            ], 1, 1);
    };
    "Group" = function (gt, vt, obj, data) {
        local kv;

        if ((kv = gt.getattr (gt, obj)))
            gt.groupnodesbyattr (gt, kv.key, kv.val, [
                'label' = kv.val; kv.key = kv.val;
            ], 0, 1);
    };
    "delete" = function (gt, vt, obj, data) {
        if (obj.eid >= 0)
            gt.removeedge (gt, obj);
        else
            gt.removenode (gt, obj);
    };
    "Delete" = function (gt, vt, obj, data) {
        gt.removesubtree (gt, obj);
    };
    "remove" = function (gt, vt, obj, data) {
        if (obj.nid >= 0)
            if ((kv = gt.getattr (gt, obj)))
                gt.removenodesbyattr (gt, kv.key, kv.val);
    };
    "Remove" = function (gt, vt, obj, data) {
        if (obj.nid >= 0)
            if ((kv = gt.getattr (gt, obj)))
                gt.removesubtreesbyattr (gt, kv.key, kv.val);
    };
    "set attr" = function (gt, vt, obj, data) {
        gt.setattr (gt, obj);
    };
    "print attr" = function (gt, vt, obj, data) {
        if (obj.nid >= 0)
            echo ('node: ', obj.name);
        dump (obj.attr);
    };
];
dotty.protogt.actions.edge = dotty.protogt.actions.node;
dotty.protovt.normal.menus = [
    'general' = [
        0 = "undo";
        1 = "paste";
        2 = "do layout";
        3 = "cancel layout";
        4 = "redraw";
        5 = "new graph";
        6 = "load graph";
        7 = "reload graph";
        8 = "save graph";
        9 = "save graph as";
        10 = "open view";
        11 = "copy view";
        12 = "clone view";
        13 = "birdseye view";
        14 = "close view";
        15 = "set graph attr";
        16 = "set node attr";
        17 = "set edge attr";
        18 = "zoom in";
        19 = "zoom out";
        20 = "find node";
        21 = "print graph";
        22 = "text view";
        23 = "quit";
    ];
    'node' = [
        0 = "cut";
        1 = "Cut";
        2 = "copy";
        3 = "Copy";
        4 = "group";
        5 = "Group";
        6 = "delete";
        7 = "Delete";
        8 = "remove";
        9 = "Remove";
        10 = "set attr";
        11 = "print attr";
    ];
    'edge' = [
        0 = "cut";
        1 = "Cut";
        2 = "copy";
        3 = "Copy";
        4 = "delete";
        5 = "Delete";
        6 = "set attr";
        7 = "print attr";
    ];
];
dotty.protovt.normal.keys = [
    'general' = [
        'u' = "undo";
        'p' = "paste";
        'l' = "do layout";
        'k' = "cancel layout";
        ' ' = "redraw";
        'L' = "reload graph";
        's' = "save graph";
        'Z' = "zoom in slowly";
        'z' = "zoom out slowly";
        'h' = "scroll horizontally";
        'v' = "scroll vertically";
    ];
    'node' = [
        'c' = "copy";
        'C' = "Copy";
        'g' = "group";
        'G' = "Group";
        'd' = "delete";
        'D' = "Delete";
        'r' = "remove";
        'R' = "Remove";
        'a' = "set attr";
    ];
    'edge' = [
        'c' = "copy";
        'C' = "Copy";
        'd' = "delete";
        'D' = "Delete";
        'a' = "set attr";
    ];
];
dotty.protovt.birdseye.menus = dotty.protovt.normal.menus;
dotty.protovt.birdseye.keys = dotty.protovt.normal.keys;
dotty.protovt.normal.uifuncs = [
    'leftdown' = function (data) {
        local gt;

        gt = dotty.graphs[dotty.views[data.widget].gtid];
        if (data.obj.nid >= 0) {
            dotty.node2move = data.obj;
            dotty.movewidget = data.widget;
            dotty.rp2 = data.pos;
        }
    };
    'leftmove' = function (data) {
        local gt;

        gt = dotty.graphs[dotty.views[data.widget].gtid];
        if (dotty.node2move & (
            dotty.rp2.x ~= data.pos.x | dotty.rp2.y ~= data.pos.y
        )) {
            gt.movenode (gt, dotty.node2move, data.pos);
            dotty.rp2 = data.pos;
        }
    };
    'leftup' = function (data) {
        local gt;

        gt = dotty.graphs[dotty.views[data.widget].gtid];
        if (dotty.node2move) {
            if (dotty.movewidget == data.widget)
                gt.movenode (gt, dotty.node2move, data.pos);
            dotty.node2move = 0;
        } else if (~data.obj)
            gt.insertnode (gt, data.pos, null, null, null, 1);
    };
    'middledown' = function (data) {
        if (~(data.obj.nid >= 0))
            return;
        dotty.rubberband = 1;
        dotty.movewidget = data.widget;
        setgfxattr (data.widget, ['mode' = 'xor';]);
        dotty.rp1 = data.pos;
        dotty.rp2 = data.pos;
        line (data.widget, null, dotty.rp1, dotty.rp2, ['color' = 1;]);
    };
    'middlemove' = function (data) {
        if (dotty.rubberband ~= 1 | (
            dotty.rp2.x == data.pos.x & dotty.rp2.y == data.pos.y
        ))
            return;
        line (data.widget, null, dotty.rp1, dotty.rp2, ['color' = 1;]);
        dotty.rp2 = data.pos;
        line (data.widget, null, dotty.rp1, dotty.rp2, ['color' = 1;]);
    };
    'middleup' = function (data) {
        local gt;

        gt = dotty.graphs[dotty.views[data.widget].gtid];
        if (dotty.rubberband ~= 1)
            return;
        dotty.rubberband = 0;
        line (dotty.movewidget, null, dotty.rp1, dotty.rp2, ['color' = 1;]);
        setgfxattr (dotty.movewidget, ['mode' = 'src';]);
        if (dotty.movewidget ~= data.widget | ~(
            data.pobj.nid >= 0) | ~(data.obj.nid >= 0
        ))
            return;
        if (data.pobj.attr.support)
            gt.groupnodes (gt, [
                data.obj.nid = data.obj;
                data.pobj.nid = data.pobj;
            ], data.obj, null, null, null, 1, 1);
        else if (data.obj.attr.support)
            gt.groupnodes (gt, [
                data.obj.nid = data.obj;
                data.pobj.nid = data.pobj;
            ], data.pobj, null, null, null, 1, 1);
        else
            gt.insertedge (gt, data.pobj, null, data.obj, null, null, 1);
    };
    'rightup' = function (data) {
        local vt, gt, menu, i;

        vt = dotty.views[data.widget];
        gt = dotty.graphs[vt.gtid];
        if (~data.obj)
            menu = vt.menus.general;
        else if (data.obj.nid >= 0)
            menu = vt.menus.node;
        else if (data.obj.eid >= 0)
            menu = vt.menus.edge;
        if ((i = displaymenu (data.widget, menu)) >= 0)
            gt.doaction (data, menu[i]);
    };
    'button3up' = function (data) {
        local vt, attr;

        vt = dotty.views[data.widget];
        attr = getwidgetattr (vt.scroll, [0 = 'childcenter';]);
        if (vt.scrollmode == 'h')
            attr.childcenter.x = attr.childcenter.x - 40;
        else
            attr.childcenter.y = attr.childcenter.y - 40;
        setwidgetattr (vt.scroll, ['childcenter' = attr.childcenter;]);
    };
    'button4up' = function (data) {
        local vt, attr;

        vt = dotty.views[data.widget];
        attr = getwidgetattr (vt.scroll, [0 = 'childcenter';]);
        if (vt.scrollmode == 'h')
            attr.childcenter.x = attr.childcenter.x + 40;
        else
            attr.childcenter.y = attr.childcenter.y + 40;
        setwidgetattr (vt.scroll, ['childcenter' = attr.childcenter;]);
    };
    'keyup' = function (data) {
        local vt, gt, action;

        vt = dotty.views[data.widget];
        gt = dotty.graphs[vt.gtid];
        if (data.obj.nid >= 0) {
            if (vt.keys.node[data.key])
                action = vt.keys.node[data.key];
        } else if (data.obj.eid >= 0) {
            if (vt.keys.edge[data.key])
                action = vt.keys.edge[data.key];
        }
        if (~action)
            if (vt.keys.general[data.key])
                action = vt.keys.general[data.key];
        if (action)
            gt.doaction (data, action);
    };
    'redraw' = function (data) {
        local vt, gt;

        vt = dotty.views[data.widget];
        gt = dotty.graphs[vt.gtid];
        gt.drawgraph (gt, [vt.vtid = vt;]);
    };
    'closeview' = function (data) {
        local vt, gt;

        vt = dotty.views[data.widget];
        gt = dotty.graphs[vt.gtid];
        gt.destroyview (gt, vt);
        if (tablesize (gt.views) == 0)
            gt.destroygraph (gt);
    };
];
dotty.protovt.birdseye.uifuncs = [
    'leftdown' = function (data) {
        local gt, vid;

        gt = dotty.graphs[dotty.views[data.widget].gtid];
        for (vid in gt.views) {
            vt = gt.views[vid];
            if (vt.type ~= 'birdseye')
                gt.setviewcenter ([vid = vt;], data.pos);
        }
    };
    'leftmove' = function (data) {
        local gt, vid;

        gt = dotty.graphs[dotty.views[data.widget].gtid];
        for (vid in gt.views) {
            vt = gt.views[vid];
            if (vt.type ~= 'birdseye')
                gt.setviewcenter ([vid = vt;], data.pos);
        }
    };
    'leftup' = function (data) {
        local gt, vid;

        gt = dotty.graphs[dotty.views[data.widget].gtid];
        for (vid in gt.views) {
            vt = gt.views[vid];
            if (vt.type ~= 'birdseye')
                gt.setviewcenter ([vid = vt;], data.pos);
        }
    };
    'middledown' = dotty.protovt.normal.uifuncs.middledown;
    'middlemove' = dotty.protovt.normal.uifuncs.middlemove;
    'middleup' = dotty.protovt.normal.uifuncs.middleup;
    'rightup' = dotty.protovt.normal.uifuncs.rightup;
    'keyup' = dotty.protovt.normal.uifuncs.keyup;
    'redraw' = dotty.protovt.normal.uifuncs.redraw;
    'closeview' = dotty.protovt.normal.uifuncs.closeview;
];
dotty.monitorfile = function (data) {
    local gtid, gt, lpt;

    for (gtid in dotty.layoutpending) {
        lpt = dotty.layoutpending[gtid];
        if (lpt.fd == data.fd) {
            gt = dotty.graphs[lpt.gtid];
            gt.haveinput = 1;
            gt.layoutgraph (gt);
            return 1;
        }
    }
    return 0;
};
lefty/fractal.lefty000064400000005234151705127100010353 0ustar00load ('def.lefty');
definit ();
#
# initialize window data
#
canvas = defcanvas;
wrect = [0 = ['x' = 0; 'y' = 0;]; 1 = ['x' = 400; 'y' = 500;];];
setwidgetattr (canvas, ['window' = wrect;]);

sq = function (x) {
    return x * x;
};

# data structures
#
length = 300;
center = ['x' = 200; 'y' = 250;];
radius = 2 * length / sqrt (12);
fractalangle = 0;
maxlevel = 2;

# drawing functions
#
# draw a Koch curve (a ``snowflake'' fractal)
#
# start with a triangle and keep replacing edges
# with the construct: _/\_
# until the recursion level reaches 'maxlevel'
#
fractal = function (level, length, angle) {
    local nlength, newpenpos;

    if (level >= maxlevel) {
        newpenpos.x = penpos.x + length * cos (angle);
        newpenpos.y = penpos.y + length * sin (angle);
        line (canvas, null, penpos, newpenpos, ['color' = 1;]);
        penpos = newpenpos;
        return;
    }
    nlength = length / 3;
    fractal (level + 1, nlength, angle);
    fractal (level + 1, nlength, angle + 60);
    fractal (level + 1, nlength, angle - 60);
    fractal (level + 1, nlength, angle);
};
drawfractal = function () {
    clear (canvas);
    setpick (canvas, center, wrect);
    penpos = [
        'x' = center.x + cos (fractalangle + 210) * radius;
        'y' = center.y + sin (fractalangle + 210) * radius;
    ];
    fractal (0, length, fractalangle +  60);
    fractal (0, length, fractalangle -  60);
    fractal (0, length, fractalangle - 180);
    remove ('penpos');
};

# editing functions
#
# transform the fractal.
#
# map point 'prevpoint' to point 'currpoint'
# with respect to the center of the fractal.
#
transformfractal = function (prevpoint, currpoint) {
    local prevtan, currtan, prevradius, currradius;

    prevtan = atan (prevpoint.y - center.y, prevpoint.x - center.x);
    currtan = atan (currpoint.y - center.y, currpoint.x - center.x);
    fractalangle = fractalangle + (currtan - prevtan);
    prevradius = sqrt (
        sq (prevpoint.y - center.y) + sq (prevpoint.x - center.x)
    );
    currradius = sqrt (
        sq (currpoint.y - center.y) + sq (currpoint.x - center.x)
    );
    radius = radius / prevradius * currradius;
    length = radius / 2 * sqrt (12);
};

# user interface functions
#
# bind changes to the fractal to user actions
#
leftup = function (data) {
    transformfractal (data.ppos, data.pos);
    drawfractal ();
};
dops = function () {
    local s;

    s = ['x' = 8 * 300; 'y' = 10.5 * 300;];
    canvas = createwidget (-1, ['type' = 'ps'; 'size' = s;]);
    setwidgetattr (canvas, ['window' = wrect;]);
    drawfractal ();
    destroywidget (canvas);
    canvas=defcanvas;
};
transformfractal (['x' = 0; 'y' = 0;], ['x' = 0; 'y' = 0;]);
drawfractal ();
lefty/fractal2.lefty000064400000013167151705127100010441 0ustar00#
# data structures
#
length = 300;
center = ['x' = 200; 'y' = 250;];
radius = 2 * length / sqrt (12);
fractalangle = 0;
maxlevel = 2;
sizes = [
    'button' = [ 'x' = 100; 'y' = 40;  ];
    'canvas' = [ 'x' = 400; 'y' = 500; ];
    'view'   = [ 'x' = 400; 'y' = 600; ];
];
sq = function (x) {
    return x * x;
};
#
# create view and other widgets
#
init = function () {
    view = createwidget (-1, [
        'type' = 'view'; 'name' = 'fractal'; 'size' = sizes.view;
    ]);

    array1 = createwidget (view, [
        'type' = 'array'; 'borderwidth' = 1; 'mode' = 'vertical';
    ]);
    widgets[array1].resize = resize;

    array2 = createwidget (array1, [
        'type' = 'array'; 'borderwidth' = 1; 'mode' = 'horizontal';
    ]);
    widgets[array2].resize = resize;

    array3 = createwidget (array2, [
        'type' = 'array'; 'borderwidth' = 1; 'mode' = 'vertical';
    ]);
    widgets[array3].resize = resize;

    morebutton = createwidget (array3, [
        'type' = 'button'; 'text' = 'more';
    ]);
    widgets[morebutton].pressed = pressed;
    lessbutton = createwidget (array3, [
        'type' = 'button'; 'text' = 'less';
    ]);
    widgets[lessbutton].pressed = pressed;
    setwidgetattr (morebutton, ['size' = sizes.button;]);
    setwidgetattr (lessbutton, ['size' = sizes.button;]);

    atext = createwidget (array2, [
        'type' = 'text'; 'mode' = 'oneline';
    ]);
    widgets[atext].oneline = oneline;
    setwidgetattr (atext, [
        'size' = ['x' = sizes.button.x; 'y' = sizes.button.y * 2;];
    ]);

    scroll = createwidget (array1, ['type' = 'scroll';]);
    canvas = createwidget (scroll, ['type' = 'canvas';]);
    wrect = [0 = ['x' = 0; 'y' = 0;]; 1 = sizes.canvas;];
    setwidgetattr (canvas, ['window' = wrect; 'viewport' = wrect[1];]);
};
#
# drawing functions
#
# draw a Koch curve (a ``snowflake'' fractal)
#
# start with a triangle and keep replacing edges
# with the construct: _/\_
# until the recursion level reaches 'maxlevel'
#
fractal = function (level, length, angle) {
    local nlength, newpenpos;

    if (level >= maxlevel) {
        newpenpos.x = penpos.x + length * cos (angle);
        newpenpos.y = penpos.y + length * sin (angle);
        line (canvas, null, penpos, newpenpos, ['color' = 1;]);
        penpos = newpenpos;
        return;
    }
    nlength = length / 3;
    fractal (level + 1, nlength, angle);
    fractal (level + 1, nlength, angle + 60);
    fractal (level + 1, nlength, angle - 60);
    fractal (level + 1, nlength, angle);
};
redrawfractal = function () {
    clear (canvas);
    setpick (canvas, center, wrect);
    penpos = [
        'x' = center.x + cos (fractalangle + 210) * radius;
        'y' = center.y + sin (fractalangle + 210) * radius;
    ];
    fractal (0, length, fractalangle +  60);
    fractal (0, length, fractalangle -  60);
    fractal (0, length, fractalangle - 180);
    remove ('penpos');
};
#
# editing functions
#
# transform the fractal.
#
# map point 'prevpoint' to point 'currpoint'
# with respect to the center of the fractal.
#
transformfractal = function (prevpoint, currpoint) {
    local prevtan, currtan, prevradius, currradius;

    prevtan = atan (prevpoint.y - center.y, prevpoint.x - center.x);
    currtan = atan (currpoint.y - center.y, currpoint.x - center.x);
    fractalangle = fractalangle + (currtan - prevtan);
    prevradius = sqrt (
        sq (prevpoint.y - center.y) + sq (prevpoint.x - center.x)
    );
    currradius = sqrt (
        sq (currpoint.y - center.y) + sq (currpoint.x - center.x)
    );
    radius = radius / prevradius * currradius;
    length = radius / 2 * sqrt (12);
};
#
# main actions
#
redraw = function (data) {
    redrawfractal ();
};
changemaxlevel = function (dn) {
    maxlevel = maxlevel + dn;
    if (maxlevel < 0)
        maxlevel = 0;
    redrawfractal ();
};
resize = function (data) {
    local ret;
    if (data.widget == array1) {
        ret = [
            array2 = [
                'x' = data.size.x;
                'y' = sizes.button.y * 2;
            ];
            scroll = [
                'x' = data.size.x;
                'y' = data.size.y - sizes.button.y * 2;
            ];
        ];
    } else if (data.widget == array2) {
        ret = [
            array3 = [
                'x' = sizes.button.x;
                'y' = 2 * sizes.button.y;
            ];
            atext = [
                'x' = data.size.x - sizes.button.x;
                'y' = 2 * sizes.button.y;
            ];
        ];
    } else if (data.widget == array3) {
        ret = [
            morebutton = sizes.button;
            lessbutton = sizes.button;
        ];
    }
    return ret;
};
#
# user interface functions
#
# bind changes to the fractal to user actions
#
leftup = function (data) {
    transformfractal (data.ppos, data.pos);
    redrawfractal ();
};
menu = [
    0 = 'more';
    1 = 'less';
];
domenu = function (i) {
    local s;
    s = menu[i];
    if (s == 'more')
        changemaxlevel (1);
    else if (s == 'less')
        changemaxlevel (-1);
};
rightdown = function (data) {
    domenu (displaymenu (canvas, menu));
};
pressed = function (data) {
    if (data.widget == morebutton)
        changemaxlevel (1);
    else if (data.widget == lessbutton)
        changemaxlevel (-1);
};
oneline = function (data) {
    local dn;
    dn = ston (data.text);
    if (dn > 0 | dn < 0)
        changemaxlevel (dn - maxlevel);
};
#
# postscript generation
#
dops = function () {
    local r;

    r = [0 = ['x' = 0; 'y' = 0;]; 1 = ['x' = 8 * 300; 'y' = 10.5 * 300;];];
    canvas = opencanvas ('pscanvas', '', r);
    setwidgetattr (canvas, ['window' = wrect;]);
    redraw ();
    closecanvas (canvas);
    canvas=defcanvas;
};
init ();
#txtview ('off');
lefty/lefty.psp000064400000004567151705127100007551 0ustar00/BOX {
    /boxy1 exch def /boxx1 exch def /boxy0 exch def /boxx0 exch def
    boxx0 boxy0 moveto boxx1 boxy0 lineto
    boxx1 boxy1 lineto boxx0 boxy1 lineto
    closepath
} def
/SCP { stroke initclip newpath BOX clip newpath } def
/CL { stroke setrgbcolor } def
/DO { stroke } def
/NP { newpath } def
/FI { fill } def
/LI { moveto lineto } def
/CT { curveto } def
/AR {
    /ang2 exch def /ang1 exch def
    /radius exch def /y2x exch def /cy exch def /cx exch def
    gsave
        cx cy translate 1 y2x scale 0 0 radius ang1 ang2 arc stroke
    grestore
} def
/ARF {
    /ang2 exch def /ang1 exch def
    /radius exch def /y2x exch def /cy exch def /cx exch def
    gsave
        cx cy translate 1 y2x scale 0 0 radius ang1 ang2 arc fill
    grestore
} def
/TXT {
    /texth exch def
    /textf exch def
    /textn exch def
    /texts exch def
    /textyj exch def /texty exch def
    /textxj exch def /textx exch def
    textf findfont texth scalefont dup setfont
    /FontBBox get 1 get 1000 div texth mul /textbl exch def
    /textth texth textn mul def /texttw 0 def
    0 1 textn 1 sub {
        texts exch get 0 get stringwidth pop
        dup texttw gt { /texttw exch def } { pop } ifelse
    } for
    textyj (b) eq { /ty texty textth add textbl add def } if
    textyj (d) eq { /ty texty textth add def } if
    textyj (c) eq { /ty texty textth 2 div add def } if
    textyj (u) eq { /ty texty def } if
    /ty ty textbl sub def
    textxj (l) eq { /tx textx def } if
    textxj (c) eq { /tx textx texttw 2 div sub def } if
    textxj (r) eq { /tx textx texttw sub def } if
    0 1 textn 1 sub {
        /ty ty texth sub def
        texts exch get dup 0 get /ts exch def 1 get /tj exch def
        tj (l) eq { tx ty moveto ts show } if
        tj (n) eq {
            tx texttw ts stringwidth pop sub 2 div add ty moveto ts show
        } if
        tj (r) eq {
            tx texttw ts stringwidth pop sub add ty moveto ts show
        } if
    } for
} def

/colorimage where {
    pop
} {
    /bwproc {
        rgbproc dup length 3 idiv string 0 3 0 5 -1 roll {
            add 2 1 roll 1 sub dup 0 eq {
                pop 3 idiv 3 -1 roll dup 4 -1 roll
                dup 3 1 roll 5 -1 roll put 1 add 3 0
            } {
                2 1 roll
            } ifelse
        } forall
        pop pop pop
    } def
    /colorimage {
        pop pop /rgbproc exch def {bwproc} image
    } bind def
} ifelse
lefty/slides.lefty000064400000006176151705127100010230 0ustar00load ('def.lefty');
definit ();
#
# initialize window data
#
canvas = defcanvas;
wrect = [0 = ['x' = 0; 'y' = 0;]; 1 = ['x' = 800; 'y' = 1000;];];
lmargin = 100;
width = 800;
height = 1000;
setwidgetattr (canvas, ['window' = wrect;]);
fonts = [
    'timr' = [
        14 = 'timr14';
        18 = 'timr18';
        24 = 'timr24';
    ];
    'courr' = [
        14 = 'courr14';
        18 = 'courr18';
        24 = 'courr24';
    ];
];
x2ps = [
    'timr24' = 'Times-Roman';
    'timr18' = 'Times-Roman';
    'timr14' = 'Times-Roman';
    'courr24' = 'Courier';
    'courr18' = 'Courier';
    'courr14' = 'Courier';
];

calc = function () {
    local i, j, cpos, tsiz, dist, slidep;
    tsiz = ['x' = 0; 'y' = 0;];
    for (i = 0; slides[i]; i = i + 1) {
        slidep = slides[i];
        if (slidep.skip) {
            tsiz.y = tsiz.y + slidep.skip;
        } else {
            for (j = 0; slidep.text[j]; j = j + 1) {
                if (j > 0)
                    tsiz.y = tsiz.y + 5;
                if (~slidep.font)
                    slidep.font = slides.font;
                if (~slidep.size)
                    slidep.size = slides.size;
                if (~slidep.just)
                    slidep.just = slides.just;
                slidep.tsiz[j] = textsize (canvas, slidep.text[j],
                        fonts[slidep.font][slidep.size], 0);
                tsiz.y = tsiz.y + slidep.tsiz[j].y;
            }
        }
    }
    dist = (height - tsiz.y) / (i + 1);
    cpos = ['x' = lmargin; 'y' = height - dist;];
    for (i = 0; slides[i]; i = i + 1) {
        slidep = slides[i];
        if (slidep.skip) {
            cpos.y = cpos.y - slidep.skip - dist;
        } else {
            for (j = 0; slidep.text[j]; j = j + 1) {
                if (j > 0)
                    cpos.y = cpos.y - 5;
                if (slidep.just == 0)
                    slidep.tpos[j].x = cpos.x;
                else
                    slidep.tpos[j].x = width / 2;
                cpos.y = cpos.y - slidep.tsiz[j].y;
                slidep.tpos[j].y = cpos.y;
            }
            cpos.y = cpos.y - dist;
        }
    }
};
redraw = function () {
    local i, j, slidep;
    for (i = 0; slides[i]; i = i + 1) {
        slidep = slides[i];
        if (~slidep.skip)
            for (j = 0; slidep.text[j]; j = j + 1)
                text (canvas, slidep, slidep.tpos[j], slidep.text[j],
                        fonts[slidep.font][slidep.size], 0, slidep.just);
    }
};
dops = function () {
    local i, j, slidep, r;

    r = [0 = ['x' = 0; 'y' = 0;]; 1 = ['x' = 8 * 300; 'y' = 10.5 * 300;];];
    canvas = createwidget (-1, ['type' = 'ps'; 'origin' = r[0]; 'size' = r[1]
;]);
    setwidgetattr (canvas, ['window' = wrect;]);
    for (i = 0; slides[i]; i = i + 1) {
        slidep = slides[i];
        if (~slidep.skip)
            for (j = 0; slidep.text[j]; j = j + 1)
                text (canvas, slidep, slidep.tpos[j], slidep.text[j],
                        x2ps[fonts[slidep.font][slidep.size]],
                        slidep.tsiz[j].y, slidep.just, 1);
    }
    destroywidget (canvas);
    canvas=defcanvas;
};
doit = function () {
    calc ();
    redraw ();
    dops ();
};
lefty/tree.lefty000064400000011626151705127100007700 0ustar00load ('def.lefty');
definit ();
#
# initialize window data
#
canvas = defcanvas;
wrect = [0 = ['x' = -5; 'y' = 0;]; 1 = ['x' = 410; 'y' = 500;];];
setwidgetattr (canvas, ['window' = wrect;]);
#
# data structures
#
nodearray = [];
nodenum = 0;
dist = ['x' = 40; 'y' = 40;];
defsize = ['x' = 10; 'y' = 10;];
fontname = 'fixed';
fontsize = 18;
tree = null;

# drawing functions
#
boxnode = function (node) {
    local center;
    box (canvas, node, node.rect, ['color' = 0; 'fill' = 'on';]);
    box (canvas, node, node.rect);
    center = [
        'x' = (node.rect[0].x + node.rect[1].x) / 2;
        'y' = (node.rect[0].y + node.rect[1].y) / 2;
    ];
    if (node.name)
        text (canvas, node, center, node.name, fontname, fontsize, 'cc');
};
circlenode = function (node) {
    local center, radius;
    center = [
        'x' = (node.rect[0].x + node.rect[1].x) / 2;
        'y' = (node.rect[0].y + node.rect[1].y) / 2;
    ];
    radius = [
        'x' = center.x - node.rect[0].x;
        'y' = center.y - node.rect[0].y;
    ];
    arc (canvas, node, center, radius, ['color' = 0; 'fill' = 'on';]);
    arc (canvas, node, center, radius);
    if (node.name)
        text (canvas, node, center, node.name, fontname, fontsize, 'cc');
};
drawnode = boxnode;
drawedge = function (node1, node2) {
    line (canvas, null,
            [
                'x' = (node1.rect[1].x + node1.rect[0].x) / 2;
                'y' = node1.rect[0].y;
            ], [
                'x' = (node2.rect[1].x + node2.rect[0].x) / 2;
                'y' = node2.rect[1].y;
            ]);
};
drawtree = function (node) {
    local i;
    for (i in nodearray)
        drawnode (nodearray[i]);
    drawtreerec (node);
};
drawtreerec = function (node) {
    local i, n;
    if ((n = tablesize (node.ch)) > 0) {
        for (i = 0; i < n; i = i + 1) {
            drawedge (node, node.ch[i]);
            drawtreerec (node.ch[i]);
        }
    }
};
redraw = function (c) {
    if (tree)
        drawtree (tree);
};

# layout functions
#
complayout = function () {
    leafx = 0;
    leafrank = 0;
    dolayout (tree, wrect[1].y - 10);
    remove ('leafx');
    remove ('leafrank');
};
dolayout = function (node, pary) {
    local r, n, i, size, lchp, rchp;
    size = nodesize (node);
    if (node.chn > 0) {
        for (i = 0; i < node.chn; i = i + 1)
            dolayout (node.ch[i], pary - size.y - dist.y);
        node.rank = (node.ch[0].rank + node.ch[node.chn - 1].rank) / 2;
        lchp = node.ch[0].rect;
        rchp = node.ch[node.chn - 1].rect;
        r[0].x = lchp[0].x + ((rchp[1].x - lchp[0].x) - size.x) / 2;
        r[0].y = pary - size.y;
        r[1].x = r[0].x + size.x;
        r[1].y = pary;
        node.rect = r;
    } else {
        node.rank = leafrank;
        r[0].x = leafx;
        r[0].y = pary - size.y;
        r[1].x = r[0].x + size.x;
        r[1].y = pary;
        leafrank = leafrank + 1;
        leafx = r[1].x + dist.x;
        node.rect = r;
    }
};

# editing functions
#
inode = function (point, name) {
    local i, nnum, size;
    nnum = nodenum;
    if (~name)
        name = ask ('give name of node:');
    nodearray[nnum].ch = [];
    nodearray[nnum].chn = 0;
    nodearray[nnum].name = name;
    size = nodesize (nodearray[nnum]);
    nodearray[nnum].rect[0] = point;
    nodearray[nnum].rect[1] = ['x' = point.x + size.x; 'y' = point.y + size.y;];
    nodenum = nodenum + 1;
    if (~tree) {
        tree = nodearray[nnum];
        tree.depth = 0;
        complayout ();
        drawtree (tree);
    } else
        drawtree (nodearray[nnum]);
    return nodearray[nnum];
};
iedge = function (node1, node2) {
    node1.ch[node1.chn] = node2;
    node1.chn = node1.chn + 1;
    node2.depth = node1.depth + 1;
    complayout ();
    clear (canvas);
    drawtree (tree);
};
fix = function (node, op, np) {
    if (node.depth ~= 0)
        dist.y = dist.y + (op.y - np.y) / node.depth;
    if (node.rank ~= 0)
        dist.x = dist.x + (np.x - op.x) / node.rank;
    complayout ();
    clear (canvas);
    drawtree (tree);
};
nodesize = function (node) {
    local siz;
    if (~(siz = textsize (canvas, node.name, fontname, fontsize)))
        siz = defsize;
    else {
        siz.x = siz.x + 8;
        siz.y = siz.y + 8;
    }
    return siz;
};
changenode = function (nodestyle) {
    drawnode = nodestyle;
    clear (canvas);
    drawtree (tree);
};

# user interface functions
#
leftdown = function (data) {
    if (~data.obj)
        inode (data.pos, null);
};
leftup = function (data) {
    if (data.pobj)
        fix (data.pobj, data.ppos, data.pos);
};
middleup = function (data) {
    if (data.pobj & data.obj)
        iedge (data.pobj, data.obj);
};
dops = function () {
    local s;

    s = ['x' = 8 * 300; 'y' = 10.5 * 300;];
    fontname = 'Times-Roman';
    canvas = createwidget (-1, ['type' = 'ps'; 'size' = s;]);
    setwidgetattr (canvas, ['window' = wrect;]);
    drawtree (tree);
    destroywidget (canvas);
    canvas=defcanvas;
    fontname = 'fixed';
};