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 | /* vladimir@cs.ualberta.ca, 9-Dec-1997 |
16 | * merge edges with specified samehead/sametail onto the same port |
17 | */ |
18 | |
19 | #include "dot.h" |
20 | |
21 | |
22 | #define MAXSAME 5 /* max no of same{head,tail} groups on a node */ |
23 | |
24 | typedef struct same_t { |
25 | char *id; /* group id */ |
26 | elist l; /* edges in the group */ |
27 | int n_arr; /* number of edges with arrows */ |
28 | double arr_len; /* arrow length of an edge in the group */ |
29 | } same_t; |
30 | |
31 | static int sameedge(same_t * same, int n_same, node_t * n, edge_t * e, char *id); |
32 | static void sameport(node_t * u, elist * l, double arr_len); |
33 | |
34 | void dot_sameports(graph_t * g) |
35 | /* merge edge ports in G */ |
36 | { |
37 | node_t *n; |
38 | edge_t *e; |
39 | char *id; |
40 | same_t samehead[MAXSAME]; |
41 | same_t sametail[MAXSAME]; |
42 | int n_samehead; /* number of same_t groups on current node */ |
43 | int n_sametail; /* number of same_t groups on current node */ |
44 | int i; |
45 | |
46 | E_samehead = agattr(g, AGEDGE, "samehead" ,(char*)0); |
47 | E_sametail = agattr(g, AGEDGE, "sametail" ,(char*)0); |
48 | if (!(E_samehead || E_sametail)) |
49 | return; |
50 | for (n = agfstnode(g); n; n = agnxtnode(g, n)) { |
51 | n_samehead = n_sametail = 0; |
52 | for (e = agfstedge(g, n); e; e = agnxtedge(g, e, n)) { |
53 | if (aghead(e) == agtail(e)) continue; /* Don't support same* for loops */ |
54 | if (aghead(e) == n && E_samehead && |
55 | (id = agxget(e, E_samehead))[0]) |
56 | n_samehead = sameedge(samehead, n_samehead, n, e, id); |
57 | else if (agtail(e) == n && E_sametail && |
58 | (id = agxget(e, E_sametail))[0]) |
59 | n_sametail = sameedge(sametail, n_sametail, n, e, id); |
60 | } |
61 | for (i = 0; i < n_samehead; i++) { |
62 | if (samehead[i].l.size > 1) |
63 | sameport(n, &samehead[i].l, samehead[i].arr_len); |
64 | free_list(samehead[i].l); |
65 | /* I sure hope I don't need to free the char* id */ |
66 | } |
67 | for (i = 0; i < n_sametail; i++) { |
68 | if (sametail[i].l.size > 1) |
69 | sameport(n, &sametail[i].l, sametail[i].arr_len); |
70 | free_list(sametail[i].l); |
71 | /* I sure hope I don't need to free the char* id */ |
72 | } |
73 | } |
74 | } |
75 | |
76 | static int sameedge(same_t * same, int n_same, node_t * n, edge_t * e, char *id) |
77 | /* register E in the SAME structure of N under ID. Uses static int N_SAME */ |
78 | { |
79 | int i, sflag, eflag, flag; |
80 | |
81 | for (i = 0; i < n_same; i++) |
82 | if (streq(same[i].id, id)) { |
83 | elist_append(e, same[i].l); |
84 | goto set_arrow; |
85 | } |
86 | if (++n_same > MAXSAME) { |
87 | n_same--; |
88 | agerr(AGERR, "too many (> %d) same{head,tail} groups for node %s\n" , |
89 | MAXSAME, agnameof(n)); |
90 | return n_same; |
91 | } |
92 | alloc_elist(1, same[i].l); |
93 | elist_fastapp(e, same[i].l); |
94 | same[i].id = id; |
95 | same[i].n_arr = 0; |
96 | same[i].arr_len = 0; |
97 | set_arrow: |
98 | arrow_flags(e, &sflag, &eflag); |
99 | if ((flag = aghead(e) == n ? eflag : sflag)) |
100 | same[i].arr_len = |
101 | /* only consider arrows if there's exactly one arrow */ |
102 | (++same[i].n_arr == 1) ? arrow_length(e, flag) : 0; |
103 | return n_same; |
104 | } |
105 | |
106 | static void sameport(node_t * u, elist * l, double arr_len) |
107 | /* make all edges in L share the same port on U. The port is placed on the |
108 | node boundary and the average angle between the edges. FIXME: this assumes |
109 | naively that the edges are straight lines, which is wrong if they are long. |
110 | In that case something like concentration could be done. |
111 | |
112 | An arr_port is also computed that's ARR_LEN away from the node boundary. |
113 | It's used for edges that don't themselves have an arrow. |
114 | */ |
115 | { |
116 | node_t *v; |
117 | edge_t *e, *f; |
118 | int i; |
119 | double x = 0, y = 0, x1, y1, x2, y2, r; |
120 | port prt; |
121 | int sflag, eflag; |
122 | #ifdef OLD |
123 | int ht; |
124 | port arr_prt; |
125 | #endif |
126 | |
127 | /* Compute the direction vector (x,y) of the average direction. We compute |
128 | with direction vectors instead of angles because else we have to first |
129 | bring the angles within PI of each other. av(a,b)!=av(a,b+2*PI) */ |
130 | for (i = 0; i < l->size; i++) { |
131 | e = l->list[i]; |
132 | if (aghead(e) == u) |
133 | v = agtail(e); |
134 | else |
135 | v = aghead(e); |
136 | x1 = ND_coord(v).x - ND_coord(u).x; |
137 | y1 = ND_coord(v).y - ND_coord(u).y; |
138 | r = hypot(x1, y1); |
139 | x += x1 / r; |
140 | y += y1 / r; |
141 | } |
142 | r = hypot(x, y); |
143 | x /= r; |
144 | y /= r; |
145 | |
146 | /* (x1,y1),(x2,y2) is a segment that must cross the node boundary */ |
147 | x1 = ND_coord(u).x; |
148 | y1 = ND_coord(u).y; /* center of node */ |
149 | r = MAX(ND_lw(u) + ND_rw(u), ND_ht(u) + GD_ranksep(agraphof(u))); /* far away */ |
150 | x2 = x * r + ND_coord(u).x; |
151 | y2 = y * r + ND_coord(u).y; |
152 | { /* now move (x1,y1) to the node boundary */ |
153 | pointf curve[4]; /* bezier control points for a straight line */ |
154 | curve[0].x = x1; |
155 | curve[0].y = y1; |
156 | curve[1].x = (2 * x1 + x2) / 3; |
157 | curve[1].y = (2 * y1 + y2) / 3; |
158 | curve[2].x = (2 * x2 + x1) / 3; |
159 | curve[2].y = (2 * y2 + y1) / 3; |
160 | curve[3].x = x2; |
161 | curve[3].y = y2; |
162 | |
163 | shape_clip(u, curve); |
164 | x1 = curve[0].x - ND_coord(u).x; |
165 | y1 = curve[0].y - ND_coord(u).y; |
166 | } |
167 | |
168 | /* compute PORT on the boundary */ |
169 | prt.p.x = ROUND(x1); |
170 | prt.p.y = ROUND(y1); |
171 | prt.bp = 0; |
172 | prt.order = |
173 | (MC_SCALE * (ND_lw(u) + prt.p.x)) / (ND_lw(u) + ND_rw(u)); |
174 | prt.constrained = FALSE; |
175 | prt.defined = TRUE; |
176 | prt.clip = FALSE; |
177 | prt.dyna = FALSE; |
178 | prt.theta = 0; |
179 | prt.side = 0; |
180 | prt.name = NULL; |
181 | |
182 | #ifdef OBSOLETE |
183 | /* This is commented because a version of gcc cannot handle it otherwise. |
184 | This code appears obsolete and wrong. First, we don't use arr_prt |
185 | anymore, as we have previously ifdef'ed out the code below where it |
186 | is used. In addition, it resets the rank height. But we've already |
187 | positioned the nodes, so it can cause the new ht2, when added to a |
188 | node's y coordinate to give a value in the middle of the rank above. |
189 | This causes havoc when constructing box for spline routing. |
190 | See bug 419. |
191 | If we really want to make room for arrowheads, this should be done in |
192 | the general case that the user sets a small ranksep, and requires repositioning |
193 | nodes and maintaining equal separation when specified |
194 | */ |
195 | /* compute ARR_PORT at a distance ARR_LEN away from the boundary */ |
196 | if ((arr_prt.defined = arr_len && TRUE)) { |
197 | arr_prt.p.x = ROUND(x1 + x * arr_len); |
198 | arr_prt.p.y = ROUND(y1 + y * arr_len); |
199 | arr_prt.bp = 0; |
200 | arr_prt.side = 0; |
201 | arr_prt.constrained = FALSE; |
202 | arr_prt.order = |
203 | (MC_SCALE * (ND_lw_i(u) + arr_prt.p.x)) / (ND_lw_i(u) + |
204 | ND_rw_i(u)); |
205 | /* adjust ht so that splines.c uses feasible boxes. |
206 | FIXME: I guess this adds an extra box for all edges in the rank */ |
207 | if (u == l->list[0]->head) { |
208 | if (GD_rank(u->graph)[ND_rank(u)].ht2 < |
209 | (ht = ABS(arr_prt.p.y))) |
210 | GD_rank(u->graph)[ND_rank(u)].ht2 = ht; |
211 | } else { |
212 | if (GD_rank(u->graph)[ND_rank(u)].ht1 < |
213 | (ht = ABS(arr_prt.p.y))) |
214 | GD_rank(u->graph)[ND_rank(u)].ht1 = ht; |
215 | } |
216 | } |
217 | #endif |
218 | |
219 | /* assign one of the ports to every edge */ |
220 | for (i = 0; i < l->size; i++) { |
221 | e = l->list[i]; |
222 | arrow_flags(e, &sflag, &eflag); |
223 | #ifndef OBSOLETE |
224 | for (; e; e = ED_to_virt(e)) { /* assign to all virt edges of e */ |
225 | for (f = e; f; |
226 | f = ED_edge_type(f) == VIRTUAL && |
227 | ND_node_type(aghead(f)) == VIRTUAL && |
228 | ND_out(aghead(f)).size == 1 ? |
229 | ND_out(aghead(f)).list[0] : NULL) { |
230 | if (aghead(f) == u) |
231 | ED_head_port(f) = prt; |
232 | if (agtail(f) == u) |
233 | ED_tail_port(f) = prt; |
234 | } |
235 | for (f = e; f; |
236 | f = ED_edge_type(f) == VIRTUAL && |
237 | ND_node_type(agtail(f)) == VIRTUAL && |
238 | ND_in(agtail(f)).size == 1 ? |
239 | ND_in(agtail(f)).list[0] : NULL) { |
240 | if (aghead(f) == u) |
241 | ED_head_port(f) = prt; |
242 | if (agtail(f) == u) |
243 | ED_tail_port(f) = prt; |
244 | } |
245 | } |
246 | #else |
247 | for (; e; e = ED_to_virt(e)) { /* assign to all virt edges of e */ |
248 | if (aghead(e) == u) |
249 | ED_head_port(e) = |
250 | arr_port.defined && !eflag ? arr_prt : prt; |
251 | if (agtail(e) == u) |
252 | ED_tail_port(e) = |
253 | arr_port.defined && !sflag ? arr_prt : prt; |
254 | } |
255 | #endif |
256 | } |
257 | |
258 | ND_has_port(u) = TRUE; /* kinda pointless, because mincross is already done */ |
259 | } |
260 | |