#pragma prototyped
#include "dot.h"
#define MAXSAME 5
typedef struct same_t {
char *id;
elist l;
int n_arr;
double arr_len;
} same_t;
static int n_same;
static void sameedge (same_t* same, node_t* n, edge_t *e, char *id);
static void sameport (node_t *u, elist *l, double arr_len);
void
dot_sameports (graph_t* g)
{
node_t *n;
edge_t *e;
char *id;
same_t same[MAXSAME];
int i;
E_samehead = agfindattr(g->proto->e,"samehead");
E_sametail = agfindattr(g->proto->e,"sametail");
if (!(E_samehead || E_sametail)) return;
for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
n_same = 0;
for (e = agfstedge(g,n); e; e = agnxtedge(g,e,n)) {
if (e->head==n && E_samehead &&
(id = agxget (e, E_samehead->index))[0])
sameedge (same, n, e, id);
else if (e->tail==n && E_sametail &&
(id = agxget (e, E_sametail->index))[0])
sameedge (same, n, e, id);
}
for (i=0; i<n_same; i++) {
if (same[i].l.size>1) sameport (n, &same[i].l, same[i].arr_len);
free_list(same[i].l);
}
}
}
static void
sameedge (same_t* same, node_t* n, edge_t *e, char *id)
{
int i,sflag,eflag,flag;
for (i=0; i<n_same; i++)
if (streq(same[i].id,id)) {
elist_append(e,same[i].l);
goto set_arrow;
}
if (++n_same > MAXSAME) {
agerr(AGERR, "too many same{head,tail} groups for node %s\n", n->name);
return;
}
alloc_elist(1,same[i].l);
elist_fastapp(e,same[i].l);
same[i].id = id;
same[i].n_arr = 0;
same[i].arr_len = 0;
set_arrow:
arrow_flags (e, &sflag, &eflag);
if ((flag = e->head==n ? eflag : sflag))
same[i].arr_len =
(++same[i].n_arr==1) ? arrow_length(e,flag) : 0;
}
static void
sameport (node_t *u, elist *l, double arr_len)
{
node_t *v;
edge_t *e, *f;
int i;
double x=0, y=0, x1, y1, x2, y2, r;
port prt, arr_prt;
int sflag, eflag, ht;
for (i=0; i < l->size; i++) {
e = l->list[i];
if (e->head==u) v=e->tail; else v=e->head;
x1 = ND_coord_i(v).x - ND_coord_i(u).x;
y1 = ND_coord_i(v).y - ND_coord_i(u).y;
r = hypot(x1,y1);
x += x1 / r;
y += y1 / r;
}
r = hypot(x,y);
x /= r;
y /= r;
x1 = ND_coord_i(u).x; y1 = ND_coord_i(u).y;
r = MAX (ND_lw_i(u)+ND_rw_i(u), ND_ht_i(u) + GD_ranksep(u->graph));
x2 = x*r + ND_coord_i(u).x; y2 = y*r + ND_coord_i(u).y;
{
point curve[4];
curve[0].x = ROUND(x1); curve[0].y = ROUND(y1);
curve[1].x = ROUND((2*x1+x2)/3); curve[1].y = ROUND((2*y1+y2)/3);
curve[2].x = ROUND((2*x2+x1)/3); curve[2].y = ROUND((2*y2+y1)/3);
curve[3].x = ROUND(x2); curve[3].y = ROUND(y2);
shape_clip (u, curve, l->list[0]);
x1 = curve[0].x - ND_coord_i(u).x;
y1 = curve[0].y - ND_coord_i(u).y;
}
prt.p.x = ROUND(x1);
prt.p.y = ROUND(y1);
prt.bp = 0;
prt.order = (MC_SCALE * (ND_lw_i(u) + prt.p.x)) / (ND_lw_i(u) + ND_rw_i(u));
prt.constrained = FALSE;
prt.defined = TRUE;
if ((arr_prt.defined = arr_len && TRUE)) {
arr_prt.p.x = ROUND(x1 + x * arr_len);
arr_prt.p.y = ROUND(y1 + y * arr_len);
arr_prt.bp = 0;
arr_prt.constrained = FALSE;
arr_prt.order = (MC_SCALE * (ND_lw_i(u) + arr_prt.p.x)) / (ND_lw_i(u) + ND_rw_i(u));
if (u == l->list[0]->head) {
if (GD_rank(u->graph)[ND_rank(u)].ht2 < (ht = ABS(arr_prt.p.y)))
GD_rank(u->graph)[ND_rank(u)].ht2 = ht;
}
else {
if (GD_rank(u->graph)[ND_rank(u)].ht1 < (ht = ABS(arr_prt.p.y)))
GD_rank(u->graph)[ND_rank(u)].ht1 = ht;
}
}
for (i=0; i < l->size; i++) {
e = l->list[i];
arrow_flags (e, &sflag, &eflag);
#ifndef OLD
for ( ; e; e=ED_to_virt(e)) {
for (f=e; f;
f = ED_edge_type(f)==VIRTUAL &&
ND_node_type(f->head)==VIRTUAL &&
ND_out(f->head).size==1 ?
ND_out(f->head).list[0] : NULL)
{
if (f->head==u) ED_head_port(f) = prt;
if (f->tail==u) ED_tail_port(f) = prt;
}
for (f=e; f;
f = ED_edge_type(f)==VIRTUAL &&
ND_node_type(f->tail)==VIRTUAL &&
ND_in(f->tail).size==1 ?
ND_in(f->tail).list[0] : NULL)
{
if (f->head==u) ED_head_port(f) = prt;
if (f->tail==u) ED_tail_port(f) = prt;
}
}
#else
for ( ; e; e=ED_to_virt(e)) {
if (e->head==u) ED_head_port(e) =
arr_port.defined && !eflag ? arr_prt : prt;
if (e->tail==u) ED_tail_port(e) =
arr_port.defined && !sflag ? arr_prt : prt;
}
#endif
}
ND_has_port(u) = TRUE;
}