2
This software may only be used by you under license from AT&T Corp.
3
("AT&T"). A copy of AT&T's Source Code Agreement is available at
4
AT&T's Internet website having the URL:
5
<http://www.research.att.com/sw/tools/graphviz/license/source.html>
6
If you received this software without first entering into a license
7
with AT&T, you have an infringing copy of this software and cannot use
8
it without violating AT&T's intellectual property rights.
12
/* vladimir@cs.ualberta.ca, 9-Dec-1997
13
* merge edges with specified samehead/sametail onto the same port
22
#define MAXSAME 5 /* max no of same{head,tail} groups on a node */
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
float arr_len; /* arrow length of an edge in the group */
30
static int n_same; /* number of same_t groups on current node */
32
static void sameedge (same_t* same, node_t* n, edge_t *e, char *id);
33
static void sameport (node_t *u, elist *l, float arr_len);
36
dot_sameports (graph_t* g)
37
/* merge edge ports in G */
45
E_samehead = agfindattr(g->proto->e,"samehead");
46
E_sametail = agfindattr(g->proto->e,"sametail");
47
if (!(E_samehead || E_sametail)) return;
48
for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
50
for (e = agfstedge(g,n); e; e = agnxtedge(g,e,n)) {
51
if (e->head==n && E_samehead &&
52
(id = agxget (e, E_samehead->index))[0])
53
sameedge (same, n, e, id);
54
else if (e->tail==n && E_sametail &&
55
(id = agxget (e, E_sametail->index))[0])
56
sameedge (same, n, e, id);
58
for (i=0; i<n_same; i++) {
59
if (same[i].l.size>1) sameport (n, &same[i].l, same[i].arr_len);
61
/* I sure hope I don't need to free the char* id */
67
sameedge (same_t* same, node_t* n, edge_t *e, char *id)
68
/* register E in the SAME structure of N under ID. Uses static int N_SAME */
70
int i,sflag,eflag,flag;
72
for (i=0; i<n_same; i++)
73
if (streq(same[i].id,id)) {
74
elist_append(e,same[i].l);
77
if (++n_same > MAXSAME) {
78
fprintf(stderr,"too many same{head,tail} groups for node %s\n", n->name);
81
alloc_elist(1,same[i].l);
82
elist_fastapp(e,same[i].l);
87
arrow_flags (e, &sflag, &eflag);
88
if ((flag = e->head==n ? eflag : sflag))
90
/* only consider arrows if there's exactly one arrow */
91
(++same[i].n_arr==1) ? arrow_length(e,flag) : 0;
95
sameport (node_t *u, elist *l, float arr_len)
96
/* make all edges in L share the same port on U. The port is placed on the
97
node boundary and the average angle between the edges. FIXME: this assumes
98
naively that the edges are straight lines, which is wrong if they are long.
99
In that case something like concentration could be done.
101
An arr_port is also computed that's ARR_LEN away from the node boundary.
102
It's used for edges that don't themselves have an arrow.
108
float x=0, y=0, x1, y1, x2, y2, r;
109
port_t port, arr_port;
110
int sflag, eflag, ht2;
112
/* Compute the direction vector (x,y) of the average direction. We compute
113
with direction vectors instead of angles because else we have to first
114
bring the angles within PI of each other. av(a,b)!=av(a,b+2*PI) */
115
for (i=0; i < l->size; i++) {
117
if (e->head==u) v=e->tail; else v=e->head;
118
x1 = v->u.coord.x - u->u.coord.x;
119
y1 = v->u.coord.y - u->u.coord.y;
128
/* (x1,y1),(x2,y2) is a segment that must cross the node boundary */
129
x1 = u->u.coord.x; y1 = u->u.coord.y; /* center of node */
130
r = MAX (u->u.lw+u->u.rw, u->u.ht); /* far away */
131
x2 = x*r + u->u.coord.x; y2 = y*r + u->u.coord.y;
132
{ /* now move (x1,y1) to the node boundary */
133
point curve[4]; /* bezier control points for a straight line */
134
curve[0].x = ROUND(x1); curve[0].y = ROUND(y1);
135
curve[1].x = ROUND((2*x1+x2)/3); curve[1].y = ROUND((2*y1+y2)/3);
136
curve[2].x = ROUND((2*x2+x1)/3); curve[2].y = ROUND((2*y2+y1)/3);
137
curve[3].x = ROUND(x2); curve[3].y = ROUND(y2);
139
shape_clip (u, curve, l->list[0]);
140
x1 = curve[0].x - u->u.coord.x;
141
y1 = curve[0].y - u->u.coord.y;
144
/* compute PORT on the boundary */
145
port.p.x = ROUND(x1);
146
port.p.y = ROUND(y1);
147
port.order = (MC_SCALE * (u->u.lw + port.p.x)) / (u->u.lw + u->u.rw);
148
port.constrained = FALSE;
151
/* compute ARR_PORT at a distance ARR_LEN away from the boundary */
152
if ((arr_port.defined = arr_len && TRUE)) {
153
arr_port.p.x = ROUND(x1 + x * arr_len);
154
arr_port.p.y = ROUND(y1 + y * arr_len);
155
arr_port.constrained = FALSE;
156
arr_port.order = (MC_SCALE * (u->u.lw + arr_port.p.x)) / (u->u.lw + u->u.rw);
157
if (u->graph->u.rank[u->u.rank].ht2 < (ht2 = ABS(arr_port.p.y)))
158
/* adjust ht2 so that splines.c uses feasible boxes.
159
FIXME: I guess this adds an extra box for all edges in the rank */
160
u->graph->u.rank[u->u.rank].ht2 = ht2;
163
/* assign one of the ports to every edge */
164
for (i=0; i < l->size; i++) {
166
arrow_flags (e, &sflag, &eflag);
168
for ( ; e; e=e->u.to_virt) { /* assign to all virt edges of e */
170
f = f->u.edge_type==VIRTUAL &&
171
f->head->u.node_type==VIRTUAL &&
172
f->head->u.out.size==1 ?
173
f->head->u.out.list[0] : NULL)
175
if (f->head==u) f->u.head_port = port;
176
if (f->tail==u) f->u.tail_port = port;
179
f = f->u.edge_type==VIRTUAL &&
180
f->tail->u.node_type==VIRTUAL &&
181
f->tail->u.in.size==1 ?
182
f->tail->u.in.list[0] : NULL)
184
if (f->head==u) f->u.head_port = port;
185
if (f->tail==u) f->u.tail_port = port;
189
for ( ; e; e=e->u.to_virt) { /* assign to all virt edges of e */
190
if (e->head==u) e->u.head_port =
191
arr_port.defined && !eflag ? arr_port : port;
192
if (e->tail==u) e->u.tail_port =
193
arr_port.defined && !sflag ? arr_port : port;
198
u->u.has_port = TRUE; /* kinda pointless, because mincross is already done */