/* This software may only be used by you under license from AT&T Corp. ("AT&T"). A copy of AT&T's Source Code Agreement is available at AT&T's Internet website having the URL: If you received this software without first entering into a license with AT&T, you have an infringing copy of this software and cannot use it without violating AT&T's intellectual property rights. */ /* Functions related to creating a spline and attaching it to * an edge, starting from a list of control points. */ #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, * check that the edge has arrowheads, and that an endpoint * isn't a merge point where several parts of an edge meet. * (e.g., with edge concentrators). */ static void arrow_clip (edge_t *fe, edge_t *le, point *ps, int *startp, int *endp, bezier *spl, splineInfo* info) { edge_t *e; int i, j, sflag, eflag; for (e = fe; ED_to_orig(e); e = ED_to_orig(e)) ; j = info->swapEnds(e); arrow_flags (e, &sflag, &eflag); if (info->splineMerge (le->head)) eflag = ARR_NONE; if (info->splineMerge (fe->tail)) sflag = ARR_NONE; if (j) {i=sflag; sflag=eflag; eflag=i;} /* swap the two ends */ if (sflag) *startp = arrowStartClip (e, ps, *startp, *endp, spl, sflag); if (eflag) *endp = arrowEndClip (e, ps, *startp, *endp, spl, eflag); } /* shape_clip0: * Clip Bezier to node shape using binary search. * left_inside specifies that curve[0] is inside the node, else * curve[3] is taken as inside. * Assumes ND_shape(n) and ND_shape(n)->insidefn are non-NULL. * See note on shape_clip. */ void shape_clip0 (node_t* n, point curve[4], edge_t* e, boolean left_inside) { int i, save_real_size; boolean found, inside; pointf pt, opt, c[4], seg[4], best[4], *left, *right; double low, high, t; save_real_size = ND_rw_i(n); for (i = 0; i < 4; i++) { c[i].x = curve[i].x - ND_coord_i(n).x; c[i].y = curve[i].y - ND_coord_i(n).y; } if (left_inside) left = NULL, right = seg; else left = seg, right = NULL; found = FALSE; low = 0.0; high = 1.0; if (left_inside) pt = c[0]; else pt = c[3]; do { opt = pt; t = (high + low) / 2.0; pt = Bezier (c, 3, t, left, right); inside = ND_shape(n)->insidefn (n, pt, e); if (inside == FALSE) { for (i = 0; i < 4; i++) best[i] = seg[i]; found = TRUE; } if (inside == left_inside) low = t; else high = t; } while (ABS (opt.x - pt.x) > .5 || ABS (opt.y - pt.y) > .5); if (found == FALSE) for (i = 0; i < 4; i++) best[i] = seg[i]; for (i = 0; i < 4; i++) { curve[i].x = ROUND(best[i].x + ND_coord_i(n).x); curve[i].y = ROUND(best[i].y + ND_coord_i(n).y); } ND_rw_i(n) = save_real_size; } /* shape_clip: * Clip Bezier to node shape * Uses curve[0] to determine which side is inside the node. * NOTE: This test is bad. It is possible for previous call to * shape_clip to produce a Bezier with curve[0] moved to the boundary * for which insidefn(curve[0]) is true. Thus, if the new Bezier is * 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. */ void shape_clip (node_t* n, point curve[4], edge_t* e) { int save_real_size; boolean left_inside; pointf c; if (ND_shape(n) == NULL) return; if (ND_shape(n)->insidefn == NULL) return; save_real_size = ND_rw_i(n); c.x = curve[0].x - ND_coord_i(n).x; c.y = curve[0].y - ND_coord_i(n).y; left_inside = ND_shape(n)->insidefn (n, c, e); ND_rw_i(n) = save_real_size; shape_clip0 (n, curve, e, left_inside); } /* new_spline: * Create and attach a new bezier of size sz to the edge d */ bezier* new_spline (edge_t* e, int sz) { bezier *rv; while (ED_edge_type(e) != NORMAL) e = ED_to_orig(e); if (ED_spl(e) == NULL) ED_spl(e) = NEW (splines); ED_spl(e)->list = ALLOC (ED_spl(e)->size + 1, ED_spl(e)->list, bezier); rv = &(ED_spl(e)->list[ED_spl(e)->size++]); rv->list = N_NEW (sz, point); rv->size = sz; rv->sflag = rv->eflag = FALSE; return rv; } /* update_bb: * Update the bounding box of g based on the addition of * point p. */ static void update_bb(graph_t* g, point pt) { if (pt.x > GD_bb(g).UR.x) GD_bb(g).UR.x = pt.x; if (pt.y > GD_bb(g).UR.y) GD_bb(g).UR.y = pt.y; if (pt.x < GD_bb(g).LL.x) GD_bb(g).LL.x = pt.x; if (pt.y < GD_bb(g).LL.y) GD_bb(g).LL.y = pt.y; } /* clip_and_install: * Given a raw spline (pn control points in ps), representing * a path from edge fe ending in edge le, clip the ends to * the node boundaries and attach the resulting spline to the * edge. */ void clip_and_install (edge_t* fe, edge_t* le, point* ps, int pn, splineInfo* info) { pointf p2; bezier *newspl; node_t *tn, *hn; int start, end, i; graph_t *g; edge_t *orig; tn = fe->tail; hn = le->head; g = tn->graph; newspl = new_spline (fe, pn); for (orig = fe; ED_edge_type(orig) != NORMAL; orig = ED_to_orig(orig)); /* may be a reversed flat edge */ if ((tn->u.rank == hn->u.rank) && (tn->u.order > hn->u.order)) {node_t *tmp; tmp = hn; hn = tn; tn = tmp;} /* spline may be interior to node */ if (wantclip(orig,tn) && ND_shape(tn) && ND_shape(tn)->insidefn) { for (start=0; start < pn - 4; start+=3) { p2.x = ps[start+3].x - ND_coord_i(tn).x; p2.y = ps[start+3].y - ND_coord_i(tn).y; if (ND_shape(tn)->insidefn (tn, p2, fe) == FALSE) break; } shape_clip0 (tn, &ps[start], fe, TRUE); } else start = 0; if (wantclip(orig,hn) && ND_shape(hn) && ND_shape(hn)->insidefn) { for (end = pn - 4; end > 0; end -= 3) { p2.x = ps[end].x - ND_coord_i(hn).x; p2.y = ps[end].y - ND_coord_i(hn).y; if (ND_shape(hn)->insidefn (hn, p2, le) == FALSE) break; } shape_clip0 (hn, &ps[end], le, FALSE); } else end = pn - 4; for (; start < pn - 4; start+=3) if (ps[start].x != ps[start + 3].x || ps[start].y != ps[start + 3].y) break; for (; end > 0; end -= 3) if (ps[end].x != ps[end + 3].x || ps[end].y != ps[end + 3].y) break; arrow_clip (fe, le, ps, &start, &end, newspl, info); for (i = start; i < end + 4; i++) { point pt; pt = newspl->list[i - start] = ps[i]; update_bb(g,pt); } newspl->size = end - start + 4; }