#pragma prototyped
/*
* Copyright (c) AT&T Corp. 1994, 1995.
* This code is licensed by AT&T Corp. For the
* terms and conditions of the license, see
* http://www.research.att.com/orgs/ssr/book/reuse
*/
/*
* set edge splines.
*/
#include "dot.h"
#define NSUB 9 /* number of subdivisions, re-aiming splines */
#define CHUNK 128 /* in building list of edges */
#define MINW 16 /* minimum width of a box in the edge path */
#define HALFMINW 8
#define REGULAREDGE 1
#define FLATEDGE 2
#define SELFWPEDGE 4
#define SELFNPEDGE 8
#define SELFEDGE 8
#define EDGETYPEMASK 15 /* the OR of the above */
#define FWDEDGE 16
#define BWDEDGE 32
#define EDGEDIRMASK 48 /* the OR of the above */
#define MAINGRAPH 64
#define AUXGRAPH 128
#define GRAPHTYPEMASK 192 /* the OR of the above */
#define TPORT(e) ((e)->attr[1])
#define HPORT(e) ((e)->attr[2])
#define MAKEFWDEDGE(new, old) { \
edge_t *newp; \
newp = new; \
*newp = *old; \
newp->tail = old->head; \
newp->head = old->tail; \
ED_tail_port(newp) = ED_head_port(old); \
ED_head_port(newp) = ED_tail_port(old); \
ED_edge_type(newp) = VIRTUAL; \
ED_to_orig(newp) = old; \
}
#define CCW -1 /* counter clock-wise */
#define CW 1 /* clock-wise */
#define ANYW 0 /* could go either way */
#define OTHERDIR(dir) ((dir == CCW) ? CW : CCW)
#define P2PF(p, pf) (pf.x = p.x, pf.y = p.y)
static int selfsidemap[16][3] = {
{ BOTTOM, BOTTOM, ANYW },
{ TOP, TOP, ANYW },
{ RIGHT, RIGHT, ANYW },
{ LEFT, LEFT, ANYW },
{ BOTTOM, LEFT, CCW },
{ LEFT, BOTTOM, CW },
{ TOP, RIGHT, CW },
{ RIGHT, TOP, CCW },
{ TOP, LEFT, CCW },
{ LEFT, TOP, CW },
{ BOTTOM, RIGHT, CCW },
{ RIGHT, BOTTOM, CW },
{ BOTTOM, TOP, CCW },
{ TOP, BOTTOM, CW },
{ LEFT, RIGHT, CCW },
{ RIGHT, LEFT, CW },
};
static int flatsidemap[16][6] = {
{ BOTTOM, BOTTOM, BOTTOM, CCW, CCW, FALSE },
{ TOP, TOP, TOP, CW, CW, FALSE },
{ RIGHT, LEFT, BOTTOM, CW, CW, TRUE },
{ BOTTOM, TOP, RIGHT, CCW, CW, TRUE },
{ TOP, BOTTOM, RIGHT, CW, CCW, TRUE },
{ RIGHT, TOP, RIGHT, CCW, CW, TRUE },
{ RIGHT, BOTTOM, RIGHT, CW, CCW, TRUE },
{ TOP, LEFT, TOP, CW, CCW, TRUE },
{ BOTTOM, LEFT, BOTTOM, CCW, CW, TRUE },
{ RIGHT, RIGHT, BOTTOM, CW, CCW, TRUE },
{ LEFT, LEFT, BOTTOM, CCW, CW, TRUE },
{ LEFT, BOTTOM, BOTTOM, CCW, CCW, FALSE },
{ TOP, RIGHT, TOP, CW, CW, FALSE },
{ LEFT, TOP, TOP, CW, CW, FALSE },
{ BOTTOM, RIGHT, BOTTOM, CCW, CCW, FALSE },
{ LEFT, RIGHT, BOTTOM, CCW, CCW, FALSE },
};
#define NEXTSIDE(side, dir) ((dir == CCW) ? \
((side & 0x8) ? BOTTOM : (side << 1)) : \
((side & 0x1) ? LEFT : (side >> 1)))
#define AVG(a, b) ((a + b) / 2)
typedef struct pathend_t {
box nb; /* the node box */
point np; /* node port */
int sidemask;
int boxn;
box boxes[20];
} pathend_t;
static path *P;
static int LeftBound, RightBound, /* FlatHeight, */ Splinesep, Multisep;
static box *Rank_box;
static point points[1000], points2[1000];
static int pointn;
static box boxes[1000];
static void add_box(box);
static void adjustregularpath(int, int);
static void adjustselfends(box *, box *, point, int, int);
static void beginpath(Agedge_t *, int, pathend_t *);
static Agedge_t *bot_bound(Agedge_t *, int);
static unsigned char pathscross(Agnode_t *, Agnode_t *, Agedge_t *, Agedge_t *);
static void chooseflatsides(pathend_t *, pathend_t *, int *, int *, int *, int *, int *, int *);
static void chooseselfsides(pathend_t *, pathend_t *, int *, int *, int *);
static Agraph_t *cl_bound(Agnode_t *, Agnode_t *);
static int cl_vninside(Agraph_t *, Agnode_t *);
static void completeflatpath(pathend_t *, pathend_t *, int, int, int, int, int, box*, box*, int, int);
static void completeregularpath(Agedge_t *, Agedge_t *, pathend_t *, pathend_t *, box *, int, int);
static void completeselfpath(pathend_t *, pathend_t *, int, int, int, int, int, int, int);
static double conc_slope(Agnode_t *);
static double dist(pointf, pointf);
static int edgecmp(Agedge_t **, Agedge_t **);
static void endpath(Agedge_t *, int, pathend_t *);
static Agedge_t *getmainedge(Agedge_t *);
static splines *getsplinepoints(Agedge_t *);
static box makeflatcomponent(box, box, int, int, int, int, int);
static void make_flat_edge(Agedge_t **, int, int);
static box makeflatend(box, int, int, box);
static void make_regular_edge(Agedge_t **, int, int);
static void makeregularend(box, int, int, box *);
static box makeselfcomponent(box, int, int, int, int, int);
static void make_self_edge(Agedge_t **, int, int);
static box makeselfend(box, int, int, int, int);
static box maximal_bbox(Agnode_t *, Agedge_t *, Agedge_t *);
static Agnode_t *neighbor(Agnode_t *, Agedge_t *, Agedge_t *, int);
static void place_vnlabel(Agnode_t *);
static void place_portlabel (edge_t *e, boolean head_p);
static box rank_box(Agraph_t *, int);
static void recover_slack(Agedge_t *, path *);
static void resize_vn(Agnode_t *, int, int, int);
static void setflags(Agedge_t *, int, int, int);
static int straight_len(Agnode_t *);
static Agedge_t *straight_path(Agedge_t *, int, point *, int *);
static Agedge_t *top_bound(Agedge_t *, int);
#define GROWEDGES (edges = ALLOC (n_edges + CHUNK, edges, edge_t*))
static boolean spline_merge(node_t* n)
{
return ((ND_node_type(n) == VIRTUAL) && ((ND_in(n).size > 1) || (ND_out(n).size > 1)));
}
static boolean swap_ends_p (edge_t *e)
{
while (ED_to_orig(e)) e = ED_to_orig(e);
if (ND_rank(e->head) > ND_rank(e->tail)) return FALSE;
if (ND_rank(e->head) < ND_rank(e->tail)) return TRUE;
if (ND_order(e->head) >= ND_order(e->tail)) return FALSE;
return TRUE;
}
static splineInfo sinfo = {swap_ends_p, spline_merge};
int portcmp(port_t p0, port_t p1)
{
int rv;
if (p1.defined == FALSE)
return (p0.defined ? 1 : 0);
if (p0.defined == FALSE)
return -1;
rv = p0.p.x - p1.p.x;
if (rv == 0) rv = p0.p.y - p1.p.y;
return rv;
}
/* swap_bezier:
*/
static void
swap_bezier (bezier* old, bezier *new)
{
point* list;
point* lp;
point* olp;
int i, sz;
sz = old->size;
list = N_GNEW(sz, point);
lp = list;
olp = old->list + (sz-1);
for (i = 0; i < sz; i++) { /* reverse list of points */
*lp++ = *olp--;
}
new->list = list;
new->size = sz;
new->sflag = old->eflag;
new->eflag = old->sflag;
new->sp = old->ep;
new->ep = old->sp;
}
/* swap_spline:
*/
static void
swap_spline (splines* s)
{
bezier* list;
bezier* lp;
bezier* olp;
int i, sz;
sz = s->size;
list = N_GNEW(sz, bezier);
lp = list;
olp = s->list + (sz-1);
for (i = 0; i < sz; i++) { /* reverse and swap list of beziers */
swap_bezier (olp--, lp++);
}
/* free old structures */
for (i = 0; i < sz; i++) free(s->list[i].list);
free(s->list);
s->list = list;
}
/* edge_normalize:
*/
static void
edge_normalize (graph_t* g)
{
edge_t* e;
node_t* n;
for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
for (e = agfstout(g,n); e; e = agnxtout(g,e)) {
if (swap_ends_p(e) && ED_spl(e)) swap_spline (ED_spl(e));
}
}
}
void dot_splines (graph_t* g)
{
int i, j, k, n_nodes, n_edges, ind, cnt;
node_t *n;
edge_t fwdedgea, fwdedgeb;
edge_t *e, *e0, *e1, *ea, *eb, *le0, *le1, **edges;
mark_lowclusters(g);
routesplinesinit();
P = NEW(path);
/* FlatHeight = 2 * GD_nodesep(g); */
Splinesep = GD_nodesep(g) / 4;
Multisep = GD_nodesep(g);
edges = N_NEW (CHUNK, edge_t*);
/* compute boundaries and list of splines */
LeftBound = RightBound = 0;
n_edges = n_nodes = 0;
for (i = GD_minrank(g); i <= GD_maxrank(g); i++) {
n_nodes += GD_rank(g)[i].n;
if ((n = GD_rank(g)[i].v[0]))
LeftBound = MIN (LeftBound, (ND_coord_i(n).x - ND_lw_i(n)));
if (GD_rank(g)[i].n && (n = GD_rank(g)[i].v[GD_rank(g)[i].n - 1]))
RightBound = MAX (RightBound, (ND_coord_i(n).x + ND_rw_i(n)));
for (j = 0; j < GD_rank(g)[i].n; j++) {
n = GD_rank(g)[i].v[j];
if ((ND_node_type(n) != NORMAL) &&
(spline_merge(n) == FALSE))
continue;
for (k = 0; (e = ND_out(n).list[k]); k++) {
if ((ED_edge_type(e) == FLATORDER) || (ED_edge_type(e) == IGNORED))
continue;
setflags (e, REGULAREDGE, FWDEDGE, MAINGRAPH);
edges[n_edges++] = e;
if (n_edges % CHUNK == 0)
GROWEDGES;
}
if (ND_flat_out(n).list)
for (k = 0; (e = ND_flat_out(n).list[k]); k++) {
setflags (e, FLATEDGE, 0, AUXGRAPH);
edges[n_edges++] = e;
if (n_edges % CHUNK == 0)
GROWEDGES;
}
if (ND_other(n).list)
for (k = 0; (e = ND_other(n).list[k]); k++) {
setflags (e, 0, 0, AUXGRAPH);
edges[n_edges++] = e;
if (n_edges % CHUNK == 0)
GROWEDGES;
}
}
}
qsort((char*) &edges[0], n_edges, sizeof (edges[0]), (qsort_cmpf)edgecmp);
/* FIXME: just how many boxes can there be? */
P->boxes = N_NEW (n_nodes + 20 * 2 * NSUB, box);
Rank_box = N_NEW (i, box);
for (i = 0; i < n_edges; ) {
ind = i;
le0 = getmainedge ((e0 = edges[i++]));
ea = (ED_tail_port(e0).defined || ED_head_port(e0).defined) ? e0 : le0;
if (ED_tree_index(ea) & BWDEDGE) {
MAKEFWDEDGE (&fwdedgea, ea);
ea = &fwdedgea;
}
for (cnt = 1; i < n_edges; cnt++, i++) {
if (le0 != (le1 = getmainedge ((e1 = edges[i]))))
break;
eb = (ED_tail_port(e1).defined || ED_head_port(e1).defined) ? e1 : le1;
if (ED_tree_index(eb) & BWDEDGE) {
MAKEFWDEDGE (&fwdedgeb, eb);
eb = &fwdedgeb;
}
if (portcmp(ED_tail_port(ea),ED_tail_port(eb))) break;
if (portcmp(ED_head_port(ea),ED_head_port(eb))) break;
if ((ED_tree_index(e0) & EDGETYPEMASK) == FLATEDGE && ED_label(e0) != ED_label(e1))
break;
if (ED_tree_index(edges[i]) & MAINGRAPH) /* Aha! -C is on */
break;
}
if (e0->tail == e0->head)
make_self_edge (edges, ind, cnt);
else if (ND_rank(e0->tail) == ND_rank(e0->head))
make_flat_edge (edges, ind, cnt);
else
make_regular_edge (edges, ind, cnt);
}
/* make the other splines and place labels */
for (n = GD_nlist(g); n; n = ND_next(n)) {
if ((ND_node_type(n) == VIRTUAL) && (ND_label(n))) place_vnlabel(n);
}
/* normalize splines so they always go from tail to head */
edge_normalize (g);
/* vladimir: place port labels */
if (E_headlabel || E_taillabel)
for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
if (E_headlabel) for (e = agfstin(g,n); e; e = agnxtin(g,e))
if (ED_head_label(e)) place_portlabel (e, TRUE);
if (E_taillabel) for (e = agfstout(g,n); e; e = agnxtout(g,e))
if (ED_tail_label(e)) place_portlabel (e, FALSE);
}
/* end vladimir */
free (edges);
free (P->boxes);
free (P);
free (Rank_box);
routesplinesterm ();
State = GVSPLINES;
}
/* compute position of an edge label from its virtual node */
static void place_vnlabel(node_t* n)
{
pointf dimen;
double width;
edge_t *e;
if (ND_in(n).size == 0) return; /* skip flat edge labels here */
for (e = ND_out(n).list[0]; ED_edge_type(e) != NORMAL; e = ED_to_orig(e));
dimen = ED_label(e)->dimen;
width = GD_left_to_right(n->graph)? dimen.y : dimen.x;
ED_label(e)->p.x = ND_coord_i(n).x + POINTS(width/2.0);
ED_label(e)->p.y = ND_coord_i(n).y;
}
/* vladimir */
static void place_portlabel (edge_t *e, boolean head_p)
/* place the {head,tail}label (depending on HEAD_P) of edge E */
/* edges are normalized, so tail is at spl->list[0].list[0]
* and head is at spl->list[spl->size-l].list[bez->size-1]
*/
{
textlabel_t *l;
splines *spl;
bezier *bez;
double dist, angle;
point p;
pointf c[4], pf;
int i;
if (ED_edge_type(e) == IGNORED) return;
l = head_p ? ED_head_label(e) : ED_tail_label(e);
spl = getsplinepoints(e);
if (!head_p) {
bez = &spl->list[0];
if (bez->sflag != ARR_NONE
&& bez->sflag != ARR_OPEN
&& bez->sflag != ARR_HALFOPEN) {
p = bez->sp;
P2PF(bez->list[0],pf);
}
else {
p = bez->list[0];
for (i=0; i<4; i++) P2PF(bez->list[i], c[i]);
pf = Bezier (c, 3, 0.1, NULL, NULL);
}
} else {
bez = &spl->list[spl->size-1];
if (bez->eflag != ARR_NONE
&& bez->eflag != ARR_OPEN
&& bez->eflag != ARR_HALFOPEN) {
p = bez->ep;
P2PF(bez->list[bez->size-1],pf);
}
else {
p = bez->list[bez->size-1];
for (i=0; i<4; i++) P2PF(bez->list[bez->size-4+i], c[i]);
pf = Bezier (c, 3, 0.9, NULL, NULL);
}
}
angle = atan2 (pf.y-p.y, pf.x-p.x) +
RADIANS(late_double(e,E_labelangle,PORT_LABEL_ANGLE,-180.0));
dist = PORT_LABEL_DISTANCE * late_double(e,E_labeldistance,1.0,0.0);
l->p.x = p.x + ROUND(dist * cos(angle));
l->p.y = p.y + ROUND(dist * sin(angle));
}
static void setflags (e, hint1, hint2, f3)
edge_t *e;
int hint1, hint2, f3;
{
int f1, f2;
if (hint1 != 0)
f1 = hint1;
else {
if (e->tail == e->head)
if (ED_tail_port(e).defined || ED_head_port(e).defined)
f1 = SELFWPEDGE;
else
f1 = SELFNPEDGE;
else if (ND_rank(e->tail) == ND_rank(e->head))
f1 = FLATEDGE;
else
f1 = REGULAREDGE;
}
if (hint2 != 0)
f2 = hint2;
else {
if (f1 == REGULAREDGE)
f2 = (ND_rank(e->tail) < ND_rank(e->head)) ?
FWDEDGE : BWDEDGE;
else if (f1 == FLATEDGE)
f2 = (ND_order(e->tail) < ND_order(e->head)) ?
FWDEDGE : BWDEDGE;
else /* f1 == SELF*EDGE */
f2 = FWDEDGE;
}
ED_tree_index(e) = (f1 | f2 | f3);
}
static int edgecmp (ptr0, ptr1)
edge_t **ptr0, **ptr1;
{
edge_t fwdedgea, fwdedgeb, *e0, *e1, *ea, *eb, *le0, *le1;
int et0, et1, v0, v1, rv;
e0 = (edge_t *) *ptr0;
e1 = (edge_t *) *ptr1;
et0 = ED_tree_index(e0) & EDGETYPEMASK;
et1 = ED_tree_index(e1) & EDGETYPEMASK;
if (et0 != et1)
return (et1 - et0);
le0 = getmainedge (e0);
le1 = getmainedge (e1);
v0 = ND_rank(le0->tail) - ND_rank(le0->head), v0 = ABS (v0);
v1 = ND_rank(le1->tail) - ND_rank(le1->head), v1 = ABS (v1);
if (v0 != v1)
return (v0 - v1);
v0 = ND_coord_i(le0->tail).x - ND_coord_i(le0->head).x, v0 = ABS (v0);
v1 = ND_coord_i(le1->tail).x - ND_coord_i(le1->head).x, v1 = ABS (v1);
if (v0 != v1)
return (v0 - v1);
if (le0->id != le1->id)
return (le0->id - le1->id);
ea = (ED_tail_port(e0).defined || ED_head_port(e0).defined) ? e0 : le0;
if (ED_tree_index(ea) & BWDEDGE) {
MAKEFWDEDGE (&fwdedgea, ea);
ea = &fwdedgea;
}
eb = (ED_tail_port(e1).defined || ED_head_port(e1).defined) ? e1 : le1;
if (ED_tree_index(eb) & BWDEDGE) {
MAKEFWDEDGE (&fwdedgeb, eb);
eb = &fwdedgeb;
}
if ((rv = portcmp(ED_tail_port(ea),ED_tail_port(eb)))) return rv;
if ((rv = portcmp(ED_head_port(ea),ED_head_port(eb)))) return rv;
v0 = ED_tree_index(e0) & GRAPHTYPEMASK;
v1 = ED_tree_index(e1) & GRAPHTYPEMASK;
if (v0 != v1)
return (v0 - v1);
if (et0 == FLATEDGE && ED_label(e0) != ED_label(e1))
return (int) (ED_label(e0) - ED_label(e1));
return (e0->id - e1->id);
}
static void make_self_edge (edges, ind, cnt)
edge_t **edges;
int ind, cnt;
{
node_t *n;
edge_t *e;
point *ps, np;
pathend_t tend, hend;
int i, j, maxx, stepx, stepy, dx, dy, tside, hside, dir, pn;
double width, height;
e = edges[ind];
n = e->tail;
i = ND_rw_i(n), ND_rw_i(n) = ND_mval(n), ND_mval(n) = i; /* recover original size */
/* self edge without ports */
if ((!ED_tail_port(e).defined) && (!ED_head_port(e).defined)) {
stepx = Multisep, stepy = (ND_ht_i(n) / 2) / cnt;
pointn = 0;
np = ND_coord_i(n);
dx = ND_rw_i(n), dy = 0;
for (i = 0; i < cnt; i++) {
e = edges[ind++];
dx += stepx, dy += stepy;
pointn = 0;
points[pointn++] = np;
points[pointn++] = pointof (np.x + dx / 3, np.y - dy);
points[pointn++] = pointof (np.x + dx, np.y - dy);
points[pointn++] = pointof (np.x + dx, np.y);
points[pointn++] = pointof (np.x + dx, np.y + dy);
points[pointn++] = pointof (np.x + dx / 3, np.y + dy);
points[pointn++] = np;
if (ED_label(e)) {
if (GD_left_to_right(e->tail->graph)) {
width = ED_label(e)->dimen.y;
height = ED_label(e)->dimen.x;
} else {
width = ED_label(e)->dimen.x;
height = ED_label(e)->dimen.y;
}
ED_label(e)->p.x = ND_coord_i(n).x + dx + POINTS(width/2.0);
ED_label(e)->p.y = ND_coord_i(n).y;
if (POINTS (width) > stepx)
dx += POINTS(width) - stepx;
if (dy + stepy < POINTS(height))
dy += (POINTS(height) - stepy);
}
clip_and_install (e, e, points, pointn, &sinfo);
}
return;
}
/* self edge with ports */
tend.nb = boxof (ND_coord_i(n).x - ND_lw_i(n), ND_coord_i(n).y - ND_ht_i(n) / 2,
ND_coord_i(n).x + ND_rw_i(n), ND_coord_i(n).y + ND_ht_i(n) / 2);
hend.nb = tend.nb;
stepx = Multisep, stepy = Multisep / 2;
dx = 0, dy = 0;
for (i = 0; i < cnt; i++) {
e = edges[ind++];
dx += stepx, dy += stepy;
/* tail setup */
beginpath (e, SELFEDGE, &tend);
/* head setup */
endpath (e, SELFEDGE, &hend);
chooseselfsides (&tend, &hend, &tside, &hside, &dir);
completeselfpath (&tend, &hend, tside, hside, dir,
dx, dy, Multisep, Multisep);
ps = routesplines (P, &pn);
if (pn == 0) return; /* will result in a lost edge */
if (ED_label(e)) {
/* FIXME: labels only right for BOTTOM -> TOP edges */
for (j = 0, maxx = ND_coord_i(n).x; j < P->nbox; j++)
if (P->boxes[j].UR.x > maxx)
maxx = P->boxes[j].UR.x;
if (GD_left_to_right(e->tail->graph))
width = ED_label(e)->dimen.y;
else
width = ED_label(e)->dimen.x;
ED_label(e)->p.x = maxx + POINTS(width/2.0);
ED_label(e)->p.y = ND_coord_i(n).y;
if (POINTS (width) > stepx)
dx += POINTS (width) - stepx;
}
clip_and_install (e, e, ps, pn, &sinfo);
}
}
static void make_flat_edge (edges, ind, cnt)
edge_t **edges;
int ind, cnt;
{
node_t *tn, *hn, *n;
edge_t fwdedge, *e;
int i, stepx, stepy, /* dx, */ dy, ht1,ht2;
int tside, hside, mside, tdir, hdir, cross, pn;
point *ps;
point tp, hp;
pathend_t tend, hend;
box lb, rb, wlb, wrb;
rank_t *rank;
/* dx = 0; */
e = edges[ind];
if (ED_tree_index(e) & BWDEDGE) {
MAKEFWDEDGE (&fwdedge, e);
e = &fwdedge;
}
tn = e->tail, hn = e->head;
/* flat edge without ports that can go straight left to right */
if (ED_label(e)) {
edge_t *f;
for (f = ED_to_virt(e); ED_to_virt(f); f = ED_to_virt(f));
ED_label(e)->p = ND_coord_i(f->tail);
}
if ((!ED_tail_port(e).defined) && (!ED_head_port(e).defined)) {
rank = &(GD_rank(tn->graph)[ND_rank(tn)]);
for (i = ND_order(tn) + 1; i < ND_order(hn); i++) {
n = rank->v[i];
if ((ND_node_type(n) == VIRTUAL && ND_label(n)) ||
ND_node_type(n) == NORMAL)
break;
}
if (i != ND_order(hn))
goto flatnostraight;
stepy = (cnt > 1) ? ND_ht_i(tn) / (cnt - 1) : 0;
tp = ND_coord_i(tn);
hp = ND_coord_i(hn);
dy = tp.y - ((cnt > 1) ? ND_ht_i(tn) / 2 : 0);
for (i = 0; i < cnt; i++) {
e = edges[ind + i];
pointn = 0;
points[pointn++] = tp;
points[pointn++] = pointof ((2 * tp.x + hp.x) / 3, dy);
points[pointn++] = pointof ((2 * hp.x + tp.x) / 3, dy);
points[pointn++] = hp;
#ifdef OBSOLETE
if (ED_label(e)) { /* FIXME: fix label positioning */
labelw = ED_label(e)->dimen.x * 72;
ED_label(e)->p = pointof (tp.x + labelw / 2, tp.y);
/* dx += labelw;*/
}
#endif
dy += stepy;
clip_and_install (e, e, points, pointn, &sinfo);
}
return;
}
/* !(flat edge without ports that can go straight left to right) */
flatnostraight:
tend.nb = boxof (ND_coord_i(tn).x - ND_lw_i(tn), ND_coord_i(tn).y - ND_ht_i(tn) / 2,
ND_coord_i(tn).x + ND_rw_i(tn), ND_coord_i(tn).y + ND_ht_i(tn) / 2);
hend.nb = boxof (ND_coord_i(hn).x - ND_lw_i(hn), ND_coord_i(hn).y - ND_ht_i(hn) / 2,
ND_coord_i(hn).x + ND_rw_i(hn), ND_coord_i(hn).y + ND_ht_i(hn) / 2);
ht1 = GD_rank(tn->graph)[ND_rank(tn)].pht1;
ht2 = GD_rank(tn->graph)[ND_rank(tn)].pht2;
stepx = Multisep / cnt, stepy = ht2 / cnt;
lb = boxof (ND_coord_i(tn).x - ND_lw_i(tn), ND_coord_i(tn).y - ht1,
ND_coord_i(tn).x + ND_rw_i(tn), ND_coord_i(tn).y + ht2);
rb = boxof (ND_coord_i(hn).x - ND_lw_i(hn), ND_coord_i(hn).y - ht1,
ND_coord_i(hn).x + ND_rw_i(hn), ND_coord_i(hn).y + ht2);
for (i = 0; i < cnt; i++) {
e = edges[ind + i];
if (ED_tree_index(e) & BWDEDGE) {
MAKEFWDEDGE (&fwdedge, e);
e = &fwdedge;
}
/* tail setup */
beginpath (e, FLATEDGE, &tend);
/* head setup */
endpath (e, FLATEDGE, &hend);
chooseflatsides (&tend, &hend, &tside, &hside, &mside,
&tdir, &hdir, &cross);
if (ED_label(e)) { /* edges with labels aren't multi-edges */
edge_t *le;
node_t *ln;
for (le = e; ED_to_virt(le); le = ED_to_virt(le))
;
ln = le->tail;
wlb.LL.x = lb.LL.x;
wlb.LL.y = lb.LL.y;
wlb.UR.x = lb.UR.x;
wlb.UR.y = ND_coord_i(ln).y - ND_ht_i(ln) / 2;
wrb.LL.x = rb.LL.x;
wrb.LL.y = rb.LL.y;
wrb.UR.x = rb.UR.x;
wrb.UR.y = ND_coord_i(ln).y - ND_ht_i(ln) / 2;
} else {
wlb.LL.x = lb.LL.x - (i + 1) * stepx;
wlb.LL.y = lb.LL.y - (i + 1) * stepy;
wlb.UR.x = lb.UR.x + (i + 1) * stepx;
wlb.UR.y = lb.UR.y + (i + 1) * stepy;
if (cross) {
wrb.LL.x = rb.LL.x - (cnt - i) * stepx;
wrb.LL.y = rb.LL.y - (cnt - i) * stepy;
wrb.UR.x = rb.UR.x + (cnt - i) * stepx;
wrb.UR.y = rb.UR.y + (cnt - i) * stepy;
} else {
wrb.LL.x = rb.LL.x - (i + 1) * stepx;
wrb.LL.y = rb.LL.y - (i + 1) * stepy;
wrb.UR.x = rb.UR.x + (i + 1) * stepx;
wrb.UR.y = rb.UR.y + (i + 1) * stepy;
}
}
completeflatpath (&tend, &hend, tside, hside, mside,
tdir, hdir, &wlb, &wrb, stepx, stepy);
ps = routesplines (P, &pn);
if (pn == 0) return;
clip_and_install (e, e, ps, pn, &sinfo);
}
}
static void make_regular_edge (edges, ind, cnt)
edge_t **edges;
int ind, cnt;
{
graph_t *g;
node_t *tn, *hn;
edge_t fwdedgea, fwdedgeb, fwdedge, *e, *fe, *le, *segfirst;
point *ps;
pathend_t tend, hend;
box b;
int boxn, sl, si, smode, i, j, dx, pn, hackflag, longedge;
sl = 0;
e = edges[ind];
hackflag = FALSE;
if (ABS (ND_rank(e->tail) - ND_rank(e->head)) > 1) {
fwdedgea = *e;
if (ED_tree_index(e) & BWDEDGE) {
MAKEFWDEDGE (&fwdedgeb, e);
fwdedgea.tail = e->head;
fwdedgea.u.tail_port = ED_head_port(e);
} else {
fwdedgeb = *e;
fwdedgea.tail = e->tail;
}
le = getmainedge (e);
while (ED_to_virt(le)) le = ED_to_virt(le);
fwdedgea.head = le->head;
fwdedgea.u.head_port.defined = FALSE;
fwdedgea.u.edge_type = VIRTUAL;
fwdedgea.u.head_port.p.x = fwdedgea.u.head_port.p.y = 0;
fwdedgea.u.to_orig = e;
e = &fwdedgea;
hackflag = TRUE;
} else {
if (ED_tree_index(e) & BWDEDGE) {
MAKEFWDEDGE (&fwdedgea, e);
e = &fwdedgea;
}
}
fe = e;
/* compute the spline points for the edge */
boxn = 0;
pointn = 0;
segfirst = e;
g = e->tail->graph;
tn = e->tail;
hn = e->head;
tend.nb = maximal_bbox (tn, NULL, e);
beginpath (e, REGULAREDGE, &tend);
makeregularend (tend.boxes[tend.boxn - 1], BOTTOM,
ND_coord_i(tn).y - GD_rank(tn->graph)[ND_rank(tn)].ht1, &b);
if (b.LL.x < b.UR.x && b.LL.y < b.UR.y)
tend.boxes[tend.boxn++] = b;
longedge = 0;
smode = FALSE, si = -1;
while (ND_node_type(hn) == VIRTUAL && !spline_merge (hn)) {
longedge = 1;
boxes[boxn++] = rank_box (g, ND_rank(tn));
if (!smode && ((sl = straight_len (hn)) >= (GD_has_edge_labels(g) ? 4 + 1 : 2 +1))) {
smode = TRUE;
si = 1, sl -= 2;
}
if (!smode || si > 0) {
si--;
boxes[boxn++] = maximal_bbox (hn, e, ND_out(hn).list[0]);
e = ND_out(hn).list[0];
tn = e->tail;
hn = e->head;
continue;
}
hend.nb = maximal_bbox (hn, e, ND_out(hn).list[0]);
endpath (e, REGULAREDGE, &hend);
makeregularend (hend.boxes[hend.boxn - 1], TOP,
ND_coord_i(hn).y + GD_rank(hn->graph)[ND_rank(hn)].ht2, &b);
if (b.LL.x < b.UR.x && b.LL.y < b.UR.y)
hend.boxes[hend.boxn++] = b;
P->end.theta = PI / 2, P->end.constrained = TRUE;
completeregularpath (segfirst, e, &tend, &hend, boxes, boxn, 1);
ps = routesplines (P, &pn);
if (pn == 0) return;
for (i = 0; i < pn; i++)
points[pointn++] = ps[i];
e = straight_path (ND_out(hn).list[0], sl, points, &pointn);
recover_slack (segfirst, P);
segfirst = e;
tn = e->tail;
hn = e->head;
boxn = 0;
tend.nb = maximal_bbox (tn, ND_in(tn).list[0], e);
beginpath (e, REGULAREDGE, &tend);
makeregularend (tend.boxes[tend.boxn - 1], BOTTOM,
ND_coord_i(tn).y - GD_rank(tn->graph)[ND_rank(tn)].ht1, &b);
if (b.LL.x < b.UR.x && b.LL.y < b.UR.y)
tend.boxes[tend.boxn++] = b;
P->start.theta = - PI / 2, P->start.constrained = TRUE;
smode = FALSE;
}
boxes[boxn++] = rank_box (g, ND_rank(tn));
hend.nb = maximal_bbox (hn, e, NULL);
endpath (hackflag ? &fwdedgeb : e, REGULAREDGE, &hend);
makeregularend (hend.boxes[hend.boxn - 1], TOP,
ND_coord_i(hn).y + GD_rank(hn->graph)[ND_rank(hn)].ht2, &b);
if (b.LL.x < b.UR.x && b.LL.y < b.UR.y)
hend.boxes[hend.boxn++] = b;
completeregularpath (segfirst, e, &tend, &hend, boxes, boxn, longedge);
ps = routesplines (P, &pn);
if (pn == 0) return;
for (i = 0; i < pn; i++)
points[pointn++] = ps[i];
recover_slack (segfirst, P);
/* make copies of the spline points, one per multi-edge */
if (cnt == 1) {
clip_and_install (fe, hackflag ? &fwdedgeb : e, points, pointn, &sinfo);
return;
}
dx = Multisep * (cnt - 1) / 2;
for (i = 1; i < pointn - 1; i++)
points[i].x -= dx;
for (i = 0; i < pointn; i++)
points2[i] = points[i];
clip_and_install (fe, hackflag ? &fwdedgeb : e, points2, pointn, &sinfo);
for (j = 1; j < cnt; j++) {
e = edges[ind + j];
if (ED_tree_index(e) & BWDEDGE) {
MAKEFWDEDGE (&fwdedge, e);
e = &fwdedge;
}
for (i = 1; i < pointn - 1; i++)
points[i].x += Multisep;
for (i = 0; i < pointn; i++)
points2[i] = points[i];
clip_and_install (e, e, points2, pointn, &sinfo);
}
}
/* self edges */
static void chooseselfsides (tendp, hendp, tsidep, hsidep, dirp)
pathend_t *tendp, *hendp;
int *tsidep, *hsidep, *dirp;
{
int i;
for (i = 0; i < 16; i++)
if ((selfsidemap[i][0] & tendp->sidemask) &&
(selfsidemap[i][1] & hendp->sidemask))
break;
if (i == 16)
abort ();
*tsidep = selfsidemap[i][0], *hsidep = selfsidemap[i][1];
*dirp = selfsidemap[i][2];
if (*dirp == ANYW) { /* ANYW can appear when tside == hside */
switch (*tsidep) {
case BOTTOM:
*dirp = (tendp->np.x < hendp->np.x) ? CCW : CW;
break;
case RIGHT:
*dirp = (tendp->np.y < hendp->np.y) ? CCW : CW;
break;
case TOP:
*dirp = (tendp->np.x > hendp->np.x) ? CCW : CW;
break;
case LEFT:
*dirp = (tendp->np.y > hendp->np.y) ? CCW : CW;
break;
}
}
}
static void completeselfpath (tendp, hendp, tside, hside, dir, dx, dy, w, h)
pathend_t *tendp, *hendp;
int tside, hside, dir, dx, dy, w, h;
{
int i, side;
box boxes[4]; /* can't have more than 6 boxes */
box tb, hb;
int boxn;
tb = makeselfend (tendp->boxes[tendp->boxn - 1],
tside, dir, dx, dy);
hb = makeselfend (hendp->boxes[hendp->boxn - 1],
hside, OTHERDIR (dir), dx, dy);
if (tside == hside && tendp->np.x == hendp->np.x &&
tendp->np.y == hendp->np.y)
adjustselfends (&tb, &hb, tendp->np, tside, dir);
boxn = 0;
for (side = tside; ; side = NEXTSIDE (side, dir)) {
boxes[boxn++] = makeselfcomponent (tendp->nb, side, dx, dy, w, h);
if (side == hside)
break;
}
for (i = 0; i < tendp->boxn; i++)
add_box (tendp->boxes[i]);
add_box (tb);
for (i = 0; i < boxn; i++)
add_box (boxes[i]);
add_box (hb);
for (i = hendp->boxn - 1; i >= 0 ; i--)
add_box (hendp->boxes[i]);
}
static box makeselfend (b, side, dir, dx, dy)
box b;
int side, dir, dx, dy;
{
box eb;
switch (side) {
case BOTTOM:
eb = boxof (b.LL.x, b.LL.y - dy, b.UR.x, b.LL.y);
(dir == CCW) ? (eb.UR.x += dx / 2) : (eb.LL.x -= dx / 2);
break;
case RIGHT:
eb = boxof (b.UR.x, b.LL.y, b.UR.x + dx, b.UR.y);
(dir == CCW) ? (eb.UR.y += dy / 2) : (eb.LL.y -= dy / 2);
break;
case TOP:
eb = boxof (b.LL.x, b.UR.y, b.UR.x, b.UR.y + dy);
(dir == CCW) ? (eb.LL.x -= dx / 2) : (eb.UR.x += dx / 2);
break;
case LEFT:
eb = boxof (b.LL.x - dx, b.LL.y, b.LL.x, b.UR.y);
(dir == CCW) ? (eb.LL.y -= dy / 2) : (eb.UR.y += dy / 2);
break;
}
return eb;
}
static box makeselfcomponent (nb, side, dx, dy, w, h)
box nb;
int side, dx, dy, w, h;
{
box b;
switch (side) {
case BOTTOM:
b.LL.x = nb.LL.x - dx - w, b.LL.y = nb.LL.y - dy - h;
b.UR.x = nb.UR.x + dx + w, b.UR.y = b.LL.y + h;
break;
case RIGHT:
b.LL.x = nb.UR.x + dx, b.LL.y = nb.LL.y - dy;
b.UR.x = b.LL.x + w, b.UR.y = nb.UR.y + dy;
break;
case TOP:
b.LL.x = nb.LL.x - dx - w, b.LL.y = nb.UR.y + dy;
b.UR.x = nb.UR.x + dx + w, b.UR.y = b.LL.y + h;
break;
case LEFT:
b.LL.x = nb.LL.x - dx - w, b.LL.y = nb.LL.y - dy;
b.UR.x = b.LL.x + w, b.UR.y = nb.UR.y + dy;
break;
}
return b;
}
static void adjustselfends (tbp, hbp, p, side, dir)
box *tbp, *hbp;
point p;
int side, dir;
{
switch (side) {
case BOTTOM:
if (dir == CCW) {
tbp->LL.x -= (tbp->UR.x - p.x), tbp->UR.x = p.x;
hbp->UR.x += (p.x - hbp->LL.x), hbp->LL.x = p.x;
} else {
tbp->UR.x -= (tbp->LL.x - p.x), tbp->LL.x = p.x;
hbp->LL.x += (p.x - hbp->UR.x), hbp->UR.x = p.x;
}
break;
case RIGHT:
if (dir == CCW) {
tbp->LL.y -= (tbp->UR.y - p.y), tbp->UR.y = p.y;
hbp->UR.y += (p.y - hbp->LL.y), hbp->LL.y = p.y;
} else {
tbp->UR.y -= (tbp->LL.y - p.y), tbp->LL.y = p.y;
hbp->LL.y += (p.y - hbp->UR.y), hbp->UR.y = p.y;
}
break;
case TOP:
if (dir == CW) {
tbp->LL.x -= (tbp->UR.x - p.x), tbp->UR.x = p.x;
hbp->UR.x += (p.x - hbp->LL.x), hbp->LL.x = p.x;
} else {
tbp->UR.x -= (tbp->LL.x - p.x), tbp->LL.x = p.x;
hbp->LL.x += (p.x - hbp->UR.x), hbp->UR.x = p.x;
}
break;
case LEFT:
if (dir == CW) {
tbp->LL.y -= (tbp->UR.y - p.y), tbp->UR.y = p.y;
hbp->UR.y += (p.y - hbp->LL.y), hbp->LL.y = p.y;
} else {
tbp->UR.y -= (tbp->LL.y - p.y), tbp->LL.y = p.y;
hbp->LL.y += (p.y - hbp->UR.y), hbp->UR.y = p.y;
}
break;
}
}
/* flat edges */
static void chooseflatsides (tendp, hendp,
tsidep, hsidep, msidep, tdirp, hdirp, crossp)
pathend_t *tendp, *hendp;
int *tsidep, *hsidep, *tdirp, *hdirp, *msidep, *crossp;
{
int i;
for (i = 0; i < 16; i++)
if ((flatsidemap[i][0] & tendp->sidemask) &&
(flatsidemap[i][1] & hendp->sidemask))
break;
if (i == 16)
abort ();
*tsidep = flatsidemap[i][0], *hsidep = flatsidemap[i][1];
*msidep = flatsidemap[i][2];
*tdirp = flatsidemap[i][3], *hdirp = flatsidemap[i][4];
*crossp = flatsidemap[i][5];
}
static void completeflatpath (tendp, hendp, tside, hside, mside,
tdir, hdir, arg_lb, arg_rb, w, h)
pathend_t *tendp, *hendp;
int tside, hside, mside, tdir, hdir;
box *arg_lb, *arg_rb;
int w, h;
{
int i, side, boxn;
box boxes[8];
box tb, hb;
box lb, rb;
lb = *arg_lb; rb = *arg_rb;
tb = makeflatend (tendp->boxes[tendp->boxn - 1], tside, tdir, lb);
hb = makeflatend (hendp->boxes[hendp->boxn - 1], hside, OTHERDIR (hdir), rb);
boxn = 0;
for (side = tside; ; side = NEXTSIDE (side, tdir)) {
boxes[boxn++] = makeflatcomponent (lb, rb, side,
(side == mside) ? 0 : -1, tdir, w, h);
if (side == mside)
break;
}
if (mside == RIGHT)
mside = LEFT;
if (mside != hside) {
for (side = NEXTSIDE (mside, hdir); ; side = NEXTSIDE (side, hdir)) {
boxes[boxn++] = makeflatcomponent (lb, rb,
side, 1, hdir, w, h);
if (side == hside)
break;
}
}
for (i = 0; i < tendp->boxn; i++)
add_box (tendp->boxes[i]);
if (tb.LL.x != tb.UR.x && tb.LL.y != tb.UR.y)
add_box (tb);
for (i = 0; i < boxn; i++)
add_box (boxes[i]);
if (hb.LL.x != hb.UR.x && hb.LL.y != hb.UR.y)
add_box (hb);
for (i = hendp->boxn - 1; i >= 0 ; i--)
add_box (hendp->boxes[i]);
}
static box makeflatend (b, side, dir, bb)
box b;
int side, dir;
box bb;
{
box eb;
switch (side) {
case BOTTOM:
eb = boxof (b.LL.x, bb.LL.y, b.UR.x, b.LL.y);
if (dir == CCW)
eb.UR.x += (bb.UR.x - b.UR.x) / 2;
else
eb.LL.x -= (b.LL.x - bb.LL.x) / 2;
break;
case RIGHT:
eb = boxof (b.UR.x, b.LL.y, bb.UR.x, b.UR.y);
if (dir == CCW)
eb.UR.y += (bb.UR.y - b.UR.y) / 2;
else
eb.LL.y -= (b.LL.y - bb.LL.y) / 2;
break;
case TOP:
eb = boxof (b.LL.x, b.UR.y, b.UR.x, bb.UR.y);
if (dir == CCW)
eb.LL.x -= (b.LL.x - bb.LL.x) / 2;
else
eb.UR.x += (bb.UR.x - b.UR.x) / 2;
break;
case LEFT:
eb = boxof (bb.LL.x, b.LL.y, b.LL.x, b.UR.y);
if (dir == CCW)
eb.LL.y -= (bb.UR.y - b.UR.y) / 2;
else
eb.UR.y += (b.LL.y - bb.LL.y) / 2;
break;
}
return eb;
}
static box makeflatcomponent (lb, rb, side, mode, dir, w, h)
box lb, rb;
int side, mode, dir, w, h;
{
box b;
/* mode == -1 means use left box, 1 means use right box
and 0 means use mostly the left box */
switch (side) {
case BOTTOM:
b.LL.x = lb.LL.x - w, b.UR.x = rb.UR.x + w;
if (mode <= 0)
b.LL.y = lb.LL.y - h, b.UR.y = lb.LL.y;
else
b.LL.y = rb.LL.y - h, b.UR.y = rb.LL.y;
break;
case RIGHT:
if (mode == -1) {
b.LL.x = lb.UR.x, b.UR.x = lb.UR.x + w;
b.LL.y = lb.LL.y, b.UR.y = lb.UR.y;
} else if (mode == 0) {
b.LL.x = lb.UR.x, b.UR.x = lb.UR.x + w;
if (dir == CCW)
b.LL.y = lb.LL.y, b.UR.y = rb.UR.y;
else
b.LL.y = rb.LL.y, b.UR.y = lb.UR.y;
} else {
b.LL.x = rb.UR.x, b.UR.x = rb.UR.x + w;
b.LL.y = rb.LL.y, b.UR.y = rb.UR.y;
}
break;
case TOP:
b.LL.x = lb.LL.x - w, b.UR.x = rb.UR.x + w;
if (mode <= 0)
b.LL.y = lb.UR.y, b.UR.y = lb.UR.y + h;
else
b.LL.y = rb.UR.y, b.UR.y = rb.UR.y + h;
break;
case LEFT:
if (mode == -1) {
b.LL.x = lb.LL.x - w, b.UR.x = lb.LL.x;
b.LL.y = lb.LL.y, b.UR.y = lb.UR.y;
} else if (mode == 0) {
b.LL.x = lb.LL.x - w, b.UR.x = lb.LL.x;
if (dir == CCW)
b.LL.y = lb.LL.y, b.UR.y = rb.UR.y;
else
b.LL.y = rb.LL.y, b.UR.y = lb.UR.y;
} else {
b.LL.x = rb.LL.x - w, b.UR.x = rb.LL.x;
b.LL.y = rb.LL.y, b.UR.y = rb.UR.y;
}
break;
}
return b;
}
/* regular edges */
#ifdef DONT_WANT_ANY_ENDPOINT_PATH_REFINEMENT
static void completeregularpath (first, last, tendp, hendp, boxes, boxn, flag)
edge_t *first, *last;
pathend_t *tendp, *hendp;
box *boxes;
int boxn, flag;
{
edge_t *uleft, *uright, *lleft, *lright;
int i, fb, lb;
splines *spl;
point *pp;
int pn;
fb = lb = -1;
uleft = uright = NULL;
uleft = top_bound (first, -1), uright = top_bound (first, 1);
if (uleft) {
spl = getsplinepoints (uleft);
pp = spl->list[0].list, pn = spl->list[0].size;
P->ulpp = &pp[0];
}
if (uright) {
spl = getsplinepoints (uright);
pp = spl->list[0].list, pn = spl->list[0].size;
P->urpp = &pp[0];
}
lleft = lright = NULL;
lleft = bot_bound (last, -1), lright = bot_bound (last, 1);
if (lleft) {
spl = getsplinepoints (lleft);
pp = spl->list[spl->size - 1].list, pn = spl->list[spl->size - 1].size;
P->llpp = &pp[pn - 1];
}
if (lright) {
spl = getsplinepoints (lright);
pp = spl->list[spl->size - 1].list, pn = spl->list[spl->size - 1].size;
P->lrpp = &pp[pn - 1];
}
for (i = 0; i < tendp->boxn; i++)
add_box (tendp->boxes[i]);
fb = P->nbox + 1; lb = fb + boxn - 3;
for (i = 0; i < boxn; i++)
add_box (boxes[i]);
for (i = hendp->boxn - 1; i >= 0 ; i--)
add_box (hendp->boxes[i]);
adjustregularpath (fb, lb);
}
#else
void refineregularends (edge_t *left, edge_t *right, pathend_t *endp, int dir, box b, box *boxes, int *boxnp);
/* box subdivision is obsolete, I think... ek */
static void completeregularpath (first, last, tendp, hendp, boxes, boxn, flag)
edge_t *first, *last;
pathend_t *tendp, *hendp;
box *boxes;
int boxn, flag;
{
edge_t *uleft, *uright, *lleft, *lright;
box uboxes[NSUB], lboxes[NSUB];
box b;
int uboxn, lboxn, i, y, fb, lb;
fb = lb = -1;
uleft = uright = NULL;
if (flag || ND_rank(first->tail) + 1 != ND_rank(last->head))
uleft = top_bound (first, -1), uright = top_bound (first, 1);
refineregularends (uleft, uright, tendp, 1, boxes[0], uboxes, &uboxn);
lleft = lright = NULL;
if (flag || ND_rank(first->tail) + 1 != ND_rank(last->head))
lleft = bot_bound (last, -1), lright = bot_bound (last, 1);
refineregularends (lleft, lright, hendp, -1, boxes[boxn - 1], lboxes, &lboxn);
for (i = 0; i < tendp->boxn; i++)
add_box (tendp->boxes[i]);
if (ND_rank(first->tail) + 1 == ND_rank(last->head)) {
if ((!uleft && !uright) && (lleft || lright)) {
b = boxes[0];
y = b.UR.y - b.LL.y;
for (i = 0; i < NSUB; i++) {
uboxes[i] = b;
uboxes[i].UR.y = b.UR.y - y * i / NSUB;
uboxes[i].LL.y = b.UR.y - y * (i + 1) / NSUB;
}
uboxn = NSUB;
} else if ((uleft || uright) && (!lleft && !lright)) {
b = boxes[boxn - 1];
y = b.UR.y - b.LL.y;
for (i = 0; i < NSUB; i++) {
lboxes[i] = b;
lboxes[i].UR.y = b.UR.y - y * i / NSUB;
lboxes[i].LL.y = b.UR.y - y * (i + 1) / NSUB;
}
lboxn = NSUB;
}
for (i = 0; i < uboxn; i++) {
uboxes[i].LL.x = MAX (uboxes[i].LL.x, lboxes[i].LL.x);
uboxes[i].UR.x = MIN (uboxes[i].UR.x, lboxes[i].UR.x);
}
for (i = 0; i < uboxn; i++)
add_box (uboxes[i]);
} else {
for (i = 0; i < uboxn; i++)
add_box (uboxes[i]);
fb = P->nbox; lb = fb + boxn - 3;
for (i = 1; i < boxn - 1; i++)
add_box (boxes[i]);
for (i = 0; i < lboxn; i++)
add_box (lboxes[i]);
}
for (i = hendp->boxn - 1; i >= 0 ; i--)
add_box (hendp->boxes[i]);
adjustregularpath (fb, lb);
}
#endif
/* for now, regular edges always go from top to bottom */
static void makeregularend (box b, int side, int y, box* bp)
{
switch (side) {
case BOTTOM:
*bp = boxof (b.LL.x, y, b.UR.x, b.LL.y);
break;
case TOP:
*bp = boxof (b.LL.x, b.UR.y, b.UR.x, y);
break;
}
}
#ifndef DONT_WANT_ANY_ENDPOINT_PATH_REFINEMENT
void refineregularends (left, right, endp, dir, b, boxes, boxnp)
edge_t *left, *right;
pathend_t *endp;
int dir;
box b;
box *boxes;
int *boxnp;
{
splines *lspls, *rspls;
point pp, cp;
box eb;
box *bp;
int y, i, j, k;
int nsub;
y = b.UR.y - b.LL.y;
if ((y == 1) || (!left && !right)) {
boxes[0] = b;
*boxnp = 1;
return;
}
nsub = MIN(NSUB,y);
for (i = 0; i < nsub; i++) {
boxes[i] = b;
boxes[i].UR.y = b.UR.y - y * i / nsub;
boxes[i].LL.y = b.UR.y - y * (i + 1) / nsub;
if (boxes[i].UR.y == boxes[i].LL.y) abort();
}
*boxnp = nsub;
/* only break big boxes */
for (j = 0; j < endp->boxn; j++) {
eb = endp->boxes[j];
y = eb.UR.y - eb.LL.y;
#ifdef STEVE_AND_LEFTY_GRASPING_AT_STRAWS
if (y < 15) continue;
#else
if (y < nsub) continue;
#endif
for (k = endp->boxn - 1; k > j; k--)
endp->boxes[k + (nsub - 1)] = endp->boxes[k];
for (i = 0; i < nsub; i++) {
bp = &endp->boxes[j + ((dir == 1) ? i : (nsub - i - 1))];
*bp = eb;
bp->UR.y = eb.UR.y - y * i / nsub;
bp->LL.y = eb.UR.y - y * (i + 1) / nsub;
if (bp->UR.y == bp->LL.y) abort();
}
endp->boxn += (nsub - 1);
j += nsub - 1;
}
if (left) {
lspls = getsplinepoints (left);
pp = spline_at_y (lspls, boxes[0].UR.y);
for (i = 0; i < nsub; i++) {
cp = spline_at_y (lspls, boxes[i].LL.y);
/*boxes[i].LL.x = AVG (pp.x, cp.x);*/
boxes[i].LL.x = MAX (pp.x, cp.x);
pp = cp;
}
pp = spline_at_y (lspls, (dir == 1) ?
endp->boxes[1].UR.y : endp->boxes[1].LL.y);
for (i = 1; i < endp->boxn; i++) {
cp = spline_at_y (lspls, (dir == 1) ?
endp->boxes[i].LL.y : endp->boxes[i].UR.y);
endp->boxes[i].LL.x = MIN (endp->nb.UR.x, MAX (pp.x, cp.x));
pp = cp;
}
i = (dir == 1) ? 0 : *boxnp - 1;
if (boxes[i].LL.x > endp->boxes[endp->boxn - 1].UR.x - MINW)
boxes[i].LL.x = endp->boxes[endp->boxn - 1].UR.x - MINW;
}
if (right) {
rspls = getsplinepoints (right);
pp = spline_at_y (rspls, boxes[0].UR.y);
for (i = 0; i < nsub; i++) {
cp = spline_at_y (rspls, boxes[i].LL.y);
/*boxes[i].UR.x = AVG (pp.x, cp.x);*/
boxes[i].UR.x = AVG (pp.x, cp.x);
pp = cp;
}
pp = spline_at_y (rspls, (dir == 1) ?
endp->boxes[1].UR.y : endp->boxes[1].LL.y);
for (i = 1; i < endp->boxn; i++) {
cp = spline_at_y (rspls, (dir == 1) ?
endp->boxes[i].LL.y : endp->boxes[i].UR.y);
endp->boxes[i].UR.x = MAX (endp->nb.LL.x, AVG (pp.x, cp.x));
pp = cp;
}
i = (dir == 1) ? 0 : *boxnp - 1;
if (boxes[i].UR.x < endp->boxes[endp->boxn - 1].LL.x + MINW)
boxes[i].UR.x = endp->boxes[endp->boxn - 1].LL.x + MINW;
}
}
#endif
static void adjustregularpath (int fb, int lb)
{
box *bp1, *bp2;
int i, x;
for (i = 0; i < P->nbox; i++) {
bp1 = &P->boxes[i];
if ((i - fb) % 2 == 0) {
if (bp1->LL.x >= bp1->UR.x) {
x = (bp1->LL.x + bp1->UR.x) / 2;
bp1->LL.x = x - HALFMINW, bp1->UR.x = x + HALFMINW;
}
} else {
if (bp1->LL.x + MINW > bp1->UR.x) {
x = (bp1->LL.x + bp1->UR.x) / 2;
bp1->LL.x = x - HALFMINW, bp1->UR.x = x + HALFMINW;
}
}
}
for (i = 0; i < P->nbox - 1; i++) {
bp1 = &P->boxes[i], bp2 = &P->boxes[i + 1];
if (i >= fb && i <= lb && (i - fb) % 2 == 0) {
if (bp1->LL.x + MINW > bp2->UR.x)
bp2->UR.x = bp1->LL.x + MINW;
if (bp1->UR.x - MINW < bp2->LL.x)
bp2->LL.x = bp1->UR.x - MINW;
} else if (i + 1 >= fb && i < lb && (i + 1 - fb) % 2 == 0) {
if (bp1->LL.x + MINW > bp2->UR.x)
bp1->LL.x = bp2->UR.x - MINW;
if (bp1->UR.x - MINW < bp2->LL.x)
bp1->UR.x = bp2->LL.x + MINW;
} else {
if (bp1->LL.x + MINW > bp2->UR.x) {
x = (bp1->LL.x + bp2->UR.x) / 2;
bp1->LL.x = x - HALFMINW;
bp2->UR.x = x + HALFMINW;
}
if (bp1->UR.x - MINW < bp2->LL.x) {
x = (bp1->UR.x + bp2->LL.x) / 2;
bp1->UR.x = x + HALFMINW;
bp2->LL.x = x - HALFMINW;
}
}
}
}
static box rank_box (graph_t* g, int r)
{
box b;
node_t /* *right0, *right1, */ *left0, *left1;
b = Rank_box[r];
if (b.LL.x == b.UR.x) {
left0 = GD_rank(g)[r].v[0];
/* right0 = GD_rank(g)[r].v[GD_rank(g)[r].n - 1]; */
left1 = GD_rank(g)[r + 1].v[0];
/* right1 = GD_rank(g)[r + 1].v[GD_rank(g)[r + 1].n - 1]; */
b.LL.x = LeftBound;
b.LL.y = ND_coord_i(left1).y + GD_rank(g)[r + 1].ht2;
b.UR.x = RightBound;
b.UR.y = ND_coord_i(left0).y - GD_rank(g)[r].ht1;
Rank_box[r] = b;
}
return b;
}
/* returns count of vertically aligned edges starting at n */
static int straight_len (node_t* n)
{
int cnt = 0;
node_t *v;
v = n;
while (1) {
v = ND_out(v).list[0]->head;
if (ND_node_type(v) != VIRTUAL)
break;
if ((ND_out(v).size != 1) || (ND_in(v).size != 1))
break;
if (ND_coord_i(v).x != ND_coord_i(n).x)
break;
cnt++;
}
return cnt;
}
static edge_t *straight_path (edge_t* e, int cnt, point* plist, int* np)
{
int n = *np;
edge_t *f = e;
while (cnt--)
f = ND_out(f->head).list[0];
plist[(*np)++] = plist[n - 1];
plist[(*np)++] = plist[n - 1];
plist[(*np)] = ND_coord_i(f->tail); /* will be overwritten by next spline */
return f;
}
static void recover_slack (edge_t* e, path* p)
{
int b;
node_t *vn;
b = 0; /* skip first rank box */
for (vn = e->head; ND_node_type(vn) == VIRTUAL && !spline_merge (vn);
vn = ND_out(vn).list[0]->head) {
while ((b < p->nbox) && (p->boxes[b].LL.y > ND_coord_i(vn).y))
b++;
if (b >= p->nbox)
break;
if (p->boxes[b].UR.y < ND_coord_i(vn).y)
continue;
if (ND_label(vn))
resize_vn (vn, p->boxes[b].LL.x, p->boxes[b].UR.x,
p->boxes[b].UR.x + ND_rw_i(vn));
else
resize_vn (vn, p->boxes[b].LL.x, (p->boxes[b].LL.x +
p->boxes[b].UR.x) / 2, p->boxes[b].UR.x);
}
}
static void resize_vn (vn, lx, cx, rx)
node_t *vn;
int lx, cx, rx;
{
ND_coord_i(vn).x = cx;
ND_lw_i(vn) = cx - lx, ND_rw_i(vn) = rx - cx;
}
/* side > 0 means right. side < 0 means left */
static edge_t *top_bound (edge_t* e, int side)
{
edge_t *f, *ans = NULL;
int i;
for (i = 0; (f = ND_out(e->tail).list[i]); i++) {
#if 0 /* were we out of our minds? */
if (ED_tail_port(e).p.x != ED_tail_port(f).p.x)
continue;
#endif
if (side * (ND_order(f->head) - ND_order(e->head)) <= 0)
continue;
if ((ED_spl(f) == NULL) && ((ED_to_orig(f) == NULL) || (ED_to_orig(f)->u.spl == NULL)))
continue;
if ((ans == NULL) || (side * (ND_order(ans->head) - ND_order(f->head)) > 0))
ans = f;
}
return ans;
}
static edge_t *bot_bound (edge_t* e, int side)
{
edge_t *f, *ans = NULL;
int i;
for (i = 0; (f = ND_in(e->head).list[i]); i++) {
#if 0 /* same here */
if (ED_head_port(e).p.x != ED_head_port(f).p.x)
continue;
#endif
if (side * (ND_order(f->tail) - ND_order(e->tail)) <= 0)
continue;
if ((ED_spl(f) == NULL) && ((ED_to_orig(f) == NULL) || (ED_to_orig(f)->u.spl == NULL)))
continue;
if ((ans == NULL) || (side * (ND_order(ans->tail) - ND_order(f->tail)) > 0))
ans = f;
}
return ans;
}
static double dist(p,q)
pointf p,q;
{
double d0,d1;
d0 = p.x - q.x;
d1 = p.y - q.y;
return sqrt(d0*d0 + d1*d1);
}
point closest(splines* spl, point p)
{
int i, j, k, besti, bestj;
double bestdist, d, dlow, dhigh;
double low, high, t;
pointf c[4], pt2, pt;
point rv;
bezier bz;
besti = bestj = -1;
bestdist = 1e+38;
pt.x = p.x; pt.y = p.y;
for (i = 0; i < spl->size; i++) {
bz = spl->list[i];
for (j = 0; j < bz.size; j++) {
pointf b;
b.x = bz.list[j].x; b.y = bz.list[j].y;
d = dist(b,pt);
if ((bestj == -1) || (d < bestdist)) {
besti = i;
bestj = j;
bestdist = d;
}
}
}
bz = spl->list[besti];
j = bestj/3; if (j >= spl->size) j--;
for (k = 0; k < 4; k++) {
c[k].x = bz.list[j + k].x;
c[k].y = bz.list[j + k].y;
}
low = 0.0; high = 1.0;
dlow = dist(c[0],pt);
dhigh = dist(c[3],pt);
do {
t = (low + high) / 2.0;
pt2 = Bezier (c, 3, t, NULL, NULL);
if (fabs(dlow - dhigh) < 1.0) break;
if (low == high) break;
if (dlow < dhigh) {high = t; dhigh = dist(pt2,pt);}
else {low = t; dlow = dist(pt2,pt); }
} while (1);
rv.x = pt2.x;
rv.y = pt2.y;
return rv;
}
/* common routines */
static double conc_slope(node_t* n)
{
double s_in, s_out,m_in,m_out;
int cnt_in,cnt_out;
pointf p;
edge_t *e;
s_in = s_out = 0.0;
for (cnt_in = 0; (e = ND_in(n).list[cnt_in]); cnt_in++)
s_in += ND_coord_i(e->tail).x;
for (cnt_out = 0; (e = ND_out(n).list[cnt_out]); cnt_out++)
s_out += ND_coord_i(e->head).x;
p.x = ND_coord_i(n).x - (s_in / cnt_in);
p.y = ND_coord_i(n).y - ND_coord_i(ND_in(n).list[0]->tail).y;
m_in = atan2(p.y,p.x);
p.x = (s_out / cnt_out) - ND_coord_i(n).x;
p.y = ND_coord_i(ND_out(n).list[0]->head).y - ND_coord_i(n).y;
m_out = atan2(p.y,p.x);
return ((m_in + m_out) / 2.0);
}
static void beginpath (edge_t* e, int et, pathend_t* endp)
{
node_t *n;
int (*pboxfn)(node_t *, edge_t *, int, box *, int *);
n = e->tail;
if (ND_shape(n))
pboxfn = ND_shape(n)->pboxfn;
else
pboxfn = NULL;
P->start.p = add_points (ND_coord_i(n), ED_tail_port(e).p);
P->ulpp = P->urpp = P->llpp = P->lrpp = NULL;
if (spline_merge (e->tail)) {
/*P->start.theta = - PI / 2;*/
P->start.theta = conc_slope(e->tail);
P->start.constrained = TRUE;
} else {
if (ED_tail_port(e).constrained) {
P->start.theta = ED_tail_port(e).theta;
P->start.constrained = TRUE;
} else
P->start.constrained = FALSE;
}
P->nbox = 0;
P->data = (void*)e;
endp->np = P->start.p;
/* FIXME: check that record_path returns a good path */
if (pboxfn)
endp->sidemask = (*pboxfn) (n, e, 1,
&endp->boxes[0], &endp->boxn);
else {
endp->boxes[0] = endp->nb;
endp->boxn = 1;
}
switch (et) {
case SELFEDGE:
/* moving the box UR.y by + 1 avoids colinearity between
port point and box that confuses Proutespline(). it's
a bug in Proutespline() but this is the easiest fix. */
endp->boxes[0].UR.y = P->start.p.y + 1;
endp->sidemask = BOTTOM;
break;
case FLATEDGE:
endp->boxes[0].LL.y = P->start.p.y;
endp->sidemask = TOP;
break;
case REGULAREDGE:
endp->boxes[0].UR.y = P->start.p.y;
endp->sidemask = BOTTOM;
break;
}
}
static void endpath (edge_t* e, int et, pathend_t* endp)
{
node_t *n;
int (*pboxfn) (node_t *, edge_t *, int, box *, int *);
n = e->head;
if (ND_shape(n))
pboxfn = ND_shape(n)->pboxfn;
else
pboxfn = NULL;
P->end.p = add_points (ND_coord_i(n), ED_head_port(e).p);
if (spline_merge (e->head)) {
/*P->end.theta = PI / 2;*/
P->end.theta = conc_slope(e->head) + PI;
assert(P->end.theta < 2*PI);
P->end.constrained = TRUE;
} else {
if (ED_head_port(e).constrained) {
P->end.theta = ED_head_port(e).theta;
P->end.constrained = TRUE;
} else
P->end.constrained = FALSE;
}
endp->np = P->end.p;
if (pboxfn)
endp->sidemask = (*pboxfn) (n, e, 2,
&endp->boxes[0], &endp->boxn);
else {
endp->boxes[0] = endp->nb;
endp->boxn = 1;
}
switch (et) {
case SELFEDGE:
endp->boxes[0].LL.y = P->end.p.y;
endp->sidemask = TOP;
break;
case FLATEDGE:
endp->boxes[0].LL.y = P->end.p.y;
endp->sidemask = TOP;
break;
case REGULAREDGE:
endp->boxes[0].LL.y = P->end.p.y;
endp->sidemask = TOP;
break;
}
}
static edge_t *getmainedge (edge_t* e)
{
edge_t *le = e;
while (ED_to_virt(le))
le = ED_to_virt(le);
while (ED_to_orig(le))
le = ED_to_orig(le);
return le;
}
static splines *getsplinepoints (edge_t* e)
{
edge_t *le;
splines *sp;
for (le = e; !(sp = ED_spl(le)) && ED_edge_type(le) != NORMAL; le = ED_to_orig(le)) ;
if (sp == NULL) abort ();
return sp;
}
static int
cl_vninside(graph_t* cl, node_t* n)
{
return (BETWEEN(GD_bb(cl).LL.x,ND_coord_i(n).x,GD_bb(cl).UR.x) &&
BETWEEN(GD_bb(cl).LL.y,ND_coord_i(n).y,GD_bb(cl).UR.y));
}
/* returns the cluster of (adj) that interferes with n,
*/
static graph_t *
cl_bound(n, adj)
node_t *n,*adj;
{
graph_t *rv,*cl,*tcl,*hcl;
edge_t *orig;
rv = NULL;
if (ND_node_type(n) == NORMAL) tcl = hcl = ND_clust(n);
else {
orig = ND_out(n).list[0]->u.to_orig;
tcl = ND_clust(orig->tail); hcl = ND_clust(orig->head);
}
if (ND_node_type(adj) == NORMAL) {
cl = ND_clust(adj);
if (cl && (cl != tcl) && (cl != hcl)) rv = cl;
}
else {
orig = ED_to_orig(ND_out(adj).list[0]);
cl = ND_clust(orig->tail);
if (cl && (cl != tcl) && (cl != hcl) && cl_vninside(cl,adj)) rv=cl;
else {
cl = ND_clust(orig->head);
if (cl && (cl != tcl) && (cl != hcl) && cl_vninside(cl,adj)) rv=cl;
}
}
return rv;
}
static box maximal_bbox (vn, ie, oe)
node_t *vn;
edge_t *ie, *oe;
{
int nb,b;
graph_t *g = vn->graph, *left_cl,*right_cl;
node_t *left,*right;
box rv;
left_cl = right_cl = NULL;
/* give this node all the available space up to its neighbors */
b = ND_coord_i(vn).x - ND_lw_i(vn);
if ((left = neighbor(vn, ie, oe, -1))) {
if ((left_cl = cl_bound(vn, left)))
nb = GD_bb(left_cl).UR.x + Splinesep;
else {
nb = ND_coord_i(left).x + ND_mval(left);
if (ND_node_type(left) == NORMAL) nb += GD_nodesep(g)/2;
else nb += Splinesep;
}
if (nb < b) b = nb;
rv.LL.x = b;
}
else rv.LL.x = MIN(b,LeftBound);
/* we have to leave room for our own label! */
if (ND_label(vn)) b = ND_coord_i(vn).x + 10;
else b = ND_coord_i(vn).x + ND_rw_i(vn);
if ((right = neighbor(vn, ie, oe, 1))) {
if ((right_cl = cl_bound(vn, right)))
nb = GD_bb(right_cl).LL.x - Splinesep;
else {
nb = ND_coord_i(right).x - ND_lw_i(right);
if (ND_node_type(right) == NORMAL) nb -= GD_nodesep(g)/2;
else nb -= Splinesep;
}
if (nb > b) b = nb;
rv.UR.x = b;
}
else rv.UR.x = MAX(b,RightBound);
if ((ND_node_type(vn) == VIRTUAL) && (ND_label(vn)))
rv.UR.x -= ND_rw_i(vn);
rv.LL.y = ND_coord_i(vn).y - GD_rank(g)[ND_rank(vn)].ht1;
rv.UR.y = ND_coord_i(vn).y + GD_rank(g)[ND_rank(vn)].ht2;
return rv;
}
static node_t *neighbor (vn, ie, oe, dir)
node_t *vn;
edge_t *ie, *oe;
int dir;
{
int i;
node_t *n, *rv = NULL;
rank_t *rank = &(GD_rank(vn->graph)[ND_rank(vn)]);
for (i = ND_order(vn) + dir; ((i >= 0) && (i < rank->n)); i += dir) {
n = rank->v[i];
if ((ND_node_type(n) == VIRTUAL) && (ND_label(n))) {
rv=n;
break;
}
if (ND_node_type(n) == NORMAL) {
rv = n;
break;
}
if (pathscross(n, vn, ie, oe) == FALSE) {
rv = n;
break;
}
}
return rv;
}
static boolean pathscross (n0, n1, ie1, oe1)
node_t *n0, *n1;
edge_t *ie1, *oe1;
{
edge_t *e0, *e1;
node_t *na, *nb;
int order, cnt;
order = (ND_order(n0) > ND_order(n1));
if ((ND_out(n0).size != 1) && (ND_out(n0).size != 1))
return FALSE;
e1 = oe1;
if (ND_out(n0).size == 1 && e1) {
e0 = ND_out(n0).list[0];
for (cnt = 0; cnt < 2; cnt++) {
if ((na = e0->head) == (nb = e1->head))
break;
if (order != (ND_order(na) > ND_order(nb)))
return TRUE;
if ((ND_out(na).size != 1) || (ND_node_type(na) == NORMAL))
break;
e0 = ND_out(na).list[0];
if ((ND_out(nb).size != 1) || (ND_node_type(nb) == NORMAL))
break;
e1 = ND_out(nb).list[0];
}
}
e1 = ie1;
if (ND_in(n0).size == 1 && e1) {
e0 = ND_in(n0).list[0];
for (cnt = 0; cnt < 2; cnt++) {
if ((na = e0->tail) == (nb = e1->tail))
break;
if (order != (ND_order(na) > ND_order(nb)))
return TRUE;
if ((ND_in(na).size != 1) || (ND_node_type(na) == NORMAL))
break;
e0 = ND_in(na).list[0];
if ((ND_in(nb).size != 1) || (ND_node_type(nb) == NORMAL))
break;
e1 = ND_in(nb).list[0];
}
}
return FALSE;
}
static void add_box (box b)
{
P->boxes[P->nbox++] = b;
}
#ifdef DEBUG
void showpath(path *p)
{
int i;
point LL,UR;
fprintf(stderr,"%%!PS\n");
for (i = 0; i < p->nbox; i++) {
LL = p->boxes[i].LL; UR = p->boxes[i].UR;
fprintf(stderr,"newpath %d %d moveto %d %d lineto %d %d lineto %d %d lineto closepath stroke\n",LL.x,LL.y,UR.x,LL.y,UR.x,UR.y,LL.x,UR.y);
}
fprintf(stderr,"showpage\n");
}
#endif
|