From c5258454ec1e87d506aaebba231a345148d9b2c1 Mon Sep 17 00:00:00 2001 From: erg Date: Sat, 26 Feb 2005 23:09:28 +0000 Subject: [PATCH] Implement new dot spline routing for regular edges to support compass points. Still need to tackle loops. --- lib/common/htmltable.c | 58 +++++++---- lib/common/htmltable.h | 3 +- lib/common/shapes.c | 208 +++++++++++++++++++++++++++++++++++----- lib/common/splines.c | 149 ++++++++++++++++++++++------ lib/common/types.h | 18 ++-- lib/dotgen/dotsplines.c | 79 ++++++++++----- 6 files changed, 408 insertions(+), 107 deletions(-) diff --git a/lib/common/htmltable.c b/lib/common/htmltable.c index 5c7281ca3..1d7be9462 100644 --- a/lib/common/htmltable.c +++ b/lib/common/htmltable.c @@ -475,14 +475,14 @@ void free_html_label(htmllabel_t * lp, int root) free(lp); } -static box *portToTbl(htmltbl_t *, char *); /* forward declaration */ +static htmldata_t* portToTbl(htmltbl_t *, char *); /* forward declaration */ -static box *portToCell(htmlcell_t * cp, char *id) +static htmldata_t* portToCell(htmlcell_t * cp, char *id) { - box *rv; + htmldata_t* rv; if (cp->data.port && (strcasecmp(cp->data.port, id) == 0)) - rv = &cp->data.box; + rv = &cp->data; else if (cp->child.kind == HTML_TBL) rv = portToTbl(cp->child.u.tbl, id); else @@ -495,14 +495,15 @@ static box *portToCell(htmlcell_t * cp, char *id) * See if tp or any of its child cells has the given port id. * If true, return corresponding box. */ -static box *portToTbl(htmltbl_t * tp, char *id) +static htmldata_t* +portToTbl(htmltbl_t* tp, char* id) { - box *rv; - htmlcell_t **cells; - htmlcell_t *cp; + htmldata_t* rv; + htmlcell_t** cells; + htmlcell_t* cp; if (tp->data.port && (strcasecmp(tp->data.port, id) == 0)) - rv = &tp->data.box; + rv = &tp->data; else { rv = NULL; cells = tp->u.n.cells; @@ -521,14 +522,22 @@ static box *portToTbl(htmltbl_t * tp, char *id) * If successful, return pointer to port's box. * Else return NULL. */ -box *html_port(node_t * n, char *pname) +box *html_port(node_t * n, char *pname, int* sides) { - htmllabel_t *lbl = ND_label(n)->u.html; + htmldata_t* tp; + htmllabel_t* lbl = ND_label(n)->u.html; + box* rv = NULL; if (lbl->kind == HTML_TEXT) return NULL; - return portToTbl(lbl->u.tbl, pname); + tp = portToTbl(lbl->u.tbl, pname); + if (tp) { + rv = &tp->box; + *sides = tp->sides; + } + return rv; + } /* html_path: @@ -1037,14 +1046,14 @@ void sizeArray(htmltbl_t * tbl) closeGraphs(rowg, colg); } -static void pos_html_tbl(htmltbl_t *, box); /* forward declaration */ +static void pos_html_tbl(htmltbl_t *, box, int); /* forward declaration */ static void pos_html_img(htmlimg_t * cp, box pos) { cp->box = pos; } -static void pos_html_cell(htmlcell_t * cp, box pos) +static void pos_html_cell(htmlcell_t * cp, box pos, int sides) { int delx, dely; point oldsz; @@ -1087,6 +1096,7 @@ static void pos_html_cell(htmlcell_t * cp, box pos) } } cp->data.box = pos; + cp->data.sides = sides; /* set up child's position */ cbox.LL.x = pos.LL.x + cp->data.border + cp->data.pad; @@ -1095,7 +1105,7 @@ static void pos_html_cell(htmlcell_t * cp, box pos) cbox.UR.y = pos.UR.y - cp->data.border - cp->data.pad; if (cp->child.kind == HTML_TBL) { - pos_html_tbl(cp->child.u.tbl, cbox); + pos_html_tbl(cp->child.u.tbl, cbox, sides); } else if (cp->child.kind == HTML_IMAGE) { pos_html_img(cp->child.u.img, cbox); } else { @@ -1136,9 +1146,11 @@ static void pos_html_cell(htmlcell_t * cp, box pos) /* pos_html_tbl: * Position table given its box, then calculate - * the position of each cell. + * the position of each cell. In addition, set the sides + * attribute indicating which external sides of the node + * are accessible to the table. */ -static void pos_html_tbl(htmltbl_t * tbl, box pos) +static void pos_html_tbl(htmltbl_t * tbl, box pos, int sides) { int x, y, delx, dely; int i, plus, extra, oldsz; @@ -1208,13 +1220,21 @@ static void pos_html_tbl(htmltbl_t * tbl, box pos) } while ((cp = *cells++)) { + int mask = 0; + if (sides) { + if (cp->col == 0) mask |= LEFT; + if (cp->row == 0) mask |= TOP; + if (cp->col + cp->cspan == tbl->cc) mask |= RIGHT; + if (cp->row + cp->rspan == tbl->rc) mask |= BOTTOM; + } cbox.LL.x = tbl->widths[cp->col]; cbox.UR.x = tbl->widths[cp->col + cp->cspan] - tbl->data.space; cbox.UR.y = tbl->heights[cp->row]; cbox.LL.y = tbl->heights[cp->row + cp->rspan] + tbl->data.space; - pos_html_cell(cp, cbox); + pos_html_cell(cp, cbox, sides & mask); } + tbl->data.sides = sides; tbl->data.box = pos; } @@ -1455,7 +1475,7 @@ int make_html_label(textlabel_t * lp, void *obj) wd2 = (lbl->u.tbl->data.box.UR.x + 1) / 2; ht2 = (lbl->u.tbl->data.box.UR.y + 1) / 2; box = boxof(-wd2, -ht2, wd2, ht2); - pos_html_tbl(lbl->u.tbl, box); + pos_html_tbl(lbl->u.tbl, box, BOTTOM | RIGHT | TOP | LEFT); lp->dimen.x = box.UR.x - box.LL.x; lp->dimen.y = box.UR.y - box.LL.y; } else { diff --git a/lib/common/htmltable.h b/lib/common/htmltable.h index b1157b974..341b6f00c 100644 --- a/lib/common/htmltable.h +++ b/lib/common/htmltable.h @@ -71,6 +71,7 @@ extern "C" { unsigned short width; unsigned short height; box box; /* its geometric placement in points */ + unsigned char sides; /* set of sides exposed to field */ } htmldata_t; #define HTML_UNSET 0 @@ -144,7 +145,7 @@ extern "C" { extern void free_html_data(htmldata_t *); extern void free_html_text(htmltxt_t *); - extern box *html_port(node_t * n, char *pname); + extern box *html_port(node_t * n, char *pname, int* sides); extern int html_path(node_t * n, edge_t * e, int pt, box * rv, int *k); extern int html_inside(node_t * n, pointf p, edge_t * e); diff --git a/lib/common/shapes.c b/lib/common/shapes.c index b993b6c5a..329cede82 100644 --- a/lib/common/shapes.c +++ b/lib/common/shapes.c @@ -36,7 +36,7 @@ extern void sincos(double x, double *s, double *c); # define sincos(x,s,c) *s = sin(x); *c = cos(x) #endif -static port Center = { {0, 0}, -1, 0, 0, 0, 0 }; +static port Center = { {0, 0}, -1, 0, 0, 0, 1, 0, 0 }; #define ATTR_SET(a,n) ((a) && (*(agxget(n,a->index)) != '\0')) #define DEF_POINT 0.05 @@ -746,11 +746,14 @@ static boolean poly_inside(inside_t * inside_context, pointf p) node_t *n = inside_context->n; P = flip_ptf(p, GD_rankdir(n->graph)); - for (f = e; ED_edge_type(f) != NORMAL; f = ED_to_orig(f)); - e = f; + if (e) { + for (f = e; ED_edge_type(f) != NORMAL; f = ED_to_orig(f)); + e = f; + } if ((n != lastn) || (e != laste)) { - bp = GET_PORT_BOX(n, e); + if (e) bp = GET_PORT_BOX(n, e); + else bp = NULL; if ((bp == NULL) && (n != datan)) { datan = n; @@ -844,22 +847,118 @@ static int poly_path(node_t * n, edge_t * e, int pt, box rv[], int *kptr) return side; } +/* invflip_side: + */ +static int invflip_side (int side, int rankdir) +{ + switch (rankdir) { + case RANKDIR_TB: + break; + case RANKDIR_BT: + switch (side) { + case TOP: + side = BOTTOM; + break; + case BOTTOM: + side = TOP; + break; + default: + break; + } + break; + case RANKDIR_LR: + switch (side) { + case TOP: + side = RIGHT; + break; + case BOTTOM: + side = LEFT; + break; + case LEFT: + side = TOP; + break; + case RIGHT: + side = BOTTOM; + break; + } + break; + case RANKDIR_RL: + switch (side) { + case TOP: + side = RIGHT; + break; + case BOTTOM: + side = LEFT; + break; + case LEFT: + side = BOTTOM; + break; + case RIGHT: + side = TOP; + break; + } + break; + } + return side; +} + +/* invflip_angle: + */ +static double invflip_angle (double angle, int rankdir) +{ + switch (rankdir) { + case RANKDIR_TB: + break; + case RANKDIR_BT: + angle *= -1; + break; + case RANKDIR_LR: + angle -= PI * 0.5; + break; + case RANKDIR_RL: + if (angle == PI) + angle = -0.5 * PI; + else if (angle == PI * 0.75) + angle = -0.25 * PI; + else if (angle == PI * 0.5) + angle = 0; + else if (angle == PI * 0.25) + angle = angle; + else if (angle == 0) + angle = PI * 0.5; + else if (angle == PI * -0.25) + angle = PI * 0.75; + else if (angle == PI * -0.5) + angle = PI; + else if (angle == PI * -0.75) + angle = angle; + break; + } + return angle; +} + /* compassPort: * Attach a compass point to a port pp, and fill in remaining fields. * n is the corresponding node; bp is the bounding box of the port. * compass is the compass point * Return 1 if unrecognized compass point, in which case we * use the center. - * This function also finishes initialized the port structure, + * This function also finishes initializing the port structure, * even if no compass point is involved. + * The sides value gives the set of sides shared by the port. This + * is used with a compass point to indicate if the port is exposed, to + * set the port's side value. */ -static int compassPort(node_t * n, box * bp, port * pp, char *compass) +static int +compassPort(node_t* n, box* bp, port* pp, char* compass, int sides) { box b; point p, ctr; int rv = 0; double theta = 0.0; int constrain = 0; + int side = 0; + int clip = TRUE; if (bp) { b = *bp; @@ -883,13 +982,17 @@ static int compassPort(node_t * n, box * bp, port * pp, char *compass) int margin = 1; /* would like = 0, but spline router croaks */ switch (*compass++) { case 'e': - p.x = b.UR.x - margin; + p.x = b.UR.x + margin; theta = 0.0; constrain = 1; + clip = FALSE; + side = sides & RIGHT; break; case 's': - p.y = b.LL.y + margin; + p.y = b.LL.y - margin; constrain = 1; + clip = FALSE; + side = sides & BOTTOM; switch (*compass) { case '\0': theta = -PI * 0.5; @@ -905,18 +1008,23 @@ static int compassPort(node_t * n, box * bp, port * pp, char *compass) default: p.y = ctr.y; constrain = 0; + clip = TRUE; rv = 1; break; } break; case 'w': - p.x = b.LL.x + margin; + p.x = b.LL.x - margin; theta = PI; constrain = 1; + clip = FALSE; + side = sides & LEFT; break; case 'n': - p.y = b.UR.y - margin; + p.y = b.UR.y + margin; constrain = 1; + clip = FALSE; + side = sides & TOP; switch (*compass) { case '\0': theta = PI * 0.5; @@ -932,6 +1040,7 @@ static int compassPort(node_t * n, box * bp, port * pp, char *compass) default: p.y = ctr.y; constrain = 0; + clip = TRUE; rv = 1; break; } @@ -942,12 +1051,21 @@ static int compassPort(node_t * n, box * bp, port * pp, char *compass) } } p = invflip_pt(p, GD_rankdir(n->graph)); + pp->side = invflip_side(side, GD_rankdir(n->graph)); pp->bp = bp; pp->p = p; - pp->theta = theta; - pp->order = - (MC_SCALE * (ND_lw_i(n) + p.x)) / (ND_lw_i(n) + ND_rw_i(n)); + pp->theta = invflip_angle(theta, GD_rankdir(n->graph)); + if ((p.x == 0) && (p.y == 0)) + pp->order = MC_SCALE/2; + else { + /* compute angle with 0 at north pole, increasing CCW */ + double angle = atan2(p.y,p.x) + 1.5*PI; + if (angle >= 2*PI) angle -= 2*PI; + pp->order = (int)((MC_SCALE * angle) / 2*PI); + } pp->constrained = constrain; + pp->defined = TRUE; + pp->clip = clip; return rv; } @@ -955,21 +1073,23 @@ static port poly_port(node_t * n, char *portname, char *compass) { port rv; box *bp; + int sides; /* bitmap of which sides the port lies along */ if (portname[0] == '\0') return Center; + sides = BOTTOM | RIGHT | TOP | LEFT; if ((ND_label(n)->html)) { - if ((bp = html_port(n, portname))) { - if (compassPort(n, bp, &rv, compass)) { + if ((bp = html_port(n, portname, &sides))) { + if (compassPort(n, bp, &rv, compass, sides)) { agerr(AGWARN, "node %s, port %s, unrecognized compass point '%s' - ignored\n", n->name, portname, compass); } - } else if (compassPort(n, NULL, &rv, portname)) { + } else if (compassPort(n, NULL, &rv, portname, sides)) { unrecognized(n, portname); } - } else if (compassPort(n, NULL, &rv, portname)) { + } else if (compassPort(n, NULL, &rv, portname, sides)) { unrecognized(n, portname); } @@ -1138,7 +1258,8 @@ static void point_init(node_t * n) static char *reclblp; -static field_t *parse_reclbl(node_t * n, int LR, int flag, char *text) +static field_t* +parse_reclbl(node_t * n, int LR, int flag, char *text) { field_t *fp, *rv = NEW(field_t); char *tsp, *psp, *hstsp, *hspsp, *sp; @@ -1347,14 +1468,40 @@ static void resize_reclbl(field_t * f, point sz, int nojustify_p) } } -static void pos_reclbl(field_t * f, point ul) +/* pos_reclbl: + * Assign position info for each field. Also, set + * the sides attribute, which indicates which sides of the + * record are accessible to the field. + */ +static void pos_reclbl(field_t * f, point ul, int sides) { - int i; + int i, last, mask; + f->sides = sides; f->b.LL = pointof(ul.x, ul.y - f->size.y); f->b.UR = pointof(ul.x + f->size.x, ul.y); - for (i = 0; i < f->n_flds; i++) { - pos_reclbl(f->fld[i], ul); + last = f->n_flds - 1; + for (i = 0; i <= last; i++) { + if (sides) { + if (f->LR) { + if (i == 0) { + if (i == last) mask = TOP | BOTTOM | RIGHT | LEFT; + else mask = TOP | BOTTOM | LEFT; + } + else if (i == last) mask = TOP | BOTTOM | RIGHT; + else mask = TOP | BOTTOM; + } + else { + if (i == 0) { + if (i == last) mask = TOP | BOTTOM | RIGHT | LEFT; + else mask = TOP | RIGHT | LEFT; + } + else if (i == last) mask = LEFT | BOTTOM | RIGHT; + else mask = LEFT | RIGHT; + } + } + else mask = 0; + pos_reclbl(f->fld[i], ul, sides & mask); if (f->LR) ul.x = ul.x + f->fld[i]->size.x; else @@ -1400,11 +1547,12 @@ static void record_init(node_t * n) point ul, sz; int len; char *textbuf; /* temp buffer for storing labels */ + int sides = BOTTOM | RIGHT | TOP | LEFT; reclblp = ND_label(n)->text; len = strlen(reclblp); textbuf = N_NEW(len + 1, char); - if (!(info = parse_reclbl(n, NOT(GD_flip(n->graph)), TRUE, textbuf))) { + if (!(info = parse_reclbl(n,NOT(GD_flip(n->graph)), TRUE, textbuf))) { agerr(AGERR, "bad label format %s\n", ND_label(n)->text); reclblp = "\\N"; info = parse_reclbl(n, NOT(GD_flip(n->graph)), TRUE, textbuf); @@ -1427,7 +1575,7 @@ static void record_init(node_t * n) } resize_reclbl(info, sz, mapbool(late_string(n, N_nojustify, "false"))); ul = pointof(-sz.x / 2, sz.y / 2); - pos_reclbl(info, ul); + pos_reclbl(info, ul, sides); ND_width(n) = PS2INCH(info->size.x); ND_height(n) = PS2INCH(info->size.y); ND_shape_info(n) = (void *) info; @@ -1461,17 +1609,19 @@ static port record_port(node_t * n, char *portname, char *compass) field_t *f; field_t *subf; port rv; + int sides; /* bitmap of which sides the port lies along */ if (portname[0] == '\0') return Center; + sides = BOTTOM | RIGHT | TOP | LEFT; f = (field_t *) ND_shape_info(n); if ((subf = map_rec_port(f, portname))) { - if (compassPort(n, &subf->b, &rv, compass)) { + if (compassPort(n, &subf->b, &rv, compass, subf->sides)) { agerr(AGWARN, "node %s, port %s, unrecognized compass point '%s' - ignored\n", n->name, portname, compass); } - } else if (compassPort(n, &f->b, &rv, portname)) { + } else if (compassPort(n, &f->b, &rv, portname, sides)) { unrecognized(n, portname); } @@ -1491,6 +1641,12 @@ static boolean record_inside(inside_t * inside_context, pointf p) /* convert point to node coordinate system */ p = flip_ptf(p, GD_rankdir(n->graph)); + + if (e == NULL) { + fld0 = (field_t *) ND_shape_info(n); + return INSIDE(p, fld0->b); + } + /* find real edge */ for (f = e; ED_edge_type(f) != NORMAL; f = ED_to_orig(f)); e = f; diff --git a/lib/common/splines.c b/lib/common/splines.c index 59a56d872..626319f28 100644 --- a/lib/common/splines.c +++ b/lib/common/splines.c @@ -21,30 +21,6 @@ #include -/* wantclip: - * Return false if head/tail end of edge should not be clipped - * to node. - */ -static boolean wantclip(edge_t * e, node_t * n) -{ - char *str; - attrsym_t *sym = 0; - boolean rv = TRUE; - - if (n == e->tail) - sym = E_tailclip; - if (n == e->head) - sym = E_headclip; - if (sym) { /* mapbool isn't a good fit, because we want "" to mean TRUE */ - str = agxget(e, sym->index); - if (str && str[0]) - rv = mapbool(str); - else - rv = TRUE; - } - return rv; -} - /* arrow_clip: * Clip arrow to node boundary. * The real work is done elsewhere. Here we get the real edge, @@ -176,6 +152,8 @@ shape_clip0(inside_t * inside_context, node_t * n, point curve[4], * fed back to shape_clip, it will again assume left_inside is true. * To be safe, shape_clip0 should guarantee that the computed boundary * point fails insidefn. + * The edge e is used to provide a port box. If NULL, the spline is + * clipped to the node shape. */ void shape_clip(node_t * n, point curve[4], edge_t * e) { @@ -266,7 +244,7 @@ clip_and_install(edge_t * fe, edge_t * le, point * ps, int pn, } /* spline may be interior to node */ - if (wantclip(orig, tn) && ND_shape(tn) && ND_shape(tn)->fns->insidefn) { + if(ED_tail_port(orig).clip && ND_shape(tn) && ND_shape(tn)->fns->insidefn) { inside_context.n = tn; inside_context.e = fe; for (start = 0; start < pn - 4; start += 3) { @@ -278,7 +256,7 @@ clip_and_install(edge_t * fe, edge_t * le, point * ps, int pn, shape_clip0(&inside_context, tn, &ps[start], TRUE); } else start = 0; - if (wantclip(orig, hn) && ND_shape(hn) && ND_shape(hn)->fns->insidefn) { + if(ED_head_port(orig).clip && ND_shape(hn) && ND_shape(hn)->fns->insidefn) { inside_context.n = hn; inside_context.e = le; for (end = pn - 4; end > 0; end -= 3) { @@ -306,7 +284,8 @@ clip_and_install(edge_t * fe, edge_t * le, point * ps, int pn, newspl->size = end - start + 4; } -static double conc_slope(node_t * n) +static double +conc_slope(node_t* n) { double s_in, s_out, m_in, m_out; int cnt_in, cnt_out; @@ -338,7 +317,7 @@ void add_box(path * P, box b) void beginpath(path * P, edge_t * e, int et, pathend_t * endp, boolean merge) { - int mask; + int side, mask; node_t *n; int (*pboxfn) (node_t * n, edge_t * e, int, box *, int *); @@ -364,6 +343,62 @@ beginpath(path * P, edge_t * e, int et, pathend_t * endp, boolean merge) P->nbox = 0; P->data = (void *) e; endp->np = P->start.p; + if ((et == REGULAREDGE) && (ND_node_type(n) == NORMAL) && ((side = ED_tail_port(e).side))) { + edge_t* orig; + box b0, b = endp->nb; + switch (side) { + case LEFT: + b.UR.x = P->start.p.x; + b.LL.y = ND_coord_i(n).y - ND_ht_i(n)/2; + b.UR.y = P->start.p.y; + endp->boxes[0] = b; + endp->boxn = 1; + break; + case RIGHT: + b.LL.x = P->start.p.x; + b.LL.y = ND_coord_i(n).y - ND_ht_i(n)/2; + b.UR.y = P->start.p.y; + endp->boxes[0] = b; + endp->boxn = 1; + break; + case TOP: + if (ND_coord_i(e->head).x < 2*ND_coord_i(n).x - endp->np.x) { + b0.LL.x = b.LL.x - 1; + b0.LL.y = ND_coord_i(n).y + ND_ht_i(n)/2; + b0.UR.x = P->start.p.x; + b0.UR.y = b0.LL.y + GD_ranksep(n->graph)/2; + b.UR.x = ND_coord_i(n).x - ND_lw_i(n) - 2; + b.UR.y = b0.LL.y; + b.LL.y = ND_coord_i(n).y - ND_ht_i(n)/2; + b.LL.x -= 1; + endp->boxes[0] = b0; + endp->boxes[1] = b; + } + else { + b0.LL.x = P->start.p.x; + b0.LL.y = ND_coord_i(n).y + ND_ht_i(n)/2; + b0.UR.x = b.UR.x+1; + b0.UR.y = b0.LL.y + GD_ranksep(n->graph)/2; + b.LL.x = ND_coord_i(n).x + ND_rw_i(n) + 2; + b.UR.y = b0.LL.y; + b.LL.y = ND_coord_i(n).y - ND_ht_i(n)/2; + b.UR.x += 1; + endp->boxes[0] = b0; + endp->boxes[1] = b; + } + endp->boxn = 2; + break; + case BOTTOM: + b.UR.y = MAX(b.UR.y,P->start.p.y); + endp->boxes[0] = b; + endp->boxn = 1; + break; + } + for (orig = e; ED_edge_type(orig) != NORMAL; orig = ED_to_orig(orig)); + ED_tail_port(orig).clip = FALSE; + endp->sidemask = side; + return; + } /* FIXME: check that record_path returns a good path */ if (pboxfn && (mask = (*pboxfn) (n, e, 1, &endp->boxes[0], &endp->boxn))) @@ -393,7 +428,7 @@ beginpath(path * P, edge_t * e, int et, pathend_t * endp, boolean merge) void endpath(path * P, edge_t * e, int et, pathend_t * endp, boolean merge) { - int mask; + int side, mask; node_t *n; int (*pboxfn) (node_t * n, edge_t * e, int, box *, int *); @@ -417,6 +452,62 @@ void endpath(path * P, edge_t * e, int et, pathend_t * endp, boolean merge) P->end.constrained = FALSE; } endp->np = P->end.p; + if ((et == REGULAREDGE) && (ND_node_type(n) == NORMAL) && ((side = ED_head_port(e).side))) { + edge_t* orig; + box b0, b = endp->nb; + switch (side) { + case LEFT: + b.UR.x = P->end.p.x; + b.UR.y = ND_coord_i(n).y + ND_ht_i(n)/2; + b.LL.y = P->end.p.y; + endp->boxes[0] = b; + endp->boxn = 1; + break; + case RIGHT: + b.LL.x = P->end.p.x; + b.UR.y = ND_coord_i(n).y + ND_ht_i(n)/2; + b.LL.y = P->end.p.y; + endp->boxes[0] = b; + endp->boxn = 1; + break; + case BOTTOM: + if (ND_coord_i(e->tail).x < 2*ND_coord_i(n).x - endp->np.x) { + b0.LL.x = b.LL.x-1; + b0.UR.y = ND_coord_i(n).y - ND_ht_i(n)/2; + b0.UR.x = P->end.p.x; + b0.LL.y = b0.UR.y - GD_ranksep(n->graph)/2; + b.UR.x = ND_coord_i(n).x - ND_lw_i(n) - 2; + b.LL.y = b0.UR.y; + b.UR.y = ND_coord_i(n).y + ND_ht_i(n)/2; + b.LL.x -= 1; + endp->boxes[0] = b0; + endp->boxes[1] = b; + } + else { + b0.LL.x = P->end.p.x; + b0.UR.y = ND_coord_i(n).y - ND_ht_i(n)/2; + b0.UR.x = b.UR.x+1; + b0.LL.y = b0.UR.y - GD_ranksep(n->graph)/2; + b.LL.x = ND_coord_i(n).x + ND_rw_i(n) + 2; + b.LL.y = b0.UR.y; + b.UR.y = ND_coord_i(n).y + ND_ht_i(n)/2; + b.UR.x += 1; + endp->boxes[0] = b0; + endp->boxes[1] = b; + } + endp->boxn = 2; + break; + case TOP: + b.LL.y = MIN(b.LL.y,P->end.p.y); + endp->boxes[0] = b; + endp->boxn = 1; + break; + } + for (orig = e; ED_edge_type(orig) != NORMAL; orig = ED_to_orig(orig)); + ED_head_port(orig).clip = FALSE; + endp->sidemask = side; + return; + } if (pboxfn && (mask = (*pboxfn) (n, e, 2, &endp->boxes[0], &endp->boxn))) endp->sidemask = mask; diff --git a/lib/common/types.h b/lib/common/types.h index b1710403d..208001c69 100644 --- a/lib/common/types.h +++ b/lib/common/types.h @@ -63,7 +63,7 @@ extern "C" { } inside_t; typedef struct port { /* internal edge endpoint specification */ - point p; /* aiming point */ + point p; /* aiming point relative to node center */ double theta; /* slope in radians */ box *bp; /* if not null, points to bbox of * rectangular area that is port target @@ -72,6 +72,7 @@ extern "C" { boolean constrained; /* if true, constraints such as theta are set */ boolean clip; /* if true, clip end to node/port shape */ unsigned char order; /* for mincross */ + unsigned char side; /* if port is on perimeter of node */ } port; typedef struct { @@ -258,15 +259,15 @@ extern "C" { } adjmatrix_t; typedef struct rank_t { - int n; /* number of nodes in this rank */ - node_t **v; /* ordered list of nodes in rank */ + int n; /* number of nodes in this rank */ + node_t **v; /* ordered list of nodes in rank */ int an; /* globally allocated number of nodes */ - node_t **av; /* allocated list of nodes in rank */ - int ht1, ht2; /* height below/above centerline */ + node_t **av; /* allocated list of nodes in rank */ + int ht1, ht2; /* height below/above centerline */ int pht1, pht2; /* as above, but only primitive nodes */ - boolean candidate; /* for transpose () */ + boolean candidate; /* for transpose () */ boolean valid; - int cache_nc; /* caches number of crossings */ + int cache_nc; /* caches number of crossings */ adjmatrix_t *flat; } rank_t; @@ -291,8 +292,9 @@ extern "C" { int n_flds; textlabel_t *lp; /* n_flds == 0 */ struct field_t **fld; /* n_flds > 0 */ - int LR; /* if box list is horizontal (left to right) */ char *id; /* user's identifier */ + unsigned char LR; /* if box list is horizontal (left to right) */ + unsigned char sides; /* sides of node exposed to field */ } field_t; typedef struct hsbcolor_t { diff --git a/lib/dotgen/dotsplines.c b/lib/dotgen/dotsplines.c index a702bcd12..7180cfc6a 100644 --- a/lib/dotgen/dotsplines.c +++ b/lib/dotgen/dotsplines.c @@ -93,7 +93,7 @@ static box makeflatcomponent(box, box, int, int, int, int, int); static void make_flat_edge(path *, Agedge_t **, int, int); static box makeflatend(box, int, int, box); static void make_regular_edge(path *, Agedge_t **, int, int); -static void makeregularend(box, int, int, box *); +static box makeregularend(box, int, int); static void make_self_edge(path *, Agedge_t **, int, int); static box maximal_bbox(Agnode_t *, Agedge_t *, Agedge_t *); static Agnode_t *neighbor(Agnode_t *, Agedge_t *, Agedge_t *, int); @@ -231,6 +231,8 @@ void dot_splines(graph_t * g) 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))); + LeftBound -= MINW; + RightBound += MINW; for (j = 0; j < GD_rank(g)[i].n; j++) { n = GD_rank(g)[i].v[j]; @@ -646,11 +648,12 @@ static void make_regular_edge(path * P, edge_t ** edges, int ind, int cnt) g = e->tail->graph; tn = e->tail; hn = e->head; - tend.nb = maximal_bbox(tn, NULL, e); + b = tend.nb = maximal_bbox(tn, NULL, e); beginpath(P, e, REGULAREDGE, &tend, spline_merge(e->tail)); - makeregularend(tend.boxes[tend.boxn - 1], BOTTOM, - ND_coord_i(tn).y - GD_rank(tn->graph)[ND_rank(tn)].ht1, - &b); + b.UR.y = tend.boxes[tend.boxn - 1].UR.y; + b.LL.y = tend.boxes[tend.boxn - 1].LL.y; + b = makeregularend(b, BOTTOM, + ND_coord_i(tn).y - GD_rank(tn->graph)[ND_rank(tn)].ht1); if (b.LL.x < b.UR.x && b.LL.y < b.UR.y) tend.boxes[tend.boxn++] = b; longedge = 0; @@ -674,9 +677,8 @@ static void make_regular_edge(path * P, edge_t ** edges, int ind, int cnt) } hend.nb = maximal_bbox(hn, e, ND_out(hn).list[0]); endpath(P, e, REGULAREDGE, &hend, spline_merge(e->head)); - makeregularend(hend.boxes[hend.boxn - 1], TOP, - ND_coord_i(hn).y + - GD_rank(hn->graph)[ND_rank(hn)].ht2, &b); + b = makeregularend(hend.boxes[hend.boxn - 1], TOP, + ND_coord_i(hn).y + GD_rank(hn->graph)[ND_rank(hn)].ht2); 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; @@ -694,21 +696,21 @@ static void make_regular_edge(path * P, edge_t ** edges, int ind, int cnt) boxn = 0; tend.nb = maximal_bbox(tn, ND_in(tn).list[0], e); beginpath(P, e, REGULAREDGE, &tend, spline_merge(e->tail)); - makeregularend(tend.boxes[tend.boxn - 1], BOTTOM, - ND_coord_i(tn).y - - GD_rank(tn->graph)[ND_rank(tn)].ht1, &b); + b = makeregularend(tend.boxes[tend.boxn - 1], BOTTOM, + ND_coord_i(tn).y - GD_rank(tn->graph)[ND_rank(tn)].ht1); 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); + b = hend.nb = maximal_bbox(hn, e, NULL); endpath(P, hackflag ? &fwdedgeb : e, REGULAREDGE, &hend, spline_merge(e->head)); - makeregularend(hend.boxes[hend.boxn - 1], TOP, - ND_coord_i(hn).y + GD_rank(hn->graph)[ND_rank(hn)].ht2, - &b); + b.UR.y = hend.boxes[hend.boxn - 1].UR.y; + b.LL.y = hend.boxes[hend.boxn - 1].LL.y; + b = makeregularend(b, TOP, + ND_coord_i(hn).y + GD_rank(hn->graph)[ND_rank(hn)].ht2); if (b.LL.x < b.UR.x && b.LL.y < b.UR.y) hend.boxes[hend.boxn++] = b; completeregularpath(P, segfirst, e, &tend, &hend, boxes, boxn, @@ -916,6 +918,7 @@ int side, mode, dir, w, h; /* regular edges */ +#define DONT_WANT_ANY_ENDPOINT_PATH_REFINEMENT #ifdef DONT_WANT_ANY_ENDPOINT_PATH_REFINEMENT static void completeregularpath(path * P, edge_t * first, edge_t * last, @@ -1034,17 +1037,23 @@ completeregularpath(path * P, edge_t * first, edge_t * last, } #endif -/* for now, regular edges always go from top to bottom */ -static void makeregularend(box b, int side, int y, box * bp) +/* makeregularend: + * Add box to fill between node and interrank space. Needed because + * nodes in a given rank can differ in height. + * for now, regular edges always go from top to bottom + */ +static box makeregularend(box b, int side, int y) { + box newb; switch (side) { case BOTTOM: - *bp = boxof(b.LL.x, y, b.UR.x, b.LL.y); + newb = 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); + newb = boxof(b.LL.x, b.UR.y, b.UR.x, y); break; } + return newb; } #ifndef DONT_WANT_ANY_ENDPOINT_PATH_REFINEMENT @@ -1147,12 +1156,28 @@ int *boxnp; } #endif +/* adjustregularpath: + * make sure the path is wide enough. + * the % 2 was so that in rank boxes would only be grown if + * they were == 0 while inter-rank boxes could be stretched to a min + * width. + * The list of boxes has three parts: tail boxes, path boxes, and head + * boxes. (Note that because of back edges, the tail boxes might actually + * belong to the head node, and vice versa.) fb is the index of the + * first interrank path box and lb is the last interrank path box. + * If fb > lb, there are none. + * + * The second for loop was added by ek long ago, and apparently is intended + * to guarantee an overlap between adjacent boxes of at least MINW. + * It doesn't do this, and the ifdef'ed part has the potential of moving + * a box within a node for more complex paths. + */ static void adjustregularpath(path * P, int fb, int lb) { box *bp1, *bp2; int i, x; - for (i = 0; i < P->nbox; i++) { + for (i = fb-1; i < lb+1; i++) { bp1 = &P->boxes[i]; if ((i - fb) % 2 == 0) { if (bp1->LL.x >= bp1->UR.x) { @@ -1178,7 +1203,9 @@ static void adjustregularpath(path * P, int fb, int lb) bp1->LL.x = bp2->UR.x - MINW; if (bp1->UR.x - MINW < bp2->LL.x) bp1->UR.x = bp2->LL.x + MINW; - } else { + } +#ifdef OLD + else { if (bp1->LL.x + MINW > bp2->UR.x) { x = (bp1->LL.x + bp2->UR.x) / 2; bp1->LL.x = x - HALFMINW; @@ -1190,6 +1217,7 @@ static void adjustregularpath(path * P, int fb, int lb) bp2->LL.x = x - HALFMINW; } } +#endif } } @@ -1444,9 +1472,12 @@ node_t *n, *adj; return rv; } -static box maximal_bbox(vn, ie, oe) -node_t *vn; -edge_t *ie, *oe; +/* maximal_bbox: + * Return an initial bounding box to be used for building the + * beginning or ending of the path of boxes. + * Height reflects height of tallest node on rank. + */ +static box maximal_bbox(node_t* vn, edge_t* ie, edge_t* oe) { int nb, b; graph_t *g = vn->graph, *left_cl, *right_cl; -- 2.40.0