Graphviz  2.29.20120524.0446
lib/dotgen/sameport.c
Go to the documentation of this file.
00001 /* $Id$ $Revision$ */
00002 /* vim:set shiftwidth=4 ts=8: */
00003 
00004 /*************************************************************************
00005  * Copyright (c) 2011 AT&T Intellectual Property 
00006  * All rights reserved. This program and the accompanying materials
00007  * are made available under the terms of the Eclipse Public License v1.0
00008  * which accompanies this distribution, and is available at
00009  * http://www.eclipse.org/legal/epl-v10.html
00010  *
00011  * Contributors: See CVS logs. Details at http://www.graphviz.org/
00012  *************************************************************************/
00013 
00014 
00015 /*  vladimir@cs.ualberta.ca,  9-Dec-1997
00016  *      merge edges with specified samehead/sametail onto the same port
00017  */
00018 
00019 #include        "dot.h"
00020 
00021 
00022 #define MAXSAME 5               /* max no of same{head,tail} groups on a node */
00023 
00024 typedef struct same_t {
00025     char *id;                   /* group id */
00026     elist l;                    /* edges in the group */
00027     int n_arr;                  /* number of edges with arrows */
00028     double arr_len;             /* arrow length of an edge in the group */
00029 } same_t;
00030 static int n_same;              /* number of same_t groups on current node */
00031 
00032 static void sameedge(same_t * same, node_t * n, edge_t * e, char *id);
00033 static void sameport(node_t * u, elist * l, double arr_len);
00034 
00035 void dot_sameports(graph_t * g)
00036 /* merge edge ports in G */
00037 {
00038     node_t *n;
00039     edge_t *e;
00040     char *id;
00041     same_t same[MAXSAME];
00042     int i;
00043 
00044 #ifndef WITH_CGRAPH
00045     E_samehead = agfindattr(g->proto->e, "samehead");
00046     E_sametail = agfindattr(g->proto->e, "sametail");
00047 #else /* WITH_CGRAPH */
00048     E_samehead = agattr(g, AGEDGE, "samehead",(char*)0);
00049     E_sametail = agattr(g, AGEDGE, "sametail",(char*)0);
00050 #endif /* WITH_CGRAPH */
00051     if (!(E_samehead || E_sametail))
00052         return;
00053     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00054         n_same = 0;
00055         for (e = agfstedge(g, n); e; e = agnxtedge(g, e, n)) {
00056             if (aghead(e) == agtail(e)) continue;  /* Don't support same* for loops */
00057             if (aghead(e) == n && E_samehead &&
00058 #ifndef WITH_CGRAPH
00059                 (id = agxget(e, E_samehead->index))[0])
00060 #else /* WITH_CGRAPH */
00061                 (id = agxget(e, E_samehead))[0])
00062 #endif /* WITH_CGRAPH */
00063                 sameedge(same, n, e, id);
00064             else if (agtail(e) == n && E_sametail &&
00065 #ifndef WITH_CGRAPH
00066                 (id = agxget(e, E_sametail->index))[0])
00067 #else /* WITH_CGRAPH */
00068                 (id = agxget(e, E_sametail))[0])
00069 #endif /* WITH_CGRAPH */
00070                 sameedge(same, n, e, id);
00071         }
00072         for (i = 0; i < n_same; i++) {
00073             if (same[i].l.size > 1)
00074                 sameport(n, &same[i].l, same[i].arr_len);
00075             free_list(same[i].l);
00076             /* I sure hope I don't need to free the char* id */
00077         }
00078     }
00079 }
00080 
00081 static void sameedge(same_t * same, node_t * n, edge_t * e, char *id)
00082 /* register E in the SAME structure of N under ID. Uses static int N_SAME */
00083 {
00084     int i, sflag, eflag, flag;
00085 
00086     for (i = 0; i < n_same; i++)
00087         if (streq(same[i].id, id)) {
00088             elist_append(e, same[i].l);
00089             goto set_arrow;
00090         }
00091     if (++n_same > MAXSAME) {
00092         n_same--;
00093         agerr(AGERR, "too many (> %d) same{head,tail} groups for node %s\n",
00094               MAXSAME, agnameof(n));
00095         return;
00096     }
00097     alloc_elist(1, same[i].l);
00098     elist_fastapp(e, same[i].l);
00099     same[i].id = id;
00100     same[i].n_arr = 0;
00101     same[i].arr_len = 0;
00102   set_arrow:
00103     arrow_flags(e, &sflag, &eflag);
00104     if ((flag = aghead(e) == n ? eflag : sflag))
00105         same[i].arr_len =
00106             /* only consider arrows if there's exactly one arrow */
00107             (++same[i].n_arr == 1) ? arrow_length(e, flag) : 0;
00108 }
00109 
00110 static void sameport(node_t * u, elist * l, double arr_len)
00111 /* make all edges in L share the same port on U. The port is placed on the
00112    node boundary and the average angle between the edges. FIXME: this assumes
00113    naively that the edges are straight lines, which is wrong if they are long.
00114    In that case something like concentration could be done.
00115 
00116    An arr_port is also computed that's ARR_LEN away from the node boundary.
00117    It's used for edges that don't themselves have an arrow.
00118 */
00119 {
00120     node_t *v;
00121     edge_t *e, *f;
00122     int i;
00123     double x = 0, y = 0, x1, y1, x2, y2, r;
00124     port prt;
00125     int sflag, eflag;
00126 #ifdef OLD
00127     int ht;
00128     port arr_prt;
00129 #endif
00130 
00131     /* Compute the direction vector (x,y) of the average direction. We compute
00132        with direction vectors instead of angles because else we have to first
00133        bring the angles within PI of each other. av(a,b)!=av(a,b+2*PI) */
00134     for (i = 0; i < l->size; i++) {
00135         e = l->list[i];
00136         if (aghead(e) == u)
00137             v = agtail(e);
00138         else
00139             v = aghead(e);
00140         x1 = ND_coord(v).x - ND_coord(u).x;
00141         y1 = ND_coord(v).y - ND_coord(u).y;
00142         r = hypot(x1, y1);
00143         x += x1 / r;
00144         y += y1 / r;
00145     }
00146     r = hypot(x, y);
00147     x /= r;
00148     y /= r;
00149 
00150     /* (x1,y1),(x2,y2) is a segment that must cross the node boundary */
00151     x1 = ND_coord(u).x;
00152     y1 = ND_coord(u).y; /* center of node */
00153     r = MAX(ND_lw(u) + ND_rw(u), ND_ht(u) + GD_ranksep(agraphof(u)));   /* far away */
00154     x2 = x * r + ND_coord(u).x;
00155     y2 = y * r + ND_coord(u).y;
00156     {                           /* now move (x1,y1) to the node boundary */
00157         pointf curve[4];                /* bezier control points for a straight line */
00158         curve[0].x = x1;
00159         curve[0].y = y1;
00160         curve[1].x = (2 * x1 + x2) / 3;
00161         curve[1].y = (2 * y1 + y2) / 3;
00162         curve[2].x = (2 * x2 + x1) / 3;
00163         curve[2].y = (2 * y2 + y1) / 3;
00164         curve[3].x = x2;
00165         curve[3].y = y2;
00166 
00167         shape_clip(u, curve);
00168         x1 = curve[0].x - ND_coord(u).x;
00169         y1 = curve[0].y - ND_coord(u).y;
00170     }
00171 
00172     /* compute PORT on the boundary */
00173     prt.p.x = ROUND(x1);
00174     prt.p.y = ROUND(y1);
00175     prt.bp = 0;
00176     prt.order =
00177         (MC_SCALE * (ND_lw(u) + prt.p.x)) / (ND_lw(u) + ND_rw(u));
00178     prt.constrained = FALSE;
00179     prt.defined = TRUE;
00180     prt.clip = FALSE;
00181     prt.dyna = FALSE;
00182     prt.theta = 0;
00183     prt.side = 0;
00184     prt.name = NULL;
00185 
00186 #ifdef OBSOLETE
00187 /* This is commented because a version of gcc cannot handle it otherwise.
00188 This code appears obsolete and wrong. First, we don't use arr_prt
00189 anymore, as we have previously ifdef'ed out the code below where it
00190 is used. In addition, it resets the rank height. But we've already
00191 positioned the nodes, so it can cause the new ht2, when added to a
00192 node's y coordinate to give a value in the middle of the rank above.
00193 This causes havoc when constructing box for spline routing.
00194 See bug 419.
00195 If we really want to make room for arrowheads, this should be done in
00196 the general case that the user sets a small ranksep, and requires repositioning
00197 nodes and maintaining equal separation when specified
00198 */
00199     /* compute ARR_PORT at a distance ARR_LEN away from the boundary */
00200     if ((arr_prt.defined = arr_len && TRUE)) {
00201         arr_prt.p.x = ROUND(x1 + x * arr_len);
00202         arr_prt.p.y = ROUND(y1 + y * arr_len);
00203         arr_prt.bp = 0;
00204         arr_prt.side = 0;
00205         arr_prt.constrained = FALSE;
00206         arr_prt.order =
00207             (MC_SCALE * (ND_lw_i(u) + arr_prt.p.x)) / (ND_lw_i(u) +
00208                                                        ND_rw_i(u));
00209         /* adjust ht so that splines.c uses feasible boxes.
00210            FIXME: I guess this adds an extra box for all edges in the rank */
00211         if (u == l->list[0]->head) {
00212             if (GD_rank(u->graph)[ND_rank(u)].ht2 <
00213                 (ht = ABS(arr_prt.p.y)))
00214                 GD_rank(u->graph)[ND_rank(u)].ht2 = ht;
00215         } else {
00216             if (GD_rank(u->graph)[ND_rank(u)].ht1 <
00217                 (ht = ABS(arr_prt.p.y)))
00218                 GD_rank(u->graph)[ND_rank(u)].ht1 = ht;
00219         }
00220     }
00221 #endif
00222 
00223     /* assign one of the ports to every edge */
00224     for (i = 0; i < l->size; i++) {
00225         e = l->list[i];
00226         arrow_flags(e, &sflag, &eflag);
00227 #ifndef OBSOLETE
00228         for (; e; e = ED_to_virt(e)) {  /* assign to all virt edges of e */
00229             for (f = e; f;
00230                  f = ED_edge_type(f) == VIRTUAL &&
00231                  ND_node_type(aghead(f)) == VIRTUAL &&
00232                  ND_out(aghead(f)).size == 1 ?
00233                  ND_out(aghead(f)).list[0] : NULL) {
00234                 if (aghead(f) == u)
00235                     ED_head_port(f) = prt;
00236                 if (agtail(f) == u)
00237                     ED_tail_port(f) = prt;
00238             }
00239             for (f = e; f;
00240                  f = ED_edge_type(f) == VIRTUAL &&
00241                  ND_node_type(agtail(f)) == VIRTUAL &&
00242                  ND_in(agtail(f)).size == 1 ?
00243                  ND_in(agtail(f)).list[0] : NULL) {
00244                 if (aghead(f) == u)
00245                     ED_head_port(f) = prt;
00246                 if (agtail(f) == u)
00247                     ED_tail_port(f) = prt;
00248             }
00249         }
00250 #else
00251         for (; e; e = ED_to_virt(e)) {  /* assign to all virt edges of e */
00252             if (aghead(e) == u)
00253                 ED_head_port(e) =
00254                     arr_port.defined && !eflag ? arr_prt : prt;
00255             if (agtail(e) == u)
00256                 ED_tail_port(e) =
00257                     arr_port.defined && !sflag ? arr_prt : prt;
00258         }
00259 #endif
00260     }
00261 
00262     ND_has_port(u) = TRUE;      /* kinda pointless, because mincross is already done */
00263 }