|
Graphviz
2.29.20120524.0446
|
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 }
1.7.5