Graphviz  2.39.20141221.0545
position.c
Go to the documentation of this file.
1 /* $Id$ $Revision$ */
2 /* vim:set shiftwidth=4 ts=8: */
3 
4 /*************************************************************************
5  * Copyright (c) 2011 AT&T Intellectual Property
6  * All rights reserved. This program and the accompanying materials
7  * are made available under the terms of the Eclipse Public License v1.0
8  * which accompanies this distribution, and is available at
9  * http://www.eclipse.org/legal/epl-v10.html
10  *
11  * Contributors: See CVS logs. Details at http://www.graphviz.org/
12  *************************************************************************/
13 
14 
15 /*
16  * position(g): set ND_coord(n) (x and y) for all nodes n of g, using GD_rank(g).
17  * (the graph may be modified by merging certain edges with a common endpoint.)
18  * the coordinates are computed by constructing and ranking an auxiliary graph.
19  * then leaf nodes are inserted in the fast graph. cluster boundary nodes are
20  * created and correctly separated.
21  */
22 
23 #include "dot.h"
24 #include "aspect.h"
25 
26 static int nsiter2(graph_t * g);
27 static void create_aux_edges(graph_t * g);
28 static void remove_aux_edges(graph_t * g);
29 static void set_xcoords(graph_t * g);
30 static void set_ycoords(graph_t * g);
31 static void set_aspect(graph_t * g, aspect_t* );
32 static void expand_leaves(graph_t * g);
33 static void make_lrvn(graph_t * g);
34 static void contain_nodes(graph_t * g);
35 static boolean idealsize(graph_t * g, double);
36 
37 #ifdef DEBUG
38 static void
39 dumpNS (graph_t * g)
40 {
41  node_t* n = GD_nlist(g);
42  elist el;
43  edge_t* e;
44  int i;
45 
46  while (n) {
47  el = ND_out(n);
48  for (i = 0; i < el.size; i++) {
49  e = el.list[i];
50  fprintf (stderr, "%s(%x) -> ", agnameof(agtail(e)),agtail(e));
51  fprintf (stderr, "%s(%x) : %d\n", agnameof(aghead(e)), aghead(e),
52  ED_minlen(e));
53  }
54  n = ND_next(n);
55  }
56 }
57 #endif
58 
59 static double
60 largeMinlen (double l)
61 {
62  agerr (AGERR, "Edge length %f larger than maximum %u allowed.\nCheck for overwide node(s).\n", l, USHRT_MAX);
63  return (double)USHRT_MAX;
64 }
65 
66 /* connectGraph:
67  * When source and/or sink nodes are defined, it is possible that
68  * after the auxiliary edges are added, the graph may still have 2 or
69  * 3 components. To fix this, we put trivial constraints connecting the
70  * first items of each rank.
71  */
72 static void
73 connectGraph (graph_t* g)
74 {
75  int i, j, r, found;
76  node_t* tp;
77  node_t* hp;
78  node_t* sn;
79  edge_t* e;
80  rank_t* rp;
81 
82  for (r = GD_minrank(g); r <= GD_maxrank(g); r++) {
83  rp = GD_rank(g)+r;
84  found =FALSE;
85  tp = NULL;
86  for (i = 0; i < rp->n; i++) {
87  tp = rp->v[i];
88  if (ND_save_out(tp).list) {
89  for (j = 0; (e = ND_save_out(tp).list[j]); j++) {
90  if ((ND_rank(aghead(e)) > r) || (ND_rank(agtail(e)) > r)) {
91  found = TRUE;
92  break;
93  }
94  }
95  if (found) break;
96  }
97  if (ND_save_in(tp).list) {
98  for (j = 0; (e = ND_save_in(tp).list[j]); j++) {
99  if ((ND_rank(agtail(e)) > r) || (ND_rank(aghead(e)) > r)) {
100  found = TRUE;
101  break;
102  }
103  }
104  if (found) break;
105  }
106  }
107  if (found || !tp) continue;
108  tp = rp->v[0];
109  if (r < GD_maxrank(g)) hp = (rp+1)->v[0];
110  else hp = (rp-1)->v[0];
111  assert (hp);
112  sn = virtual_node(g);
113  ND_node_type(sn) = SLACKNODE;
114  make_aux_edge(sn, tp, 0, 0);
115  make_aux_edge(sn, hp, 0, 0);
116  ND_rank(sn) = MIN(ND_rank(tp), ND_rank(hp));
117  }
118 }
119 
121 {
122  if (GD_nlist(g) == NULL)
123  return; /* ignore empty graph */
124  mark_lowclusters(g); /* we could remove from splines.c now */
125  set_ycoords(g);
126  if (Concentrate)
127  dot_concentrate(g);
128  expand_leaves(g);
129  if (flat_edges(g))
130  set_ycoords(g);
131  create_aux_edges(g);
132  if (rank(g, 2, nsiter2(g))) { /* LR balance == 2 */
133  connectGraph (g);
134  assert(rank(g, 2, nsiter2(g)) == 0);
135  }
136  set_xcoords(g);
137  set_aspect(g, asp);
138  remove_aux_edges(g); /* must come after set_aspect since we now
139  * use GD_ln and GD_rn for bbox width.
140  */
141 }
142 
143 static int nsiter2(graph_t * g)
144 {
145  int maxiter = INT_MAX;
146  char *s;
147 
148  if ((s = agget(g, "nslimit")))
149  maxiter = atof(s) * agnnodes(g);
150  return maxiter;
151 }
152 
153 static int go(node_t * u, node_t * v)
154 {
155  int i;
156  edge_t *e;
157 
158  if (u == v)
159  return TRUE;
160  for (i = 0; (e = ND_out(u).list[i]); i++) {
161  if (go(aghead(e), v))
162  return TRUE;
163  }
164  return FALSE;
165 }
166 
167 static int canreach(node_t * u, node_t * v)
168 {
169  return go(u, v);
170 }
171 
172 edge_t *make_aux_edge(node_t * u, node_t * v, double len, int wt)
173 {
174  edge_t *e;
175 
177  AGTYPE(&(e2->in)) = AGINEDGE;
178  AGTYPE(&(e2->out)) = AGOUTEDGE;
179  e2->out.base.data = (Agrec_t*)NEW(Agedgeinfo_t);
180  e = &(e2->out);
181 
182  agtail(e) = u;
183  aghead(e) = v;
184  if (len > USHRT_MAX)
185  len = largeMinlen (len);
186  ED_minlen(e) = ROUND(len);
187  ED_weight(e) = wt;
188  fast_edge(e);
189  return e;
190 }
191 
192 static void allocate_aux_edges(graph_t * g)
193 {
194  int i, j, n_in;
195  node_t *n;
196 
197  /* allocate space for aux edge lists */
198  for (n = GD_nlist(g); n; n = ND_next(n)) {
199  ND_save_in(n) = ND_in(n);
200  ND_save_out(n) = ND_out(n);
201  for (i = 0; ND_out(n).list[i]; i++);
202  for (j = 0; ND_in(n).list[j]; j++);
203  n_in = i + j;
204  alloc_elist(n_in + 3, ND_in(n));
205  alloc_elist(3, ND_out(n));
206  }
207 }
208 
209 /* make_LR_constraints:
210  */
211 static void
212 make_LR_constraints(graph_t * g)
213 {
214  int i, j, k;
215  int sw; /* self width */
216  int m0, m1;
217  double width;
218  int sep[2];
219  int nodesep; /* separation between nodes on same rank */
220  edge_t *e, *e0, *e1, *ff;
221  node_t *u, *v, *t0, *h0;
222  rank_t *rank = GD_rank(g);
223 
224  /* Use smaller separation on odd ranks if g has edge labels */
225  if (GD_has_labels(g->root) & EDGE_LABEL) {
226  sep[0] = GD_nodesep(g);
227  sep[1] = 5;
228  }
229  else {
230  sep[1] = sep[0] = GD_nodesep(g);
231  }
232  /* make edges to constrain left-to-right ordering */
233  for (i = GD_minrank(g); i <= GD_maxrank(g); i++) {
234  double last;
235  last = ND_rank(rank[i].v[0]) = 0;
236  nodesep = sep[i & 1];
237  for (j = 0; j < rank[i].n; j++) {
238  u = rank[i].v[j];
239  ND_mval(u) = ND_rw(u); /* keep it somewhere safe */
240  if (ND_other(u).size > 0) { /* compute self size */
241  /* FIX: dot assumes all self-edges go to the right. This
242  * is no longer true, though makeSelfEdge still attempts to
243  * put as many as reasonable on the right. The dot code
244  * should be modified to allow a box reflecting the placement
245  * of all self-edges, and use that to reposition the nodes.
246  * Note that this would not only affect left and right
247  * positioning but may also affect interrank spacing.
248  */
249  sw = 0;
250  for (k = 0; (e = ND_other(u).list[k]); k++) {
251  if (agtail(e) == aghead(e)) {
252  sw += selfRightSpace (e);
253  }
254  }
255  ND_rw(u) += sw; /* increment to include self edges */
256  }
257  v = rank[i].v[j + 1];
258  if (v) {
259  width = ND_rw(u) + ND_lw(v) + nodesep;
260  e0 = make_aux_edge(u, v, width, 0);
261  last = (ND_rank(v) = last + width);
262  }
263 
264  /* constraints from labels of flat edges on previous rank */
265  if ((e = (edge_t*)ND_alg(u))) {
266  e0 = ND_save_out(u).list[0];
267  e1 = ND_save_out(u).list[1];
268  if (ND_order(aghead(e0)) > ND_order(aghead(e1))) {
269  ff = e0;
270  e0 = e1;
271  e1 = ff;
272  }
273  m0 = (ED_minlen(e) * GD_nodesep(g)) / 2;
274  m1 = m0 + ND_rw(aghead(e0)) + ND_lw(agtail(e0));
275  /* these guards are needed because the flat edges
276  * work very poorly with cluster layout */
277  if (canreach(agtail(e0), aghead(e0)) == FALSE)
278  make_aux_edge(aghead(e0), agtail(e0), m1,
279  ED_weight(e));
280  m1 = m0 + ND_rw(agtail(e1)) + ND_lw(aghead(e1));
281  if (canreach(aghead(e1), agtail(e1)) == FALSE)
282  make_aux_edge(agtail(e1), aghead(e1), m1,
283  ED_weight(e));
284  }
285 
286  /* position flat edge endpoints */
287  for (k = 0; k < ND_flat_out(u).size; k++) {
288  e = ND_flat_out(u).list[k];
289  if (ND_order(agtail(e)) < ND_order(aghead(e))) {
290  t0 = agtail(e);
291  h0 = aghead(e);
292  } else {
293  t0 = aghead(e);
294  h0 = agtail(e);
295  }
296 
297  width = ND_rw(t0) + ND_lw(h0);
298  m0 = ED_minlen(e) * GD_nodesep(g) + width;
299 
300  if ((e0 = find_fast_edge(t0, h0))) {
301  /* flat edge between adjacent neighbors
302  * ED_dist contains the largest label width.
303  */
304  m0 = MAX(m0, width + GD_nodesep(g) + ROUND(ED_dist(e)));
305  if (m0 > USHRT_MAX)
306  m0 = largeMinlen (m0);
307  ED_minlen(e0) = MAX(ED_minlen(e0), m0);
308  ED_weight(e0) = MAX(ED_weight(e0), ED_weight(e));
309  }
310  else if (!ED_label(e)) {
311  /* unlabeled flat edge between non-neighbors
312  * ED_minlen(e) is max of ED_minlen of all equivalent
313  * edges.
314  */
315  make_aux_edge(t0, h0, m0, ED_weight(e));
316  }
317  /* labeled flat edges between non-neighbors have already
318  * been constrained by the label above.
319  */
320  }
321  }
322  }
323 }
324 
325 /* make_edge_pairs: make virtual edge pairs corresponding to input edges */
326 static void make_edge_pairs(graph_t * g)
327 {
328  int i, m0, m1;
329  node_t *n, *sn;
330  edge_t *e;
331 
332  for (n = GD_nlist(g); n; n = ND_next(n)) {
333  if (ND_save_out(n).list)
334  for (i = 0; (e = ND_save_out(n).list[i]); i++) {
335  sn = virtual_node(g);
336  ND_node_type(sn) = SLACKNODE;
337  m0 = (ED_head_port(e).p.x - ED_tail_port(e).p.x);
338  if (m0 > 0)
339  m1 = 0;
340  else {
341  m1 = -m0;
342  m0 = 0;
343  }
344 #ifdef NOTDEF
345 /* was trying to improve LR balance */
346  if ((ND_save_out(n).size % 2 == 0)
347  && (i == ND_save_out(n).size / 2 - 1)) {
348  node_t *u = ND_save_out(n).list[i]->head;
349  node_t *v = ND_save_out(n).list[i + 1]->head;
350  double width = ND_rw(u) + ND_lw(v) + GD_nodesep(g);
351  m0 = width / 2 - 1;
352  }
353 #endif
354  make_aux_edge(sn, agtail(e), m0 + 1, ED_weight(e));
355  make_aux_edge(sn, aghead(e), m1 + 1, ED_weight(e));
356  ND_rank(sn) =
357  MIN(ND_rank(agtail(e)) - m0 - 1,
358  ND_rank(aghead(e)) - m1 - 1);
359  }
360  }
361 }
362 
363 static void contain_clustnodes(graph_t * g)
364 {
365  int c;
366  edge_t *e;
367 
368  if (g != dot_root(g)) {
369  contain_nodes(g);
370  if ((e = find_fast_edge(GD_ln(g),GD_rn(g)))) /* maybe from lrvn()?*/
371  ED_weight(e) += 128;
372  else
373  make_aux_edge(GD_ln(g), GD_rn(g), 1, 128); /* clust compaction edge */
374  }
375  for (c = 1; c <= GD_n_cluster(g); c++)
376  contain_clustnodes(GD_clust(g)[c]);
377 }
378 
379 static int vnode_not_related_to(graph_t * g, node_t * v)
380 {
381  edge_t *e;
382 
383  if (ND_node_type(v) != VIRTUAL)
384  return FALSE;
385  for (e = ND_save_out(v).list[0]; ED_to_orig(e); e = ED_to_orig(e));
386  if (agcontains(g, agtail(e)))
387  return FALSE;
388  if (agcontains(g, aghead(e)))
389  return FALSE;
390  return TRUE;
391 }
392 
393 /* keepout_othernodes:
394  * Guarantee nodes outside the cluster g are placed outside of it.
395  * This is done by adding constraints to make sure such nodes have
396  * a gap of margin from the left or right bounding box node ln or rn.
397  *
398  * We could probably reduce some of these constraints by checking if
399  * the node is in a cluster, since elsewhere we make constrain a
400  * separate between clusters. Also, we should be able to skip the
401  * first loop if g is the root graph.
402  */
403 static void keepout_othernodes(graph_t * g)
404 {
405  int i, c, r, margin;
406  node_t *u, *v;
407 
408  margin = late_int (g, G_margin, CL_OFFSET, 0);
409  for (r = GD_minrank(g); r <= GD_maxrank(g); r++) {
410  if (GD_rank(g)[r].n == 0)
411  continue;
412  v = GD_rank(g)[r].v[0];
413  if (v == NULL)
414  continue;
415  for (i = ND_order(v) - 1; i >= 0; i--) {
416  u = GD_rank(dot_root(g))[r].v[i];
417  /* can't use "is_a_vnode_of" because elists are swapped */
418  if ((ND_node_type(u) == NORMAL) || vnode_not_related_to(g, u)) {
419  make_aux_edge(u, GD_ln(g), margin + ND_rw(u), 0);
420  break;
421  }
422  }
423  for (i = ND_order(v) + GD_rank(g)[r].n; i < GD_rank(dot_root(g))[r].n;
424  i++) {
425  u = GD_rank(dot_root(g))[r].v[i];
426  if ((ND_node_type(u) == NORMAL) || vnode_not_related_to(g, u)) {
427  make_aux_edge(GD_rn(g), u, margin + ND_lw(u), 0);
428  break;
429  }
430  }
431  }
432 
433  for (c = 1; c <= GD_n_cluster(g); c++)
434  keepout_othernodes(GD_clust(g)[c]);
435 }
436 
437 /* contain_subclust:
438  * Make sure boxes of subclusters of g are offset from the
439  * box of g. This is done by a constraint between the left and
440  * right bounding box nodes ln and rn of g and a subcluster.
441  * The gap needs to include any left or right labels.
442  */
443 static void contain_subclust(graph_t * g)
444 {
445  int margin, c;
446  graph_t *subg;
447 
448  margin = late_int (g, G_margin, CL_OFFSET, 0);
449  make_lrvn(g);
450  for (c = 1; c <= GD_n_cluster(g); c++) {
451  subg = GD_clust(g)[c];
452  make_lrvn(subg);
453  make_aux_edge(GD_ln(g), GD_ln(subg),
454  margin + GD_border(g)[LEFT_IX].x, 0);
455  make_aux_edge(GD_rn(subg), GD_rn(g),
456  margin + GD_border(g)[RIGHT_IX].x, 0);
457  contain_subclust(subg);
458  }
459 }
460 
461 /* separate_subclust:
462  * Guarantee space between subcluster of g.
463  * This is done by adding a constraint between the right bbox node rn
464  * of the left cluster and the left bbox node ln of the right cluster.
465  * This is only done if the two clusters overlap in some rank.
466  */
467 static void separate_subclust(graph_t * g)
468 {
469  int i, j, margin;
470  graph_t *low, *high;
471  graph_t *left, *right;
472 
473  margin = late_int (g, G_margin, CL_OFFSET, 0);
474  for (i = 1; i <= GD_n_cluster(g); i++)
475  make_lrvn(GD_clust(g)[i]);
476  for (i = 1; i <= GD_n_cluster(g); i++) {
477  for (j = i + 1; j <= GD_n_cluster(g); j++) {
478  low = GD_clust(g)[i];
479  high = GD_clust(g)[j];
480  if (GD_minrank(low) > GD_minrank(high)) {
481  graph_t *temp = low;
482  low = high;
483  high = temp;
484  }
485  if (GD_maxrank(low) < GD_minrank(high))
486  continue;
487  if (ND_order(GD_rank(low)[GD_minrank(high)].v[0])
488  < ND_order(GD_rank(high)[GD_minrank(high)].v[0])) {
489  left = low;
490  right = high;
491  } else {
492  left = high;
493  right = low;
494  }
495  make_aux_edge(GD_rn(left), GD_ln(right), margin, 0);
496  }
497  separate_subclust(GD_clust(g)[i]);
498  }
499 }
500 
501 /* pos_clusters: create constraints for:
502  * node containment in clusters,
503  * cluster containment in clusters,
504  * separation of sibling clusters.
505  */
506 static void pos_clusters(graph_t * g)
507 {
508  if (GD_n_cluster(g) > 0) {
509  contain_clustnodes(g);
510  keepout_othernodes(g);
511  contain_subclust(g);
512  separate_subclust(g);
513  }
514 }
515 
516 static void compress_graph(graph_t * g)
517 {
518  double x;
519  pointf p;
520 
521  if (GD_drawing(g)->ratio_kind != R_COMPRESS)
522  return;
523  p = GD_drawing(g)->size;
524  if (p.x * p.y <= 1)
525  return;
526  contain_nodes(g);
527  if (GD_flip(g) == FALSE)
528  x = p.x;
529  else
530  x = p.y;
531 
532  /* Guard against huge size attribute since max. edge length is USHRT_MAX
533  * A warning might be called for. Also, one could check that the graph
534  * already fits GD_drawing(g)->size and return immediately.
535  */
536  x = MIN(x,USHRT_MAX);
537  make_aux_edge(GD_ln(g), GD_rn(g), x, 1000);
538 }
539 
540 static void create_aux_edges(graph_t * g)
541 {
542  allocate_aux_edges(g);
543  make_LR_constraints(g);
544  make_edge_pairs(g);
545  pos_clusters(g);
546  compress_graph(g);
547 }
548 
549 static void remove_aux_edges(graph_t * g)
550 {
551  int i;
552  node_t *n, *nnext, *nprev;
553  edge_t *e;
554 
555  for (n = GD_nlist(g); n; n = ND_next(n)) {
556  for (i = 0; (e = ND_out(n).list[i]); i++) {
557  free(e->base.data);
558  free(e);
559  }
560  free_list(ND_out(n));
561  free_list(ND_in(n));
562  ND_out(n) = ND_save_out(n);
563  ND_in(n) = ND_save_in(n);
564  }
565  /* cannot be merged with previous loop */
566  nprev = NULL;
567  for (n = GD_nlist(g); n; n = nnext) {
568  nnext = ND_next(n);
569  if (ND_node_type(n) == SLACKNODE) {
570  if (nprev)
571  ND_next(nprev) = nnext;
572  else
573  GD_nlist(g) = nnext;
574  free(n->base.data);
575  free(n);
576  } else
577  nprev = n;
578  }
579  ND_prev(GD_nlist(g)) = NULL;
580 }
581 
582 /* set_xcoords:
583  * Set x coords of nodes.
584  */
585 static void
586 set_xcoords(graph_t * g)
587 {
588  int i, j;
589  node_t *v;
590  rank_t *rank = GD_rank(g);
591 
592  for (i = GD_minrank(g); i <= GD_maxrank(g); i++) {
593  for (j = 0; j < rank[i].n; j++) {
594  v = rank[i].v[j];
595  ND_coord(v).x = ND_rank(v);
596  ND_rank(v) = i;
597  }
598  }
599 }
600 
601 /* adjustSimple:
602  * Expand cluster height by delta, adding half to top
603  * and half to bottom. If the bottom expansion exceeds the
604  * ht1 of the rank, shift the ranks in the cluster up.
605  * If the top expansion, including any shift from the bottom
606  * expansion, exceeds to ht2 of the rank, shift the ranks above
607  * the cluster up.
608  *
609  * FIX: There can be excess space between ranks. Not sure where this is
610  * coming from but it could be cleaned up.
611  */
612 static void adjustSimple(graph_t * g, int delta, int margin_total)
613 {
614  int r, bottom, deltop, delbottom;
615  graph_t *root = dot_root(g);
616  rank_t *rank = GD_rank(root);
617  int maxr = GD_maxrank(g);
618  int minr = GD_minrank(g);
619 
620  bottom = (delta+1) / 2;
621  delbottom = GD_ht1(g) + bottom - (rank[maxr].ht1 - margin_total);
622  if (delbottom > 0) {
623  for (r = maxr; r >= minr; r--) {
624  if (rank[r].n > 0)
625  ND_coord(rank[r].v[0]).y += delbottom;
626  }
627  deltop = GD_ht2(g) + (delta-bottom) + delbottom - (rank[minr].ht2 - margin_total);
628  }
629  else
630  deltop = GD_ht2(g) + (delta-bottom) - (rank[minr].ht2 - margin_total);
631  if (deltop > 0) {
632  for (r = minr-1; r >= GD_minrank(root); r--) {
633  if (rank[r].n > 0)
634  ND_coord(rank[r].v[0]).y += deltop;
635  }
636  }
637  GD_ht2(g) += (delta - bottom);
638  GD_ht1(g) += bottom;
639 }
640 
641 /* adjustRanks:
642  * Recursively adjust ranks to take into account
643  * wide cluster labels when rankdir=LR.
644  * We divide the extra space between the top and bottom.
645  * Adjust the ht1 and ht2 values in the process.
646  */
647 static void adjustRanks(graph_t * g, int margin_total)
648 {
649  double lht; /* label height */
650  double rht; /* height between top and bottom ranks */
651  int maxr, minr, margin;
652  int c;
653  double delta, ht1, ht2;
654 
655  rank_t *rank = GD_rank(dot_root(g));
656  if (g == dot_root(g))
657  margin = 0;
658  else
659  margin = late_int (g, G_margin, CL_OFFSET, 0);
660 
661  ht1 = GD_ht1(g);
662  ht2 = GD_ht2(g);
663 
664  for (c = 1; c <= GD_n_cluster(g); c++) {
665  graph_t *subg = GD_clust(g)[c];
666  adjustRanks(subg, margin+margin_total);
667  if (GD_maxrank(subg) == GD_maxrank(g))
668  ht1 = MAX(ht1, GD_ht1(subg) + margin);
669  if (GD_minrank(subg) == GD_minrank(g))
670  ht2 = MAX(ht2, GD_ht2(subg) + margin);
671  }
672 
673  GD_ht1(g) = ht1;
674  GD_ht2(g) = ht2;
675 
676  if ((g != dot_root(g)) && GD_label(g)) {
677  lht = MAX(GD_border(g)[LEFT_IX].y, GD_border(g)[RIGHT_IX].y);
678  maxr = GD_maxrank(g);
679  minr = GD_minrank(g);
680  rht = ND_coord(rank[minr].v[0]).y - ND_coord(rank[maxr].v[0]).y;
681  delta = lht - (rht + ht1 + ht2);
682  if (delta > 0) {
683  adjustSimple(g, delta, margin_total);
684  }
685  }
686 
687  /* update the global ranks */
688  if (g != dot_root(g)) {
689  rank[GD_minrank(g)].ht2 = MAX(rank[GD_minrank(g)].ht2, GD_ht2(g));
690  rank[GD_maxrank(g)].ht1 = MAX(rank[GD_maxrank(g)].ht1, GD_ht1(g));
691  }
692 }
693 
694 /* clust_ht:
695  * recursively compute cluster ht requirements. assumes GD_ht1(subg) and ht2
696  * are computed from primitive nodes only. updates ht1 and ht2 to reflect
697  * cluster nesting and labels. also maintains global rank ht1 and ht2.
698  * Return true if some cluster has a label.
699  */
700 static int clust_ht(Agraph_t * g)
701 {
702  int c;
703  double ht1, ht2;
704  graph_t *subg;
705  rank_t *rank = GD_rank(dot_root(g));
706  int margin, haveClustLabel = 0;
707 
708  if (g == dot_root(g))
709  margin = CL_OFFSET;
710  else
711  margin = late_int (g, G_margin, CL_OFFSET, 0);
712 
713  ht1 = GD_ht1(g);
714  ht2 = GD_ht2(g);
715 
716  /* account for sub-clusters */
717  for (c = 1; c <= GD_n_cluster(g); c++) {
718  subg = GD_clust(g)[c];
719  haveClustLabel |= clust_ht(subg);
720  if (GD_maxrank(subg) == GD_maxrank(g))
721  ht1 = MAX(ht1, GD_ht1(subg) + margin);
722  if (GD_minrank(subg) == GD_minrank(g))
723  ht2 = MAX(ht2, GD_ht2(subg) + margin);
724  }
725 
726  /* account for a possible cluster label in clusters */
727  /* room for root graph label is handled in dotneato_postprocess */
728  if ((g != dot_root(g)) && GD_label(g)) {
729  haveClustLabel = 1;
730  if (!GD_flip(agroot(g))) {
731  ht1 += GD_border(g)[BOTTOM_IX].y;
732  ht2 += GD_border(g)[TOP_IX].y;
733  }
734  }
735  GD_ht1(g) = ht1;
736  GD_ht2(g) = ht2;
737 
738  /* update the global ranks */
739  if (g != dot_root(g)) {
740  rank[GD_minrank(g)].ht2 = MAX(rank[GD_minrank(g)].ht2, ht2);
741  rank[GD_maxrank(g)].ht1 = MAX(rank[GD_maxrank(g)].ht1, ht1);
742  }
743 
744  return haveClustLabel;
745 }
746 
747 /* set y coordinates of nodes, a rank at a time */
748 static void set_ycoords(graph_t * g)
749 {
750  int i, j, r;
751  double ht2, maxht, delta, d0, d1;
752  node_t *n;
753  edge_t *e;
754  rank_t *rank = GD_rank(g);
755  graph_t *clust;
756  int lbl;
757 
758  ht2 = maxht = 0;
759 
760  /* scan ranks for tallest nodes. */
761  for (r = GD_minrank(g); r <= GD_maxrank(g); r++) {
762  for (i = 0; i < rank[r].n; i++) {
763  n = rank[r].v[i];
764 
765  /* assumes symmetry, ht1 = ht2 */
766  ht2 = ND_ht(n) / 2;
767 
768 
769  /* have to look for high self-edge labels, too */
770  if (ND_other(n).list)
771  for (j = 0; (e = ND_other(n).list[j]); j++) {
772  if (agtail(e) == aghead(e)) {
773  if (ED_label(e))
774  ht2 = MAX(ht2, ED_label(e)->dimen.y / 2);
775  }
776  }
777 
778  /* update global rank ht */
779  if (rank[r].pht2 < ht2)
780  rank[r].pht2 = rank[r].ht2 = ht2;
781  if (rank[r].pht1 < ht2)
782  rank[r].pht1 = rank[r].ht1 = ht2;
783 
784  /* update nearest enclosing cluster rank ht */
785  if ((clust = ND_clust(n))) {
786  int yoff = (clust == g ? 0 : late_int (clust, G_margin, CL_OFFSET, 0));
787  if (ND_rank(n) == GD_minrank(clust))
788  GD_ht2(clust) = MAX(GD_ht2(clust), ht2 + yoff);
789  if (ND_rank(n) == GD_maxrank(clust))
790  GD_ht1(clust) = MAX(GD_ht1(clust), ht2 + yoff);
791  }
792  }
793  }
794 
795  /* scan sub-clusters */
796  lbl = clust_ht(g);
797 
798  /* make the initial assignment of ycoords to leftmost nodes by ranks */
799  maxht = 0;
800  r = GD_maxrank(g);
801  (ND_coord(rank[r].v[0])).y = rank[r].ht1;
802  while (--r >= GD_minrank(g)) {
803  d0 = rank[r + 1].pht2 + rank[r].pht1 + GD_ranksep(g); /* prim node sep */
804  d1 = rank[r + 1].ht2 + rank[r].ht1 + CL_OFFSET; /* cluster sep */
805  delta = MAX(d0, d1);
806  if (rank[r].n > 0) /* this may reflect some problem */
807  (ND_coord(rank[r].v[0])).y = (ND_coord(rank[r + 1].v[0])).y + delta;
808 #ifdef DEBUG
809  else
810  fprintf(stderr, "dot set_ycoords: rank %d is empty\n",
811  rank[r].n);
812 #endif
813  maxht = MAX(maxht, delta);
814  }
815 
816  /* If there are cluster labels and the drawing is rotated, we need special processing to
817  * allocate enough room. We use adjustRanks for this, and then recompute the maxht if
818  * the ranks are to be equally spaced. This seems simpler and appears to work better than
819  * handling equal spacing as a special case.
820  */
821  if (lbl && GD_flip(g)) {
822  adjustRanks(g, 0);
823  if (GD_exact_ranksep(g)) { /* recompute maxht */
824  maxht = 0;
825  r = GD_maxrank(g);
826  d0 = (ND_coord(rank[r].v[0])).y;
827  while (--r >= GD_minrank(g)) {
828  d1 = (ND_coord(rank[r].v[0])).y;
829  delta = d1 - d0;
830  maxht = MAX(maxht, delta);
831  d0 = d1;
832  }
833  }
834  }
835 
836  /* re-assign if ranks are equally spaced */
837  if (GD_exact_ranksep(g)) {
838  for (r = GD_maxrank(g) - 1; r >= GD_minrank(g); r--)
839  if (rank[r].n > 0) /* this may reflect the same problem :-() */
840  (ND_coord(rank[r].v[0])).y=
841  (ND_coord(rank[r + 1].v[0])).y + maxht;
842  }
843 
844  /* copy ycoord assignment from leftmost nodes to others */
845  for (n = GD_nlist(g); n; n = ND_next(n))
846  ND_coord(n).y = (ND_coord(rank[ND_rank(n)].v[0])).y;
847 }
848 
849 /* dot_compute_bb:
850  * Compute bounding box of g.
851  * The x limits of clusters are given by the x positions of ln and rn.
852  * This information is stored in the rank field, since it was calculated
853  * using network simplex.
854  * For the root graph, we don't enforce all the constraints on lr and
855  * rn, so we traverse the nodes and subclusters.
856  */
857 static void dot_compute_bb(graph_t * g, graph_t * root)
858 {
859  int r, c;
860  double x, offset;
861  node_t *v;
862  pointf LL, UR;
863 
864  if (g == dot_root(g)) {
865  LL.x = (double)(INT_MAX);
866  UR.x = (double)(-INT_MAX);
867  for (r = GD_minrank(g); r <= GD_maxrank(g); r++) {
868  int rnkn = GD_rank(g)[r].n;
869  if (rnkn == 0)
870  continue;
871  if ((v = GD_rank(g)[r].v[0]) == NULL)
872  continue;
873  for (c = 1; (ND_node_type(v) != NORMAL) && c < rnkn; c++)
874  v = GD_rank(g)[r].v[c];
875  if (ND_node_type(v) == NORMAL) {
876  x = ND_coord(v).x - ND_lw(v);
877  LL.x = MIN(LL.x, x);
878  }
879  else continue;
880  /* At this point, we know the rank contains a NORMAL node */
881  v = GD_rank(g)[r].v[rnkn - 1];
882  for (c = rnkn-2; ND_node_type(v) != NORMAL; c--)
883  v = GD_rank(g)[r].v[c];
884  x = ND_coord(v).x + ND_rw(v);
885  UR.x = MAX(UR.x, x);
886  }
887  offset = CL_OFFSET;
888  for (c = 1; c <= GD_n_cluster(g); c++) {
889  x = (double)(GD_bb(GD_clust(g)[c]).LL.x - offset);
890  LL.x = MIN(LL.x, x);
891  x = (double)(GD_bb(GD_clust(g)[c]).UR.x + offset);
892  UR.x = MAX(UR.x, x);
893  }
894  } else {
895  LL.x = (double)(ND_rank(GD_ln(g)));
896  UR.x = (double)(ND_rank(GD_rn(g)));
897  }
898  LL.y = ND_coord(GD_rank(root)[GD_maxrank(g)].v[0]).y - GD_ht1(g);
899  UR.y = ND_coord(GD_rank(root)[GD_minrank(g)].v[0]).y + GD_ht2(g);
900  GD_bb(g).LL = LL;
901  GD_bb(g).UR = UR;
902 }
903 
904 static void rec_bb(graph_t * g, graph_t * root)
905 {
906  int c;
907  for (c = 1; c <= GD_n_cluster(g); c++)
908  rec_bb(GD_clust(g)[c], root);
909  dot_compute_bb(g, root);
910 }
911 
912 /* scale_bb:
913  * Recursively rescale all bounding boxes using scale factors
914  * xf and yf. We assume all the bboxes have been computed.
915  */
916 static void scale_bb(graph_t * g, graph_t * root, double xf, double yf)
917 {
918  int c;
919 
920  for (c = 1; c <= GD_n_cluster(g); c++)
921  scale_bb(GD_clust(g)[c], root, xf, yf);
922  GD_bb(g).LL.x *= xf;
923  GD_bb(g).LL.y *= yf;
924  GD_bb(g).UR.x *= xf;
925  GD_bb(g).UR.y *= yf;
926 }
927 
928 /* adjustAspectRatio:
929  */
930 static void adjustAspectRatio (graph_t* g, aspect_t* asp)
931 {
932  double AR = (GD_bb(g).UR.x - GD_bb(g).LL.x)/(GD_bb(g).UR.y - GD_bb(g).LL.y);
933  if (Verbose) {
934  fprintf(stderr, "AR=%0.4lf\t Area= %0.4lf\t", AR, (double)(GD_bb(g).UR.x - GD_bb(g).LL.x)*(GD_bb(g).UR.y - GD_bb(g).LL.y)/10000.0);
935  fprintf(stderr, "Dummy=%d\n", countDummyNodes(g));
936  }
937  if (AR > 1.1*asp->targetAR) {
938  asp->nextIter = (int)(asp->targetAR * (double)(asp->curIterations - asp->prevIterations)/(AR));
939  }
940  else if (AR <= 0.8 * asp->targetAR) {
941  asp->nextIter = -1;
942  if (Verbose)
943  fprintf(stderr, "Going to apply another expansion.\n");
944  }
945  else {
946  asp->nextIter = 0;
947  }
948  if (Verbose)
949  fprintf(stderr, "next#iter=%d\n", asp->nextIter);
950 }
951 
952 /* set_aspect:
953  * Set bounding boxes and, if ratio is set, rescale graph.
954  * Note that if some dimension shrinks, there may be problems
955  * with labels.
956  */
957 static void set_aspect(graph_t * g, aspect_t* asp)
958 {
959  double xf = 0.0, yf = 0.0, actual, desired;
960  node_t *n;
961  boolean scale_it, filled;
962  point sz;
963 
964  rec_bb(g, g);
965  if ((GD_maxrank(g) > 0) && (GD_drawing(g)->ratio_kind)) {
966  sz.x = GD_bb(g).UR.x - GD_bb(g).LL.x;
967  sz.y = GD_bb(g).UR.y - GD_bb(g).LL.y; /* normalize */
968  if (GD_flip(g)) {
969  int t = sz.x;
970  sz.x = sz.y;
971  sz.y = t;
972  }
973  scale_it = TRUE;
974  if (GD_drawing(g)->ratio_kind == R_AUTO)
975  filled = idealsize(g, .5);
976  else
977  filled = (GD_drawing(g)->ratio_kind == R_FILL);
978  if (filled) {
979  /* fill is weird because both X and Y can stretch */
980  if (GD_drawing(g)->size.x <= 0)
981  scale_it = FALSE;
982  else {
983  xf = (double) GD_drawing(g)->size.x / (double) sz.x;
984  yf = (double) GD_drawing(g)->size.y / (double) sz.y;
985  if ((xf < 1.0) || (yf < 1.0)) {
986  if (xf < yf) {
987  yf = yf / xf;
988  xf = 1.0;
989  } else {
990  xf = xf / yf;
991  yf = 1.0;
992  }
993  }
994  }
995  } else if (GD_drawing(g)->ratio_kind == R_EXPAND) {
996  if (GD_drawing(g)->size.x <= 0)
997  scale_it = FALSE;
998  else {
999  xf = (double) GD_drawing(g)->size.x /
1000  (double) GD_bb(g).UR.x;
1001  yf = (double) GD_drawing(g)->size.y /
1002  (double) GD_bb(g).UR.y;
1003  if ((xf > 1.0) && (yf > 1.0)) {
1004  double scale = MIN(xf, yf);
1005  xf = yf = scale;
1006  } else
1007  scale_it = FALSE;
1008  }
1009  } else if (GD_drawing(g)->ratio_kind == R_VALUE) {
1010  desired = GD_drawing(g)->ratio;
1011  actual = ((double) sz.y) / ((double) sz.x);
1012  if (actual < desired) {
1013  yf = desired / actual;
1014  xf = 1.0;
1015  } else {
1016  xf = actual / desired;
1017  yf = 1.0;
1018  }
1019  } else
1020  scale_it = FALSE;
1021  if (scale_it) {
1022  if (GD_flip(g)) {
1023  double t = xf;
1024  xf = yf;
1025  yf = t;
1026  }
1027  for (n = GD_nlist(g); n; n = ND_next(n)) {
1028  ND_coord(n).x = ROUND(ND_coord(n).x * xf);
1029  ND_coord(n).y = ROUND(ND_coord(n).y * yf);
1030  }
1031  scale_bb(g, g, xf, yf);
1032  }
1033  }
1034 
1035  if (asp) adjustAspectRatio (g, asp);
1036 }
1037 
1038 static point resize_leaf(node_t * leaf, point lbound)
1039 {
1040  gv_nodesize(leaf, GD_flip(agraphof(leaf)));
1041  ND_coord(leaf).y = lbound.y;
1042  ND_coord(leaf).x = lbound.x + ND_lw(leaf);
1043  lbound.x = lbound.x + ND_lw(leaf) + ND_rw(leaf) + GD_nodesep(agraphof(leaf));
1044  return lbound;
1045 }
1046 
1047 static point place_leaf(graph_t* ing, node_t * leaf, point lbound, int order)
1048 {
1049  node_t *leader;
1050  graph_t *g = dot_root(ing);
1051 
1052  leader = UF_find(leaf);
1053  if (leaf != leader)
1054  fast_nodeapp(leader, leaf);
1055  ND_order(leaf) = order;
1056  ND_rank(leaf) = ND_rank(leader);
1057  GD_rank(g)[ND_rank(leaf)].v[ND_order(leaf)] = leaf;
1058  return resize_leaf(leaf, lbound);
1059 }
1060 
1061 /* make space for the leaf nodes of each rank */
1062 static void make_leafslots(graph_t * g)
1063 {
1064  int i, j, r;
1065  node_t *v;
1066 
1067  for (r = GD_minrank(g); r <= GD_maxrank(g); r++) {
1068  j = 0;
1069  for (i = 0; i < GD_rank(g)[r].n; i++) {
1070  v = GD_rank(g)[r].v[i];
1071  ND_order(v) = j;
1072  if (ND_ranktype(v) == LEAFSET)
1073  j = j + ND_UF_size(v);
1074  else
1075  j++;
1076  }
1077  if (j <= GD_rank(g)[r].n)
1078  continue;
1079  GD_rank(g)[r].v = ALLOC(j + 1, GD_rank(g)[r].v, node_t *);
1080  for (i = GD_rank(g)[r].n - 1; i >= 0; i--) {
1081  v = GD_rank(g)[r].v[i];
1082  GD_rank(g)[r].v[ND_order(v)] = v;
1083  }
1084  GD_rank(g)[r].n = j;
1085  GD_rank(g)[r].v[j] = NULL;
1086  }
1087 }
1088 
1089 static void do_leaves(graph_t * g, node_t * leader)
1090 {
1091  int j;
1092  point lbound;
1093  node_t *n;
1094  edge_t *e;
1095 
1096  if (ND_UF_size(leader) <= 1)
1097  return;
1098  lbound.x = ND_coord(leader).x - ND_lw(leader);
1099  lbound.y = ND_coord(leader).y;
1100  lbound = resize_leaf(leader, lbound);
1101  if (ND_out(leader).size > 0) { /* in-edge leaves */
1102  n = aghead(ND_out(leader).list[0]);
1103  j = ND_order(leader) + 1;
1104  for (e = agfstin(g, n); e; e = agnxtin(g, e)) {
1105  edge_t *e1 = AGMKOUT(e);
1106  if ((agtail(e1) != leader) && (UF_find(agtail(e1)) == leader)) {
1107  lbound = place_leaf(g, agtail(e1), lbound, j++);
1108  unmerge_oneway(e1);
1109  elist_append(e1, ND_in(aghead(e1)));
1110  }
1111  }
1112  } else { /* out edge leaves */
1113  n = agtail(ND_in(leader).list[0]);
1114  j = ND_order(leader) + 1;
1115  for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
1116  if ((aghead(e) != leader) && (UF_find(aghead(e)) == leader)) {
1117  lbound = place_leaf(g, aghead(e), lbound, j++);
1118  unmerge_oneway(e);
1119  elist_append(e, ND_out(agtail(e)));
1120  }
1121  }
1122  }
1123 }
1124 
1125 int ports_eq(edge_t * e, edge_t * f)
1126 {
1127  return ((ED_head_port(e).defined == ED_head_port(f).defined)
1128  && (((ED_head_port(e).p.x == ED_head_port(f).p.x) &&
1129  (ED_head_port(e).p.y == ED_head_port(f).p.y))
1130  || (ED_head_port(e).defined == FALSE))
1131  && (((ED_tail_port(e).p.x == ED_tail_port(f).p.x) &&
1132  (ED_tail_port(e).p.y == ED_tail_port(f).p.y))
1133  || (ED_tail_port(e).defined == FALSE))
1134  );
1135 }
1136 
1137 static void expand_leaves(graph_t * g)
1138 {
1139  int i, d;
1140  node_t *n;
1141  edge_t *e, *f;
1142 
1143  make_leafslots(g);
1144  for (n = GD_nlist(g); n; n = ND_next(n)) {
1145  if (ND_inleaf(n))
1146  do_leaves(g, ND_inleaf(n));
1147  if (ND_outleaf(n))
1148  do_leaves(g, ND_outleaf(n));
1149  if (ND_other(n).list)
1150  for (i = 0; (e = ND_other(n).list[i]); i++) {
1151  if ((d = ND_rank(aghead(e)) - ND_rank(aghead(e))) == 0)
1152  continue;
1153  f = ED_to_orig(e);
1154  if (ports_eq(e, f) == FALSE) {
1155  zapinlist(&(ND_other(n)), e);
1156  if (d == 1)
1157  fast_edge(e);
1158  /*else unitize(e); ### */
1159  i--;
1160  }
1161  }
1162  }
1163 }
1164 
1165 /* make_lrvn:
1166  * Add left and right slacknodes to a cluster which
1167  * are used in the LP to constrain nodes not in g but
1168  * sharing its ranks to be to the left or right of g
1169  * by a specified amount.
1170  * The slacknodes ln and rn give the x position of the
1171  * left and right side of the cluster's bounding box. In
1172  * particular, any cluster labels on the left or right side
1173  * are inside.
1174  * If a cluster has a label, and we have rankdir!=LR, we make
1175  * sure the cluster is wide enough for the label. Note that
1176  * if the label is wider than the cluster, the nodes in the
1177  * cluster may not be centered.
1178  */
1179 static void make_lrvn(graph_t * g)
1180 {
1181  node_t *ln, *rn;
1182 
1183  if (GD_ln(g))
1184  return;
1185  ln = virtual_node(dot_root(g));
1186  ND_node_type(ln) = SLACKNODE;
1187  rn = virtual_node(dot_root(g));
1188  ND_node_type(rn) = SLACKNODE;
1189 
1190  if (GD_label(g) && (g != dot_root(g)) && !GD_flip(agroot(g))) {
1191  int w = MAX(GD_border(g)[BOTTOM_IX].x, GD_border(g)[TOP_IX].x);
1192  make_aux_edge(ln, rn, w, 0);
1193  }
1194 
1195  GD_ln(g) = ln;
1196  GD_rn(g) = rn;
1197 }
1198 
1199 /* contain_nodes:
1200  * make left and right bounding box virtual nodes ln and rn
1201  * constrain interior nodes
1202  */
1203 static void contain_nodes(graph_t * g)
1204 {
1205  int margin, r;
1206  node_t *ln, *rn, *v;
1207 
1208  margin = late_int (g, G_margin, CL_OFFSET, 0);
1209  make_lrvn(g);
1210  ln = GD_ln(g);
1211  rn = GD_rn(g);
1212  for (r = GD_minrank(g); r <= GD_maxrank(g); r++) {
1213  if (GD_rank(g)[r].n == 0)
1214  continue;
1215  v = GD_rank(g)[r].v[0];
1216  if (v == NULL) {
1217  agerr(AGERR, "contain_nodes clust %s rank %d missing node\n",
1218  agnameof(g), r);
1219  continue;
1220  }
1221  make_aux_edge(ln, v,
1222  ND_lw(v) + margin + GD_border(g)[LEFT_IX].x, 0);
1223  v = GD_rank(g)[r].v[GD_rank(g)[r].n - 1];
1224  make_aux_edge(v, rn,
1225  ND_rw(v) + margin + GD_border(g)[RIGHT_IX].x, 0);
1226  }
1227 }
1228 
1229 /* idealsize:
1230  * set g->drawing->size to a reasonable default.
1231  * returns a boolean to indicate if drawing is to
1232  * be scaled and filled */
1233 static boolean idealsize(graph_t * g, double minallowed)
1234 {
1235  double xf, yf, f, R;
1236  pointf b, relpage, margin;
1237 
1238  /* try for one page */
1239  relpage = GD_drawing(g)->page;
1240  if (relpage.x < 0.001 || relpage.y < 0.001)
1241  return FALSE; /* no page was specified */
1242  margin = GD_drawing(g)->margin;
1243  relpage = sub_pointf(relpage, margin);
1244  relpage = sub_pointf(relpage, margin);
1245  b.x = GD_bb(g).UR.x;
1246  b.y = GD_bb(g).UR.y;
1247  xf = relpage.x / b.x;
1248  yf = relpage.y / b.y;
1249  if ((xf >= 1.0) && (yf >= 1.0))
1250  return FALSE; /* fits on one page */
1251 
1252  f = MIN(xf, yf);
1253  xf = yf = MAX(f, minallowed);
1254 
1255  R = ceil((xf * b.x) / relpage.x);
1256  xf = ((R * relpage.x) / b.x);
1257  R = ceil((yf * b.y) / relpage.y);
1258  yf = ((R * relpage.y) / b.y);
1259  GD_drawing(g)->size.x = b.x * xf;
1260  GD_drawing(g)->size.y = b.y * yf;
1261  return TRUE;
1262 }
double pht1
Definition: types.h:208
int curIterations
Definition: aspect.h:21
#define GD_label(g)
Definition: types.h:361
#define MAX(a, b)
Definition: agerror.c:17
#define LEFT_IX
Definition: const.h:115
#define ND_rank(n)
Definition: types.h:490
Agnode_t * agtail(Agedge_t *e)
Definition: edge.c:494
Definition: cgraph.h:389
#define GD_has_labels(g)
Definition: types.h:352
#define GD_nlist(g)
Definition: types.h:381
Agnode_t * aghead(Agedge_t *e)
Definition: edge.c:502
int nextIter
Definition: aspect.h:22
#define elist_append(item, L)
Definition: types.h:262
double ht1
Definition: types.h:207
#define MIN(a, b)
Definition: arith.h:38
#define GD_n_cluster(g)
Definition: types.h:376
#define GD_border(g)
Definition: types.h:342
void dot_position(graph_t *g, aspect_t *asp)
Definition: position.c:120
void fast_nodeapp(Agnode_t *, Agnode_t *)
Definition: fastgr.c:218
Agedge_t * find_fast_edge(Agnode_t *, Agnode_t *)
Definition: fastgr.c:42
#define ND_inleaf(n)
Definition: types.h:469
#define ND_node_type(n)
Definition: types.h:479
#define ALLOC(size, ptr, type)
Definition: memory.h:41
#define ED_head_port(e)
Definition: types.h:545
#define ND_save_in(n)
Definition: types.h:493
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition: edge.c:25
#define ROUND(f)
Definition: arith.h:87
#define assert(x)
Definition: cghdr.h:48
Agedge_t in
Definition: cgraph.h:134
Agnode_t * virtual_node(Agraph_t *)
Definition: fastgr.c:240
#define GD_ht2(g)
Definition: types.h:358
Definition: geom.h:30
#define ND_UF_size(n)
Definition: types.h:454
EXTERN Agsym_t * G_margin
Definition: globals.h:97
#define ED_to_orig(e)
Definition: types.h:555
#define ED_weight(e)
Definition: types.h:560
Agedge_t * agfstin(Agraph_t *g, Agnode_t *n)
Definition: edge.c:56
#define ED_label(e)
Definition: types.h:546
void unmerge_oneway(Agedge_t *)
Definition: fastgr.c:353
int agnnodes(Agraph_t *g)
double pht2
Definition: types.h:208
Agobj_t base
Definition: cgraph.h:121
#define GD_ln(g)
Definition: types.h:368
Definition: types.h:216
#define alloc_elist(n, L)
Definition: types.h:263
int agerr(agerrlevel_t level, const char *fmt,...)
Definition: agerror.c:142
#define AGOUTEDGE
Definition: cgraph.h:89
void dot_concentrate(graph_t *g)
Definition: conc.c:197
edge_t * make_aux_edge(node_t *u, node_t *v, double len, int wt)
Definition: position.c:172
int x
Definition: geom.h:28
Agedge_t * agnxtin(Agraph_t *g, Agedge_t *e)
Definition: edge.c:70
#define GD_ranksep(g)
Definition: types.h:386
#define AGTYPE(obj)
Definition: cgraph.h:100
#define ND_clust(n)
Definition: types.h:456
char * agget(void *obj, char *name)
Definition: attr.c:428
node_t * UF_find(node_t *n)
Definition: utils.c:144
#define BOTTOM_IX
Definition: const.h:112
if(!(aag_init))
Definition: scan.c:915
#define ED_tail_port(e)
Definition: types.h:554
int rank(graph_t *g, int balance, int maxiter)
Definition: ns.c:681
Definition: types.h:251
#define ND_ht(n)
Definition: types.h:467
void free()
Definition: types.h:216
int i
Definition: gvdevice.c:448
Agedge_t out
Definition: cgraph.h:134
double y
Definition: geom.h:30
#define ND_order(n)
Definition: types.h:481
#define SLACKNODE
Definition: const.h:29
int selfRightSpace(edge_t *e)
Definition: splines.c:1163
double targetAR
Definition: aspect.h:18
#define ND_mval(n)
Definition: types.h:476
#define VIRTUAL
Definition: const.h:28
#define GD_maxrank(g)
Definition: types.h:369
int n
Definition: types.h:203
void mark_lowclusters(Agraph_t *root)
Definition: cluster.c:395
Agraph_t * agroot(void *obj)
Definition: obj.c:169
int ports_eq(edge_t *e, edge_t *f)
Definition: position.c:1125
htmllabel_t * lbl
Definition: htmlparse.c:81
#define LEAFSET
Definition: const.h:42
#define ND_rw(n)
Definition: types.h:492
double ht2
Definition: types.h:207
edge_t ** list
Definition: types.h:252
#define AGMKOUT(e)
Definition: cgraph.h:405
int late_int(void *obj, attrsym_t *attr, int def, int low)
Definition: utils.c:69
Definition: grammar.c:79
#define GD_clust(g)
Definition: types.h:344
int prevIterations
Definition: aspect.h:20
Definition: cgraph.h:70
#define GD_flip(g)
Definition: types.h:365
Agobj_t base
Definition: cgraph.h:127
Agraph_t * dot_root(void *p)
Definition: dotinit.c:510
#define ND_outleaf(n)
Definition: types.h:484
#define CL_OFFSET
Definition: const.h:155
#define GD_exact_ranksep(g)
Definition: types.h:347
Definition: types.h:216
#define ND_lw(n)
Definition: types.h:474
#define ND_alg(n)
Definition: types.h:451
EXTERN unsigned char Concentrate
Definition: globals.h:85
Agraph_t * agraphof(void *obj)
Definition: obj.c:185
#define NULL
Definition: logic.h:50
node_t ** v
Definition: types.h:204
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition: edge.c:40
Definition: geom.h:28
double x
Definition: geom.h:30
#define ND_in(n)
Definition: types.h:468
#define ND_coord(n)
Definition: types.h:457
#define right(i)
Definition: closest.c:87
#define ND_next(n)
Definition: types.h:478
EXTERN unsigned char Verbose
Definition: globals.h:73
void zapinlist(elist *, Agedge_t *)
Definition: fastgr.c:100
char * agnameof(void *)
Definition: id.c:143
Agrec_t * data
Definition: cgraph.h:96
#define ND_out(n)
Definition: types.h:483
int size
Definition: types.h:253
#define ND_ranktype(n)
Definition: types.h:491
#define ND_other(n)
Definition: types.h:482
#define left
Definition: dthdr.h:23
#define AGINEDGE
Definition: cgraph.h:90
int agcontains(Agraph_t *, void *)
Definition: obj.c:245
void gv_nodesize(node_t *n, boolean flip)
Definition: utils.c:1930
#define GD_ht1(g)
Definition: types.h:357
int countDummyNodes(graph_t *g)
Definition: aspect.c:129
#define GD_bb(g)
Definition: types.h:337
#define GD_nodesep(g)
Definition: types.h:382
#define GD_minrank(g)
Definition: types.h:371
#define RIGHT_IX
Definition: const.h:113
int flat_edges(Agraph_t *)
Definition: flat.c:260
Agraph_t * root
Definition: cgraph.h:241
#define ND_flat_out(n)
Definition: types.h:460
#define GD_rank(g)
Definition: types.h:384
Definition: types.h:202
#define GD_rn(g)
Definition: types.h:387
#define NORMAL
Definition: const.h:27
#define GD_drawing(g)
Definition: types.h:336
#define EDGE_LABEL
Definition: const.h:184
#define ND_save_out(n)
Definition: types.h:494
int y
Definition: geom.h:28
#define ND_prev(n)
Definition: types.h:488
#define ED_dist(e)
Definition: types.h:559
char * el(GVJ_t *job, char *template,...)
#define FALSE
Definition: cgraph.h:24
#define TOP_IX
Definition: const.h:114
#define free_list(L)
Definition: types.h:264
Agedge_t * fast_edge(Agedge_t *)
Definition: fastgr.c:74
#define ED_minlen(e)
Definition: types.h:549
#define INT_MAX
Definition: arith.h:55
#define NEW(t)
Definition: memory.h:35
#define TRUE
Definition: cgraph.h:27