|
Graphviz
2.31.20130525.0447
|
00001 /* $Id$ $Revision$ */ 00002 /* vim:set shiftwidth=4 ts=8: */ 00003 00004 /************************************************************************* 00005 * Copyright (c) 2011 AT&T Intellectual Property 00006 * All rights reserved. This program and the accompanying materials 00007 * are made available under the terms of the Eclipse Public License v1.0 00008 * which accompanies this distribution, and is available at 00009 * http://www.eclipse.org/legal/epl-v10.html 00010 * 00011 * Contributors: See CVS logs. Details at http://www.graphviz.org/ 00012 *************************************************************************/ 00013 00014 00015 /* Functions related to creating a spline and attaching it to 00016 * an edge, starting from a list of control points. 00017 */ 00018 00019 #include "render.h" 00020 00021 #ifdef DEBUG 00022 static int debugleveln(edge_t* e, int i) 00023 { 00024 return (GD_showboxes(agraphof(aghead(e))) == i || 00025 GD_showboxes(agraphof(agtail(e))) == i || 00026 ED_showboxes(e) == i || 00027 ND_showboxes(aghead(e)) == i || 00028 ND_showboxes(agtail(e)) == i); 00029 } 00030 00031 static void showPoints(pointf ps[], int pn) 00032 { 00033 char buf[BUFSIZ]; 00034 int newcnt = Show_cnt + pn + 3; 00035 int bi, li; 00036 00037 Show_boxes = ALLOC(newcnt+2,Show_boxes,char*); 00038 li = Show_cnt+1; 00039 Show_boxes[li++] = strdup ("%% self list"); 00040 Show_boxes[li++] = strdup ("dbgstart"); 00041 for (bi = 0; bi < pn; bi++) { 00042 sprintf(buf, "%.5g %.5g point", ps[bi].x, ps[bi].y); 00043 Show_boxes[li++] = strdup (buf); 00044 } 00045 Show_boxes[li++] = strdup ("grestore"); 00046 00047 Show_cnt = newcnt; 00048 Show_boxes[Show_cnt+1] = NULL; 00049 } 00050 #endif 00051 00052 /* arrow_clip: 00053 * Clip arrow to node boundary. 00054 * The real work is done elsewhere. Here we get the real edge, 00055 * check that the edge has arrowheads, and that an endpoint 00056 * isn't a merge point where several parts of an edge meet. 00057 * (e.g., with edge concentrators). 00058 */ 00059 static void 00060 arrow_clip(edge_t * fe, node_t * hn, 00061 pointf * ps, int *startp, int *endp, 00062 bezier * spl, splineInfo * info) 00063 { 00064 edge_t *e; 00065 int i, j, sflag, eflag; 00066 00067 for (e = fe; ED_to_orig(e); e = ED_to_orig(e)); 00068 00069 if (info->ignoreSwap) 00070 j = 0; 00071 else 00072 j = info->swapEnds(e); 00073 arrow_flags(e, &sflag, &eflag); 00074 if (info->splineMerge(hn)) 00075 eflag = ARR_NONE; 00076 if (info->splineMerge(agtail(fe))) 00077 sflag = ARR_NONE; 00078 /* swap the two ends */ 00079 if (j) { 00080 i = sflag; 00081 sflag = eflag; 00082 eflag = i; 00083 } 00084 if (info->isOrtho) { 00085 if (eflag || sflag) 00086 arrowOrthoClip(e, ps, *startp, *endp, spl, sflag, eflag); 00087 } 00088 else { 00089 if (sflag) 00090 *startp = 00091 arrowStartClip(e, ps, *startp, *endp, spl, sflag); 00092 if (eflag) 00093 *endp = 00094 arrowEndClip(e, ps, *startp, *endp, spl, eflag); 00095 } 00096 } 00097 00098 /* bezier_clip 00099 * Clip bezier to shape using binary search. 00100 * The details of the shape are passed in the inside_context; 00101 * The function providing the inside test is passed as a parameter. 00102 * left_inside specifies that sp[0] is inside the node, 00103 * else sp[3] is taken as inside. 00104 * The points p are in node coordinates. 00105 */ 00106 void bezier_clip(inside_t * inside_context, 00107 boolean(*inside) (inside_t * inside_context, pointf p), 00108 pointf * sp, boolean left_inside) 00109 { 00110 pointf seg[4], best[4], pt, opt, *left, *right; 00111 double low, high, t, *idir, *odir; 00112 boolean found; 00113 int i; 00114 00115 if (left_inside) { 00116 left = NULL; 00117 right = seg; 00118 pt = sp[0]; 00119 idir = &low; 00120 odir = &high; 00121 } else { 00122 left = seg; 00123 right = NULL; 00124 pt = sp[3]; 00125 idir = &high; 00126 odir = &low; 00127 } 00128 found = FALSE; 00129 low = 0.0; 00130 high = 1.0; 00131 do { 00132 opt = pt; 00133 t = (high + low) / 2.0; 00134 pt = Bezier(sp, 3, t, left, right); 00135 if (inside(inside_context, pt)) { 00136 *idir = t; 00137 } else { 00138 for (i = 0; i < 4; i++) 00139 best[i] = seg[i]; 00140 found = TRUE; 00141 *odir = t; 00142 } 00143 } while (ABS(opt.x - pt.x) > .5 || ABS(opt.y - pt.y) > .5); 00144 if (found) 00145 for (i = 0; i < 4; i++) 00146 sp[i] = best[i]; 00147 else 00148 for (i = 0; i < 4; i++) 00149 sp[i] = seg[i]; 00150 } 00151 00152 /* shape_clip0: 00153 * Clip Bezier to node shape using binary search. 00154 * left_inside specifies that curve[0] is inside the node, else 00155 * curve[3] is taken as inside. 00156 * Assumes ND_shape(n) and ND_shape(n)->fns->insidefn are non-NULL. 00157 * See note on shape_clip. 00158 */ 00159 static void 00160 shape_clip0(inside_t * inside_context, node_t * n, pointf curve[4], 00161 boolean left_inside) 00162 { 00163 int i; 00164 double save_real_size; 00165 pointf c[4]; 00166 00167 save_real_size = ND_rw(n); 00168 for (i = 0; i < 4; i++) { 00169 c[i].x = curve[i].x - ND_coord(n).x; 00170 c[i].y = curve[i].y - ND_coord(n).y; 00171 } 00172 00173 bezier_clip(inside_context, ND_shape(n)->fns->insidefn, c, 00174 left_inside); 00175 00176 for (i = 0; i < 4; i++) { 00177 curve[i].x = c[i].x + ND_coord(n).x; 00178 curve[i].y = c[i].y + ND_coord(n).y; 00179 } 00180 ND_rw(n) = save_real_size; 00181 } 00182 00183 /* shape_clip: 00184 * Clip Bezier to node shape 00185 * Uses curve[0] to determine which which side is inside the node. 00186 * NOTE: This test is bad. It is possible for previous call to 00187 * shape_clip to produce a Bezier with curve[0] moved to the boundary 00188 * for which insidefn(curve[0]) is true. Thus, if the new Bezier is 00189 * fed back to shape_clip, it will again assume left_inside is true. 00190 * To be safe, shape_clip0 should guarantee that the computed boundary 00191 * point fails insidefn. 00192 * The edge e is used to provide a port box. If NULL, the spline is 00193 * clipped to the node shape. 00194 */ 00195 void shape_clip(node_t * n, pointf curve[4]) 00196 { 00197 double save_real_size; 00198 boolean left_inside; 00199 pointf c; 00200 inside_t inside_context; 00201 00202 if (ND_shape(n) == NULL || ND_shape(n)->fns->insidefn == NULL) 00203 return; 00204 00205 inside_context.s.n = n; 00206 inside_context.s.bp = NULL; 00207 save_real_size = ND_rw(n); 00208 c.x = curve[0].x - ND_coord(n).x; 00209 c.y = curve[0].y - ND_coord(n).y; 00210 left_inside = ND_shape(n)->fns->insidefn(&inside_context, c); 00211 ND_rw(n) = save_real_size; 00212 shape_clip0(&inside_context, n, curve, left_inside); 00213 } 00214 00215 /* new_spline: 00216 * Create and attach a new bezier of size sz to the edge d 00217 */ 00218 bezier *new_spline(edge_t * e, int sz) 00219 { 00220 bezier *rv; 00221 while (ED_edge_type(e) != NORMAL) 00222 e = ED_to_orig(e); 00223 if (ED_spl(e) == NULL) 00224 ED_spl(e) = NEW(splines); 00225 ED_spl(e)->list = ALLOC(ED_spl(e)->size + 1, ED_spl(e)->list, bezier); 00226 rv = &(ED_spl(e)->list[ED_spl(e)->size++]); 00227 rv->list = N_NEW(sz, pointf); 00228 rv->size = sz; 00229 rv->sflag = rv->eflag = FALSE; 00230 rv->sp.x = rv->sp.y = rv->ep.x = rv->ep.y = 0; 00231 return rv; 00232 } 00233 00234 /* clip_and_install: 00235 * Given a raw spline (pn control points in ps), representing 00236 * a path from edge agtail(fe) ending in node hn, clip the ends to 00237 * the node boundaries and attach the resulting spline to the 00238 * edge. 00239 */ 00240 void 00241 clip_and_install(edge_t * fe, node_t * hn, pointf * ps, int pn, 00242 splineInfo * info) 00243 { 00244 pointf p2; 00245 bezier *newspl; 00246 node_t *tn; 00247 int start, end, i, clipTail, clipHead; 00248 graph_t *g; 00249 edge_t *orig; 00250 boxf *tbox, *hbox; 00251 inside_t inside_context; 00252 00253 tn = agtail(fe); 00254 g = agraphof(tn); 00255 newspl = new_spline(fe, pn); 00256 00257 for (orig = fe; ED_edge_type(orig) != NORMAL; orig = ED_to_orig(orig)); 00258 00259 /* may be a reversed flat edge */ 00260 if (!info->ignoreSwap && (ND_rank(tn) == ND_rank(hn)) && (ND_order(tn) > ND_order(hn))) { 00261 node_t *tmp; 00262 tmp = hn; 00263 hn = tn; 00264 tn = tmp; 00265 } 00266 if (tn == agtail(orig)) { 00267 clipTail = ED_tail_port(orig).clip; 00268 clipHead = ED_head_port(orig).clip; 00269 tbox = ED_tail_port(orig).bp; 00270 hbox = ED_head_port(orig).bp; 00271 } 00272 else { /* fe and orig are reversed */ 00273 clipTail = ED_head_port(orig).clip; 00274 clipHead = ED_tail_port(orig).clip; 00275 hbox = ED_tail_port(orig).bp; 00276 tbox = ED_head_port(orig).bp; 00277 } 00278 00279 /* spline may be interior to node */ 00280 if(clipTail && ND_shape(tn) && ND_shape(tn)->fns->insidefn) { 00281 inside_context.s.n = tn; 00282 inside_context.s.bp = tbox; 00283 for (start = 0; start < pn - 4; start += 3) { 00284 p2.x = ps[start + 3].x - ND_coord(tn).x; 00285 p2.y = ps[start + 3].y - ND_coord(tn).y; 00286 if (ND_shape(tn)->fns->insidefn(&inside_context, p2) == FALSE) 00287 break; 00288 } 00289 shape_clip0(&inside_context, tn, &ps[start], TRUE); 00290 } else 00291 start = 0; 00292 if(clipHead && ND_shape(hn) && ND_shape(hn)->fns->insidefn) { 00293 inside_context.s.n = hn; 00294 inside_context.s.bp = hbox; 00295 for (end = pn - 4; end > 0; end -= 3) { 00296 p2.x = ps[end].x - ND_coord(hn).x; 00297 p2.y = ps[end].y - ND_coord(hn).y; 00298 if (ND_shape(hn)->fns->insidefn(&inside_context, p2) == FALSE) 00299 break; 00300 } 00301 shape_clip0(&inside_context, hn, &ps[end], FALSE); 00302 } else 00303 end = pn - 4; 00304 for (; start < pn - 4; start += 3) 00305 if (! APPROXEQPT(ps[start], ps[start + 3], MILLIPOINT)) 00306 break; 00307 for (; end > 0; end -= 3) 00308 if (! APPROXEQPT(ps[end], ps[end + 3], MILLIPOINT)) 00309 break; 00310 arrow_clip(fe, hn, ps, &start, &end, newspl, info); 00311 for (i = start; i < end + 4; ) { 00312 pointf cp[4]; 00313 newspl->list[i - start] = ps[i]; 00314 cp[0] = ps[i]; 00315 i++; 00316 if ( i >= end + 4) 00317 break; 00318 newspl->list[i - start] = ps[i]; 00319 cp[1] = ps[i]; 00320 i++; 00321 newspl->list[i - start] = ps[i]; 00322 cp[2] = ps[i]; 00323 i++; 00324 cp[3] = ps[i]; 00325 update_bb_bz(&GD_bb(g), cp); 00326 } 00327 newspl->size = end - start + 4; 00328 } 00329 00330 static double 00331 conc_slope(node_t* n) 00332 { 00333 double s_in, s_out, m_in, m_out; 00334 int cnt_in, cnt_out; 00335 pointf p; 00336 edge_t *e; 00337 00338 s_in = s_out = 0.0; 00339 for (cnt_in = 0; (e = ND_in(n).list[cnt_in]); cnt_in++) 00340 s_in += ND_coord(agtail(e)).x; 00341 for (cnt_out = 0; (e = ND_out(n).list[cnt_out]); cnt_out++) 00342 s_out += ND_coord(aghead(e)).x; 00343 p.x = ND_coord(n).x - (s_in / cnt_in); 00344 p.y = ND_coord(n).y - ND_coord(agtail(ND_in(n).list[0])).y; 00345 m_in = atan2(p.y, p.x); 00346 p.x = (s_out / cnt_out) - ND_coord(n).x; 00347 p.y = ND_coord(aghead(ND_out(n).list[0])).y - ND_coord(n).y; 00348 m_out = atan2(p.y, p.x); 00349 return ((m_in + m_out) / 2.0); 00350 } 00351 00352 void add_box(path * P, boxf b) 00353 { 00354 if (b.LL.x < b.UR.x && b.LL.y < b.UR.y) 00355 P->boxes[P->nbox++] = b; 00356 } 00357 00358 /* beginpath: 00359 * Set up boxes near the tail node. 00360 * For regular nodes, the result should be a list of contiguous rectangles 00361 * such that the last one has the smallest LL.y and its LL.y is above 00362 * the bottom of the rank (rank.ht1). 00363 * 00364 * For flat edges, we assume endp->sidemask has been set. For regular 00365 * edges, we set this, but it doesn't appear to be needed any more. 00366 * 00367 * In many cases, we tweak the x or y coordinate of P->start.p by 1. 00368 * This is because of a problem in the path routing code. If the starting 00369 * point actually lies on the polygon, in some cases, the router gets 00370 * confused and routes the path outside the polygon. So, the offset ensures 00371 * the starting point is in the polygon. 00372 * 00373 * FIX: Creating the initial boxes only really works for rankdir=TB and 00374 * rankdir=LR. For the others, we rely on compassPort flipping the side 00375 * and then assume that the node shape has top-bottom symmetry. Since we 00376 * at present only put compass points on the bounding box, this works. 00377 * If we attempt to implement compass points on actual node perimeters, 00378 * something major will probably be necessary. Doing the coordinate 00379 * flip (postprocess) before spline routing will be too disruptive. The 00380 * correct solution is probably to have beginpath/endpath create the 00381 * boxes assuming an inverted node. Note that compassPort already does 00382 * some flipping. Even better would be to allow the *_path function 00383 * to provide a polygon. 00384 * 00385 * The extra space provided by FUDGE-2 prevents the edge from getting 00386 * too close the side of the node. 00387 * 00388 * The HT2 macro is needed because dot calculates ht2 and ht1 of ranks using 00389 * integers. 00390 */ 00391 #define FUDGE 2 00392 #define HT2(n) ((ROUND(ND_ht(n))+1)/2) 00393 00394 void 00395 beginpath(path * P, edge_t * e, int et, pathend_t * endp, boolean merge) 00396 { 00397 int side, mask; 00398 node_t *n; 00399 int (*pboxfn) (node_t*, port*, int, boxf*, int*); 00400 00401 n = agtail(e); 00402 00403 if (ED_tail_port(e).dyna) 00404 ED_tail_port(e) = resolvePort(agtail(e), aghead(e), &ED_tail_port(e)); 00405 if (ND_shape(n)) 00406 pboxfn = ND_shape(n)->fns->pboxfn; 00407 else 00408 pboxfn = NULL; 00409 P->start.p = add_pointf(ND_coord(n), ED_tail_port(e).p); 00410 if (merge) { 00411 /*P->start.theta = - M_PI / 2; */ 00412 P->start.theta = conc_slope(agtail(e)); 00413 P->start.constrained = TRUE; 00414 } else { 00415 if (ED_tail_port(e).constrained) { 00416 P->start.theta = ED_tail_port(e).theta; 00417 P->start.constrained = TRUE; 00418 } else 00419 P->start.constrained = FALSE; 00420 } 00421 P->nbox = 0; 00422 P->data = (void *) e; 00423 endp->np = P->start.p; 00424 if ((et == REGULAREDGE) && (ND_node_type(n) == NORMAL) && ((side = ED_tail_port(e).side))) { 00425 edge_t* orig; 00426 boxf b0, b = endp->nb; 00427 if (side & TOP) { 00428 endp->sidemask = TOP; 00429 if (P->start.p.x < ND_coord(n).x) { /* go left */ 00430 b0.LL.x = b.LL.x - 1; 00431 /* b0.LL.y = ND_coord(n).y + HT2(n); */ 00432 b0.LL.y = P->start.p.y; 00433 b0.UR.x = b.UR.x; 00434 b0.UR.y = ND_coord(n).y + HT2(n) + GD_ranksep(agraphof(n))/2; 00435 b.UR.x = ND_coord(n).x - ND_lw(n) - (FUDGE-2); 00436 b.UR.y = b0.LL.y; 00437 b.LL.y = ND_coord(n).y - HT2(n); 00438 b.LL.x -= 1; 00439 endp->boxes[0] = b0; 00440 endp->boxes[1] = b; 00441 } 00442 else { 00443 b0.LL.x = b.LL.x; 00444 b0.LL.y = P->start.p.y; 00445 /* b0.LL.y = ND_coord(n).y + HT2(n); */ 00446 b0.UR.x = b.UR.x+1; 00447 b0.UR.y = ND_coord(n).y + HT2(n) + GD_ranksep(agraphof(n))/2; 00448 b.LL.x = ND_coord(n).x + ND_rw(n) + (FUDGE-2); 00449 b.UR.y = b0.LL.y; 00450 b.LL.y = ND_coord(n).y - HT2(n); 00451 b.UR.x += 1; 00452 endp->boxes[0] = b0; 00453 endp->boxes[1] = b; 00454 } 00455 P->start.p.y += 1; 00456 endp->boxn = 2; 00457 } 00458 else if (side & BOTTOM) { 00459 endp->sidemask = BOTTOM; 00460 b.UR.y = MAX(b.UR.y,P->start.p.y); 00461 endp->boxes[0] = b; 00462 endp->boxn = 1; 00463 P->start.p.y -= 1; 00464 } 00465 else if (side & LEFT) { 00466 endp->sidemask = LEFT; 00467 b.UR.x = P->start.p.x; 00468 b.LL.y = ND_coord(n).y - HT2(n); 00469 b.UR.y = P->start.p.y; 00470 endp->boxes[0] = b; 00471 endp->boxn = 1; 00472 P->start.p.x -= 1; 00473 } 00474 else { 00475 endp->sidemask = RIGHT; 00476 b.LL.x = P->start.p.x; 00477 b.LL.y = ND_coord(n).y - HT2(n); 00478 b.UR.y = P->start.p.y; 00479 endp->boxes[0] = b; 00480 endp->boxn = 1; 00481 P->start.p.x += 1; 00482 } 00483 for (orig = e; ED_edge_type(orig) != NORMAL; orig = ED_to_orig(orig)); 00484 if (n == agtail(orig)) 00485 ED_tail_port(orig).clip = FALSE; 00486 else 00487 ED_head_port(orig).clip = FALSE; 00488 return; 00489 } 00490 if ((et == FLATEDGE) && ((side = ED_tail_port(e).side))) { 00491 boxf b0, b = endp->nb; 00492 edge_t* orig; 00493 if (side & TOP) { 00494 b.LL.y = MIN(b.LL.y,P->start.p.y); 00495 endp->boxes[0] = b; 00496 endp->boxn = 1; 00497 P->start.p.y += 1; 00498 } 00499 else if (side & BOTTOM) { 00500 if (endp->sidemask == TOP) { 00501 b0.UR.y = ND_coord(n).y - HT2(n); 00502 b0.UR.x = b.UR.x+1; 00503 b0.LL.x = P->start.p.x; 00504 b0.LL.y = b0.UR.y - GD_ranksep(agraphof(n))/2; 00505 b.LL.x = ND_coord(n).x + ND_rw(n) + (FUDGE-2); 00506 b.LL.y = b0.UR.y; 00507 b.UR.y = ND_coord(n).y + HT2(n); 00508 b.UR.x += 1; 00509 endp->boxes[0] = b0; 00510 endp->boxes[1] = b; 00511 endp->boxn = 2; 00512 } 00513 else { 00514 b.UR.y = MAX(b.UR.y,P->start.p.y); 00515 endp->boxes[0] = b; 00516 endp->boxn = 1; 00517 } 00518 P->start.p.y -= 1; 00519 } 00520 else if (side & LEFT) { 00521 b.UR.x = P->start.p.x+1; 00522 if (endp->sidemask == TOP) { 00523 b.UR.y = ND_coord(n).y + HT2(n); 00524 b.LL.y = P->start.p.y-1; 00525 } 00526 else { 00527 b.LL.y = ND_coord(n).y - HT2(n); 00528 b.UR.y = P->start.p.y+1; 00529 } 00530 endp->boxes[0] = b; 00531 endp->boxn = 1; 00532 P->start.p.x -= 1; 00533 } 00534 else { 00535 b.LL.x = P->start.p.x; 00536 if (endp->sidemask == TOP) { 00537 b.UR.y = ND_coord(n).y + HT2(n); 00538 b.LL.y = P->start.p.y; 00539 } 00540 else { 00541 b.LL.y = ND_coord(n).y - HT2(n); 00542 b.UR.y = P->start.p.y+1; 00543 } 00544 endp->boxes[0] = b; 00545 endp->boxn = 1; 00546 P->start.p.x += 1; 00547 } 00548 for (orig = e; ED_edge_type(orig) != NORMAL; orig = ED_to_orig(orig)); 00549 if (n == agtail(orig)) 00550 ED_tail_port(orig).clip = FALSE; 00551 else 00552 ED_head_port(orig).clip = FALSE; 00553 endp->sidemask = side; 00554 return; 00555 } 00556 00557 if (et == REGULAREDGE) side = BOTTOM; 00558 else side = endp->sidemask; /* for flat edges */ 00559 if (pboxfn 00560 && (mask = (*pboxfn) (n, &ED_tail_port(e), side, &endp->boxes[0], &endp->boxn))) 00561 endp->sidemask = mask; 00562 else { 00563 endp->boxes[0] = endp->nb; 00564 endp->boxn = 1; 00565 00566 switch (et) { 00567 case SELFEDGE: 00568 /* moving the box UR.y by + 1 avoids colinearity between 00569 port point and box that confuses Proutespline(). it's 00570 a bug in Proutespline() but this is the easiest fix. */ 00571 assert(0); /* at present, we don't use beginpath for selfedges */ 00572 endp->boxes[0].UR.y = P->start.p.y - 1; 00573 endp->sidemask = BOTTOM; 00574 break; 00575 case FLATEDGE: 00576 if (endp->sidemask == TOP) 00577 endp->boxes[0].LL.y = P->start.p.y; 00578 else 00579 endp->boxes[0].UR.y = P->start.p.y; 00580 break; 00581 case REGULAREDGE: 00582 endp->boxes[0].UR.y = P->start.p.y; 00583 endp->sidemask = BOTTOM; 00584 P->start.p.y -= 1; 00585 break; 00586 } 00587 } 00588 } 00589 00590 void endpath(path * P, edge_t * e, int et, pathend_t * endp, boolean merge) 00591 { 00592 int side, mask; 00593 node_t *n; 00594 int (*pboxfn) (node_t* n, port*, int, boxf*, int*); 00595 00596 n = aghead(e); 00597 00598 if (ED_head_port(e).dyna) 00599 ED_head_port(e) = resolvePort(aghead(e), agtail(e), &ED_head_port(e)); 00600 if (ND_shape(n)) 00601 pboxfn = ND_shape(n)->fns->pboxfn; 00602 else 00603 pboxfn = NULL; 00604 P->end.p = add_pointf(ND_coord(n), ED_head_port(e).p); 00605 if (merge) { 00606 /*P->end.theta = M_PI / 2; */ 00607 P->end.theta = conc_slope(aghead(e)) + M_PI; 00608 assert(P->end.theta < 2 * M_PI); 00609 P->end.constrained = TRUE; 00610 } else { 00611 if (ED_head_port(e).constrained) { 00612 P->end.theta = ED_head_port(e).theta; 00613 P->end.constrained = TRUE; 00614 } else 00615 P->end.constrained = FALSE; 00616 } 00617 endp->np = P->end.p; 00618 if ((et == REGULAREDGE) && (ND_node_type(n) == NORMAL) && ((side = ED_head_port(e).side))) { 00619 edge_t* orig; 00620 boxf b0, b = endp->nb; 00621 if (side & TOP) { 00622 endp->sidemask = TOP; 00623 b.LL.y = MIN(b.LL.y,P->end.p.y); 00624 endp->boxes[0] = b; 00625 endp->boxn = 1; 00626 P->end.p.y += 1; 00627 } 00628 else if (side & BOTTOM) { 00629 endp->sidemask = BOTTOM; 00630 if (P->end.p.x < ND_coord(n).x) { /* go left */ 00631 b0.LL.x = b.LL.x-1; 00632 /* b0.UR.y = ND_coord(n).y - HT2(n); */ 00633 b0.UR.y = P->end.p.y; 00634 b0.UR.x = b.UR.x; 00635 b0.LL.y = ND_coord(n).y - HT2(n) - GD_ranksep(agraphof(n))/2; 00636 b.UR.x = ND_coord(n).x - ND_lw(n) - (FUDGE-2); 00637 b.LL.y = b0.UR.y; 00638 b.UR.y = ND_coord(n).y + HT2(n); 00639 b.LL.x -= 1; 00640 endp->boxes[0] = b0; 00641 endp->boxes[1] = b; 00642 } 00643 else { 00644 b0.LL.x = b.LL.x; 00645 b0.UR.y = P->end.p.y; 00646 /* b0.UR.y = ND_coord(n).y - HT2(n); */ 00647 b0.UR.x = b.UR.x+1; 00648 b0.LL.y = ND_coord(n).y - HT2(n) - GD_ranksep(agraphof(n))/2; 00649 b.LL.x = ND_coord(n).x + ND_rw(n) + (FUDGE-2); 00650 b.LL.y = b0.UR.y; 00651 b.UR.y = ND_coord(n).y + HT2(n); 00652 b.UR.x += 1; 00653 endp->boxes[0] = b0; 00654 endp->boxes[1] = b; 00655 } 00656 endp->boxn = 2; 00657 P->end.p.y -= 1; 00658 } 00659 else if (side & LEFT) { 00660 endp->sidemask = LEFT; 00661 b.UR.x = P->end.p.x; 00662 b.UR.y = ND_coord(n).y + HT2(n); 00663 b.LL.y = P->end.p.y; 00664 endp->boxes[0] = b; 00665 endp->boxn = 1; 00666 P->end.p.x -= 1; 00667 } 00668 else { 00669 endp->sidemask = RIGHT; 00670 b.LL.x = P->end.p.x; 00671 b.UR.y = ND_coord(n).y + HT2(n); 00672 b.LL.y = P->end.p.y; 00673 endp->boxes[0] = b; 00674 endp->boxn = 1; 00675 P->end.p.x += 1; 00676 } 00677 for (orig = e; ED_edge_type(orig) != NORMAL; orig = ED_to_orig(orig)); 00678 if (n == aghead(orig)) 00679 ED_head_port(orig).clip = FALSE; 00680 else 00681 ED_tail_port(orig).clip = FALSE; 00682 endp->sidemask = side; 00683 return; 00684 } 00685 00686 if ((et == FLATEDGE) && ((side = ED_head_port(e).side))) { 00687 boxf b0, b = endp->nb; 00688 edge_t* orig; 00689 if (side & TOP) { 00690 b.LL.y = MIN(b.LL.y,P->end.p.y); 00691 endp->boxes[0] = b; 00692 endp->boxn = 1; 00693 P->end.p.y += 1; 00694 } 00695 else if (side & BOTTOM) { 00696 if (endp->sidemask == TOP) { 00697 b0.LL.x = b.LL.x-1; 00698 b0.UR.y = ND_coord(n).y - HT2(n); 00699 b0.UR.x = P->end.p.x; 00700 b0.LL.y = b0.UR.y - GD_ranksep(agraphof(n))/2; 00701 b.UR.x = ND_coord(n).x - ND_lw(n) - 2; 00702 b.LL.y = b0.UR.y; 00703 b.UR.y = ND_coord(n).y + HT2(n); 00704 b.LL.x -= 1; 00705 endp->boxes[0] = b0; 00706 endp->boxes[1] = b; 00707 endp->boxn = 2; 00708 } 00709 else { 00710 b.UR.y = MAX(b.UR.y,P->start.p.y); 00711 endp->boxes[0] = b; 00712 endp->boxn = 1; 00713 } 00714 P->end.p.y -= 1; 00715 } 00716 else if (side & LEFT) { 00717 b.UR.x = P->end.p.x+1; 00718 if (endp->sidemask == TOP) { 00719 b.UR.y = ND_coord(n).y + HT2(n); 00720 b.LL.y = P->end.p.y-1; 00721 } 00722 else { 00723 b.LL.y = ND_coord(n).y - HT2(n); 00724 b.UR.y = P->end.p.y+1; 00725 } 00726 endp->boxes[0] = b; 00727 endp->boxn = 1; 00728 P->end.p.x -= 1; 00729 } 00730 else { 00731 b.LL.x = P->end.p.x-1; 00732 if (endp->sidemask == TOP) { 00733 b.UR.y = ND_coord(n).y + HT2(n); 00734 b.LL.y = P->end.p.y-1; 00735 } 00736 else { 00737 b.LL.y = ND_coord(n).y - HT2(n); 00738 b.UR.y = P->end.p.y; 00739 } 00740 endp->boxes[0] = b; 00741 endp->boxn = 1; 00742 P->end.p.x += 1; 00743 } 00744 for (orig = e; ED_edge_type(orig) != NORMAL; orig = ED_to_orig(orig)); 00745 if (n == aghead(orig)) 00746 ED_head_port(orig).clip = FALSE; 00747 else 00748 ED_tail_port(orig).clip = FALSE; 00749 endp->sidemask = side; 00750 return; 00751 } 00752 00753 if (et == REGULAREDGE) side = TOP; 00754 else side = endp->sidemask; /* for flat edges */ 00755 if (pboxfn 00756 && (mask = (*pboxfn) (n, &ED_head_port(e), side, &endp->boxes[0], &endp->boxn))) 00757 endp->sidemask = mask; 00758 else { 00759 endp->boxes[0] = endp->nb; 00760 endp->boxn = 1; 00761 00762 switch (et) { 00763 case SELFEDGE: 00764 /* offset of -1 is symmetric w.r.t. beginpath() 00765 * FIXME: is any of this right? what if self-edge 00766 * doesn't connect from BOTTOM to TOP??? */ 00767 assert(0); /* at present, we don't use endpath for selfedges */ 00768 endp->boxes[0].LL.y = P->end.p.y + 1; 00769 endp->sidemask = TOP; 00770 break; 00771 case FLATEDGE: 00772 if (endp->sidemask == TOP) 00773 endp->boxes[0].LL.y = P->end.p.y; 00774 else 00775 endp->boxes[0].UR.y = P->end.p.y; 00776 break; 00777 case REGULAREDGE: 00778 endp->boxes[0].LL.y = P->end.p.y; 00779 endp->sidemask = TOP; 00780 P->end.p.y += 1; 00781 break; 00782 } 00783 } 00784 } 00785 00786 00787 static int convert_sides_to_points(int tail_side, int head_side) 00788 { 00789 int vertices[] = {12,4,6,2,3,1,9,8}; //the cumulative side value of each node point 00790 int i, tail_i, head_i; 00791 int pair_a[8][8] = { //array of possible node point pairs 00792 {11,12,13,14,15,16,17,18}, 00793 {21,22,23,24,25,26,27,28}, 00794 {31,32,33,34,35,36,37,38}, 00795 {41,42,43,44,45,46,47,48}, 00796 {51,52,53,54,55,56,57,58}, 00797 {61,62,63,64,65,66,67,68}, 00798 {71,72,73,74,75,76,77,78}, 00799 {81,82,83,84,85,86,87,88} 00800 }; 00801 00802 tail_i = head_i = -1; 00803 for(i=0;i< 8; i++){ 00804 if(head_side == vertices[i]){ 00805 head_i = i; 00806 break; 00807 } 00808 } 00809 for(i=0;i< 8; i++){ 00810 if(tail_side == vertices[i]){ 00811 tail_i = i; 00812 break; 00813 } 00814 } 00815 00816 if( tail_i < 0 || head_i < 0) 00817 return 0; 00818 else 00819 return pair_a[tail_i][head_i]; 00820 } 00821 00822 00823 static void selfBottom (edge_t* edges[], int ind, int cnt, 00824 double sizex, double stepy, splineInfo* sinfo) 00825 { 00826 pointf tp, hp, np; 00827 node_t *n; 00828 edge_t *e; 00829 int i, sgn, point_pair; 00830 double hy, ty, stepx, dx, dy, width, height; 00831 pointf points[1000]; 00832 int pointn; 00833 00834 e = edges[ind]; 00835 n = agtail(e); 00836 00837 stepx = (sizex / 2.) / cnt; 00838 stepx = MAX(stepx,2.); 00839 pointn = 0; 00840 np = ND_coord(n); 00841 tp = ED_tail_port(e).p; 00842 tp.x += np.x; 00843 tp.y += np.y; 00844 hp = ED_head_port(e).p; 00845 hp.x += np.x; 00846 hp.y += np.y; 00847 if (tp.x >= hp.x) sgn = 1; 00848 else sgn = -1; 00849 dy = ND_ht(n)/2., dx = 0.; 00850 // certain adjustments are required for some point_pairs in order to improve the 00851 // display of the edge path between them 00852 point_pair = convert_sides_to_points(ED_tail_port(e).side,ED_head_port(e).side); 00853 switch(point_pair){ 00854 case 67: sgn = -sgn; 00855 break; 00856 default: 00857 break; 00858 } 00859 ty = MIN(dy, 3*(tp.y + dy - np.y)); 00860 hy = MIN(dy, 3*(hp.y + dy - np.y)); 00861 for (i = 0; i < cnt; i++) { 00862 e = edges[ind++]; 00863 dy += stepy, ty += stepy, hy += stepy, dx += sgn*stepx; 00864 pointn = 0; 00865 points[pointn++] = tp; 00866 points[pointn++] = pointfof(tp.x + dx, tp.y - ty / 3); 00867 points[pointn++] = pointfof(tp.x + dx, np.y - dy); 00868 points[pointn++] = pointfof((tp.x+hp.x)/2, np.y - dy); 00869 points[pointn++] = pointfof(hp.x - dx, np.y - dy); 00870 points[pointn++] = pointfof(hp.x - dx, hp.y - hy / 3); 00871 points[pointn++] = hp; 00872 if (ED_label(e)) { 00873 if (GD_flip(agraphof(agtail(e)))) { 00874 width = ED_label(e)->dimen.y; 00875 height = ED_label(e)->dimen.x; 00876 } else { 00877 width = ED_label(e)->dimen.x; 00878 height = ED_label(e)->dimen.y; 00879 } 00880 ED_label(e)->pos.y = ND_coord(n).y - dy - height / 2.0; 00881 ED_label(e)->pos.x = ND_coord(n).x; 00882 ED_label(e)->set = TRUE; 00883 if (height > stepy) 00884 dy += height - stepy; 00885 } 00886 clip_and_install(e, aghead(e), points, pointn, sinfo); 00887 #ifdef DEBUG 00888 if (debugleveln(e,1)) 00889 showPoints (points, pointn); 00890 #endif 00891 } 00892 } 00893 00894 00895 static void 00896 selfTop (edge_t* edges[], int ind, int cnt, double sizex, double stepy, 00897 splineInfo* sinfo) 00898 { 00899 int i, sgn, point_pair; 00900 double hy, ty, stepx, dx, dy, width, height; 00901 pointf tp, hp, np; 00902 node_t *n; 00903 edge_t *e; 00904 pointf points[1000]; 00905 int pointn; 00906 00907 e = edges[ind]; 00908 n = agtail(e); 00909 00910 stepx = (sizex / 2.) / cnt; 00911 stepx = MAX(stepx, 2.); 00912 pointn = 0; 00913 np = ND_coord(n); 00914 tp = ED_tail_port(e).p; 00915 tp.x += np.x; 00916 tp.y += np.y; 00917 hp = ED_head_port(e).p; 00918 hp.x += np.x; 00919 hp.y += np.y; 00920 if (tp.x >= hp.x) sgn = 1; 00921 else sgn = -1; 00922 dy = ND_ht(n)/2., dx = 0.; 00923 // certain adjustments are required for some point_pairs in order to improve the 00924 // display of the edge path between them 00925 point_pair = convert_sides_to_points(ED_tail_port(e).side,ED_head_port(e).side); 00926 switch(point_pair){ 00927 case 15: 00928 dx = sgn*(ND_rw(n) - (hp.x-np.x) + stepx); 00929 break; 00930 00931 case 38: 00932 dx = sgn*(ND_lw(n)-(np.x-hp.x) + stepx); 00933 break; 00934 case 41: 00935 dx = sgn*(ND_rw(n)-(tp.x-np.x) + stepx); 00936 break; 00937 case 48: 00938 dx = sgn*(ND_rw(n)-(tp.x-np.x) + stepx); 00939 break; 00940 00941 case 14: 00942 case 37: 00943 case 47: 00944 case 51: 00945 case 57: 00946 case 58: 00947 dx = sgn*((((ND_lw(n)-(np.x-tp.x)) + (ND_rw(n)-(hp.x-np.x)))/3.)); 00948 break; 00949 case 73: 00950 dx = sgn*(ND_lw(n)-(np.x-tp.x) + stepx); 00951 break; 00952 case 83: 00953 dx = sgn*(ND_lw(n)-(np.x-tp.x)); 00954 break; 00955 case 84: 00956 dx = sgn*((((ND_lw(n)-(np.x-tp.x)) + (ND_rw(n)-(hp.x-np.x)))/2.) + stepx); 00957 break; 00958 case 74: 00959 case 75: 00960 case 85: 00961 dx = sgn*((((ND_lw(n)-(np.x-tp.x)) + (ND_rw(n)-(hp.x-np.x)))/2.) + 2*stepx); 00962 break; 00963 default: 00964 break; 00965 } 00966 ty = MIN(dy, 3*(np.y + dy - tp.y)); 00967 hy = MIN(dy, 3*(np.y + dy - hp.y)); 00968 for (i = 0; i < cnt; i++) { 00969 e = edges[ind++]; 00970 dy += stepy, ty += stepy, hy += stepy, dx += sgn*stepx; 00971 pointn = 0; 00972 points[pointn++] = tp; 00973 points[pointn++] = pointfof(tp.x + dx, tp.y + ty / 3); 00974 points[pointn++] = pointfof(tp.x + dx, np.y + dy); 00975 points[pointn++] = pointfof((tp.x+hp.x)/2, np.y + dy); 00976 points[pointn++] = pointfof(hp.x - dx, np.y + dy); 00977 points[pointn++] = pointfof(hp.x - dx, hp.y + hy / 3); 00978 points[pointn++] = hp; 00979 if (ED_label(e)) { 00980 if (GD_flip(agraphof(agtail(e)))) { 00981 width = ED_label(e)->dimen.y; 00982 height = ED_label(e)->dimen.x; 00983 } else { 00984 width = ED_label(e)->dimen.x; 00985 height = ED_label(e)->dimen.y; 00986 } 00987 ED_label(e)->pos.y = ND_coord(n).y + dy + height / 2.0; 00988 ED_label(e)->pos.x = ND_coord(n).x; 00989 ED_label(e)->set = TRUE; 00990 if (height > stepy) 00991 dy += height - stepy; 00992 } 00993 clip_and_install(e, aghead(e), points, pointn, sinfo); 00994 #ifdef DEBUG 00995 if (debugleveln(e,1)) 00996 showPoints (points, pointn); 00997 #endif 00998 } 00999 return; 01000 } 01001 01002 static void 01003 selfRight (edge_t* edges[], int ind, int cnt, double stepx, double sizey, 01004 splineInfo* sinfo) 01005 { 01006 int i, sgn, point_pair; 01007 double hx, tx, stepy, dx, dy, width, height; 01008 pointf tp, hp, np; 01009 node_t *n; 01010 edge_t *e; 01011 pointf points[1000]; 01012 int pointn; 01013 01014 e = edges[ind]; 01015 n = agtail(e); 01016 01017 stepy = (sizey / 2.) / cnt; 01018 stepy = MAX(stepy, 2.); 01019 pointn = 0; 01020 np = ND_coord(n); 01021 tp = ED_tail_port(e).p; 01022 tp.x += np.x; 01023 tp.y += np.y; 01024 hp = ED_head_port(e).p; 01025 hp.x += np.x; 01026 hp.y += np.y; 01027 if (tp.y >= hp.y) sgn = 1; 01028 else sgn = -1; 01029 dx = ND_rw(n), dy = 0; 01030 // certain adjustments are required for some point_pairs in order to improve the 01031 // display of the edge path between them 01032 point_pair = convert_sides_to_points(ED_tail_port(e).side,ED_head_port(e).side); 01033 switch(point_pair){ 01034 case 32: 01035 case 65: if(tp.y == hp.y) 01036 sgn = -sgn; 01037 break; 01038 default: 01039 break; 01040 } 01041 tx = MIN(dx, 3*(np.x + dx - tp.x)); 01042 hx = MIN(dx, 3*(np.x + dx - hp.x)); 01043 for (i = 0; i < cnt; i++) { 01044 e = edges[ind++]; 01045 dx += stepx, tx += stepx, hx += stepx, dy += sgn*stepy; 01046 pointn = 0; 01047 points[pointn++] = tp; 01048 points[pointn++] = pointfof(tp.x + tx / 3, tp.y + dy); 01049 points[pointn++] = pointfof(np.x + dx, tp.y + dy); 01050 points[pointn++] = pointfof(np.x + dx, (tp.y+hp.y)/2); 01051 points[pointn++] = pointfof(np.x + dx, hp.y - dy); 01052 points[pointn++] = pointfof(hp.x + hx / 3, hp.y - dy); 01053 points[pointn++] = hp; 01054 if (ED_label(e)) { 01055 if (GD_flip(agraphof(agtail(e)))) { 01056 width = ED_label(e)->dimen.y; 01057 height = ED_label(e)->dimen.x; 01058 } else { 01059 width = ED_label(e)->dimen.x; 01060 height = ED_label(e)->dimen.y; 01061 } 01062 ED_label(e)->pos.x = ND_coord(n).x + dx + width / 2.0; 01063 ED_label(e)->pos.y = ND_coord(n).y; 01064 ED_label(e)->set = TRUE; 01065 if (width > stepx) 01066 dx += width - stepx; 01067 } 01068 clip_and_install(e, aghead(e), points, pointn, sinfo); 01069 #ifdef DEBUG 01070 if (debugleveln(e,1)) 01071 showPoints (points, pointn); 01072 #endif 01073 } 01074 return; 01075 } 01076 01077 static void 01078 selfLeft (edge_t* edges[], int ind, int cnt, double stepx, double sizey, 01079 splineInfo* sinfo) 01080 { 01081 int i, sgn,point_pair; 01082 double hx, tx, stepy, dx, dy, width, height; 01083 pointf tp, hp, np; 01084 node_t *n; 01085 edge_t *e; 01086 pointf points[1000]; 01087 int pointn; 01088 01089 e = edges[ind]; 01090 n = agtail(e); 01091 01092 stepy = (sizey / 2.) / cnt; 01093 stepy = MAX(stepy,2.); 01094 pointn = 0; 01095 np = ND_coord(n); 01096 tp = ED_tail_port(e).p; 01097 tp.x += np.x; 01098 tp.y += np.y; 01099 hp = ED_head_port(e).p; 01100 hp.x += np.x; 01101 hp.y += np.y; 01102 01103 01104 if (tp.y >= hp.y) sgn = 1; 01105 else sgn = -1; 01106 dx = ND_lw(n), dy = 0.; 01107 // certain adjustments are required for some point_pairs in order to improve the 01108 // display of the edge path between them 01109 point_pair = convert_sides_to_points(ED_tail_port(e).side,ED_head_port(e).side); 01110 switch(point_pair){ 01111 case 12: 01112 case 67: 01113 if(tp.y == hp.y) 01114 sgn = -sgn; 01115 break; 01116 default: 01117 break; 01118 } 01119 tx = MIN(dx, 3*(tp.x + dx - np.x)); 01120 hx = MIN(dx, 3*(hp.x + dx - np.x)); 01121 for (i = 0; i < cnt; i++) { 01122 e = edges[ind++]; 01123 dx += stepx, tx += stepx, hx += stepx, dy += sgn*stepy; 01124 pointn = 0; 01125 points[pointn++] = tp; 01126 points[pointn++] = pointfof(tp.x - tx / 3, tp.y + dy); 01127 points[pointn++] = pointfof(np.x - dx, tp.y + dy); 01128 points[pointn++] = pointfof(np.x - dx, (tp.y+hp.y)/2); 01129 points[pointn++] = pointfof(np.x - dx, hp.y - dy); 01130 points[pointn++] = pointfof(hp.x - hx / 3, hp.y - dy); 01131 01132 points[pointn++] = hp; 01133 if (ED_label(e)) { 01134 if (GD_flip(agraphof(agtail(e)))) { 01135 width = ED_label(e)->dimen.y; 01136 height = ED_label(e)->dimen.x; 01137 } else { 01138 width = ED_label(e)->dimen.x; 01139 height = ED_label(e)->dimen.y; 01140 } 01141 ED_label(e)->pos.x = ND_coord(n).x - dx - width / 2.0; 01142 ED_label(e)->pos.y = ND_coord(n).y; 01143 ED_label(e)->set = TRUE; 01144 if (width > stepx) 01145 dx += width - stepx; 01146 } 01147 01148 clip_and_install(e, aghead(e), points, pointn, sinfo); 01149 #ifdef DEBUG 01150 if (debugleveln(e,1)) 01151 showPoints (points, pointn); 01152 #endif 01153 } 01154 } 01155 01156 /* selfRightSpace: 01157 * Assume e is self-edge. 01158 * Return extra space necessary on the right for this edge. 01159 * If the edge does not go on the right, return 0. 01160 * NOTE: the actual space is determined dynamically by GD_nodesep, 01161 * so using the constant SELF_EDGE_SIZE is going to be wrong. 01162 * Fortunately, the default nodesep is the same as SELF_EDGE_SIZE. 01163 */ 01164 int 01165 selfRightSpace (edge_t* e) 01166 { 01167 int sw; 01168 double label_width; 01169 textlabel_t* l = ED_label(e); 01170 01171 if (((!ED_tail_port(e).defined) && (!ED_head_port(e).defined)) || 01172 (!(ED_tail_port(e).side & LEFT) && 01173 !(ED_head_port(e).side & LEFT) && 01174 ((ED_tail_port(e).side != ED_head_port(e).side) || 01175 (!(ED_tail_port(e).side & (TOP|BOTTOM)))))) { 01176 sw = SELF_EDGE_SIZE; 01177 if (l) { 01178 label_width = GD_flip(agraphof(aghead(e))) ? l->dimen.y : l->dimen.x; 01179 sw += label_width; 01180 } 01181 } 01182 else sw = 0; 01183 return sw; 01184 } 01185 01186 /* makeSelfEdge: 01187 * The routing is biased towards the right side because this is what 01188 * dot supports, and leaves room for. 01189 * FIX: With this bias, labels tend to be placed on top of each other. 01190 * Perhaps for self-edges, the label should be centered. 01191 */ 01192 void 01193 makeSelfEdge(path * P, edge_t * edges[], int ind, int cnt, double sizex, 01194 double sizey, splineInfo * sinfo) 01195 { 01196 edge_t *e; 01197 01198 e = edges[ind]; 01199 01200 /* self edge without ports or 01201 * self edge with all ports inside, on the right, or at most 1 on top 01202 * and at most 1 on bottom 01203 */ 01204 01205 if (((!ED_tail_port(e).defined) && (!ED_head_port(e).defined)) || 01206 (!(ED_tail_port(e).side & LEFT) && 01207 !(ED_head_port(e).side & LEFT) && 01208 ((ED_tail_port(e).side != ED_head_port(e).side) || 01209 (!(ED_tail_port(e).side & (TOP|BOTTOM)))))) { 01210 selfRight(edges, ind, cnt, sizex, sizey, sinfo); 01211 } 01212 01213 /* self edge with port on left side */ 01214 else if ((ED_tail_port(e).side & LEFT) || (ED_head_port(e).side & LEFT)) { 01215 01216 /* handle L-R specially */ 01217 if ((ED_tail_port(e).side & RIGHT) || (ED_head_port(e).side & RIGHT)) { 01218 selfTop(edges, ind, cnt, sizex, sizey, sinfo); 01219 } 01220 else { 01221 selfLeft(edges, ind, cnt, sizex, sizey, sinfo); 01222 } 01223 } 01224 01225 /* self edge with both ports on top side */ 01226 else if (ED_tail_port(e).side & TOP) { 01227 selfTop(edges, ind, cnt, sizex, sizey, sinfo); 01228 } 01229 else if (ED_tail_port(e).side & BOTTOM) { 01230 selfBottom(edges, ind, cnt, sizex, sizey, sinfo); 01231 } 01232 01233 else assert(0); 01234 } 01235 01236 /* makePortLabels: 01237 * Add head and tail labels if necessary and update bounding box. 01238 */ 01239 void makePortLabels(edge_t * e) 01240 { 01241 /* Only use this if labelangle or labeldistance is set for the edge; 01242 * otherwise, handle with external labels. 01243 */ 01244 if (!E_labelangle && !E_labeldistance) return; 01245 01246 if (ED_head_label(e) && !ED_head_label(e)->set) { 01247 if (place_portlabel(e, TRUE)) 01248 updateBB(agraphof(agtail(e)), ED_head_label(e)); 01249 } 01250 if (ED_tail_label(e) && !ED_tail_label(e)->set) { 01251 if (place_portlabel(e, FALSE)) 01252 updateBB(agraphof(agtail(e)), ED_tail_label(e)); 01253 } 01254 } 01255 01256 /* endPoints: 01257 * Extract the actual end points of the spline, where 01258 * they touch the node. 01259 */ 01260 static void endPoints(splines * spl, pointf * p, pointf * q) 01261 { 01262 bezier bz; 01263 01264 bz = spl->list[0]; 01265 if (bz.sflag) { 01266 *p = bz.sp; 01267 } 01268 else { 01269 *p = bz.list[0]; 01270 } 01271 bz = spl->list[spl->size - 1]; 01272 if (bz.eflag) { 01273 *q = bz.ep; 01274 } 01275 else { 01276 *q = bz.list[bz.size - 1]; 01277 } 01278 } 01279 01280 /* polylineMidpoint; 01281 * Find midpoint of polyline. 01282 * pp and pq are set to the endpoints of the line segment containing it. 01283 */ 01284 static pointf 01285 polylineMidpoint (splines* spl, pointf* pp, pointf* pq) 01286 { 01287 bezier bz; 01288 int i, j, k; 01289 double d, dist = 0; 01290 pointf pf, qf, mf; 01291 01292 for (i = 0; i < spl->size; i++) { 01293 bz = spl->list[i]; 01294 for (j = 0, k=3; k < bz.size; j+=3,k+=3) { 01295 pf = bz.list[j]; 01296 qf = bz.list[k]; 01297 dist += DIST(pf, qf); 01298 } 01299 } 01300 dist /= 2; 01301 for (i = 0; i < spl->size; i++) { 01302 bz = spl->list[i]; 01303 for (j = 0, k=3; k < bz.size; j+=3,k+=3) { 01304 pf = bz.list[j]; 01305 qf = bz.list[k]; 01306 d = DIST(pf,qf); 01307 if (d >= dist) { 01308 *pp = pf; 01309 *pq = qf; 01310 mf.x = ((qf.x*dist) + (pf.x*(d-dist)))/d; 01311 mf.y = ((qf.y*dist) + (pf.y*(d-dist)))/d; 01312 return mf; 01313 } 01314 else 01315 dist -= d; 01316 } 01317 } 01318 assert (FALSE); /* should never get here */ 01319 return mf; 01320 } 01321 01322 pointf 01323 edgeMidpoint (graph_t* g, edge_t * e) 01324 { 01325 int et = EDGE_TYPE (g); 01326 pointf d, spf, p, q; 01327 01328 endPoints(ED_spl(e), &p, &q); 01329 if (APPROXEQPT(p, q, MILLIPOINT)) { /* degenerate spline */ 01330 spf = p; 01331 } 01332 else if ((et == ET_SPLINE) || (et == ET_CURVED)) { 01333 d.x = (q.x + p.x) / 2.; 01334 d.y = (p.y + q.y) / 2.; 01335 spf = dotneato_closest(ED_spl(e), d); 01336 } 01337 else { /* ET_PLINE, ET_ORTHO or ET_LINE */ 01338 spf = polylineMidpoint (ED_spl(e), &p, &q); 01339 } 01340 01341 return spf; 01342 } 01343 01344 #define LEFTOF(a,b,c) (((a.y - b.y)*(c.x - b.x) - (c.y - b.y)*(a.x - b.x)) > 0) 01345 #define MAXLABELWD (POINTS_PER_INCH/2.0) 01346 01347 /* addEdgeLabels: 01348 * rp and rq are the port points of the tail and head node. 01349 * Adds label, headlabel and taillabel. 01350 * The use of 2 and 4 in computing ld.x and ld.y are fudge factors, to 01351 * introduce a bit of spacing. 01352 * Updates bounding box. 01353 * We try to use the actual endpoints of the spline, as they may differ 01354 * significantly from rp and rq, but if the spline is degenerate (e.g., 01355 * the nodes overlap), we use rp and rq. 01356 */ 01357 void addEdgeLabels(graph_t* g, edge_t * e, pointf rp, pointf rq) 01358 { 01359 #if 0 01360 int et = EDGE_TYPE (g); 01361 pointf p, q; 01362 pointf d; /* midpoint of segment p-q */ 01363 point ld; 01364 point del; 01365 pointf spf; 01366 double f, ht, wd, dist2; 01367 int leftOf; 01368 01369 if (ED_label(e) && !ED_label(e)->set) { 01370 endPoints(ED_spl(e), &p, &q); 01371 if (APPROXEQPT(p, q, MILLIPOINT)) { /* degenerate spline */ 01372 p = rp; 01373 q = rq; 01374 spf = p; 01375 } 01376 else if (et == ET_SPLINE) { 01377 d.x = (q.x + p.x) / 2.; 01378 d.y = (p.y + q.y) / 2.; 01379 spf = dotneato_closest(ED_spl(e), d); 01380 } 01381 else { /* ET_PLINE, ET_ORTHO or ET_LINE */ 01382 spf = polylineMidpoint (ED_spl(e), &p, &q); 01383 } 01384 del.x = q.x - p.x; 01385 del.y = q.y - p.y; 01386 dist2 = del.x*del.x + del.y*del.y; 01387 ht = (ED_label(e)->dimen.y + 2)/2.0; 01388 if (dist2) { 01389 wd = (MIN(ED_label(e)->dimen.x + 2, MAXLABELWD))/2.0; 01390 leftOf = LEFTOF(p, q, spf); 01391 if ((leftOf && (del.y >= 0)) || (!leftOf && (del.y < 0))) { 01392 if (del.x*del.y >= 0) 01393 ht *= -1; 01394 } 01395 else { 01396 wd *= -1; 01397 if (del.x*del.y < 0) 01398 ht *= -1; 01399 } 01400 f = (del.y*wd - del.x*ht)/dist2; 01401 ld.x = -f*del.y; 01402 ld.y = f*del.x; 01403 } 01404 else { /* end points the same */ 01405 ld.x = 0; 01406 ld.y = -ht; 01407 } 01408 01409 ED_label(e)->pos.x = spf.x + ld.x; 01410 ED_label(e)->pos.y = spf.y + ld.y; 01411 ED_label(e)->set = TRUE; 01412 updateBB(agraphof(agtail(e)), ED_label(e)); 01413 } 01414 #endif 01415 makePortLabels(e); 01416 } 01417 01418 #ifndef WITH_CGRAPH 01419 #define AGXGET(o,a) agxget(o,a->index) 01420 #else /* WITH_CGRAPH */ 01421 #define AGXGET(o,a) agxget(o,a) 01422 #endif /* WITH_CGRAPH */ 01423 01424 /* vladimir */ 01425 /* place_portlabel: 01426 * place the {head,tail}label (depending on HEAD_P) of edge E 01427 * N.B. Assume edges are normalized, so tail is at spl->list[0].list[0] 01428 * and head is at spl->list[spl->size-l].list[bez->size-1] 01429 * Return 1 on success 01430 */ 01431 int place_portlabel(edge_t * e, boolean head_p) 01432 { 01433 textlabel_t *l; 01434 splines *spl; 01435 bezier *bez; 01436 double dist, angle; 01437 pointf c[4], pe, pf; 01438 int i; 01439 char* la; 01440 char* ld; 01441 01442 if (ED_edge_type(e) == IGNORED) 01443 return 0; 01444 /* add label here only if labelangle or labeldistance is defined; else, use external label */ 01445 if ((!E_labelangle || (*(la = AGXGET(e,E_labelangle)) == '\0')) && 01446 (!E_labeldistance || (*(ld = AGXGET(e,E_labeldistance)) == '\0'))) { 01447 return 0; 01448 } 01449 01450 l = head_p ? ED_head_label(e) : ED_tail_label(e); 01451 if ((spl = getsplinepoints(e)) == NULL) return 0; 01452 if (!head_p) { 01453 bez = &spl->list[0]; 01454 if (bez->sflag) { 01455 pe = bez->sp; 01456 pf = bez->list[0]; 01457 } else { 01458 pe = bez->list[0]; 01459 for (i = 0; i < 4; i++) 01460 c[i] = bez->list[i]; 01461 pf = Bezier(c, 3, 0.1, NULL, NULL); 01462 } 01463 } else { 01464 bez = &spl->list[spl->size - 1]; 01465 if (bez->eflag) { 01466 pe = bez->ep; 01467 pf = bez->list[bez->size - 1]; 01468 } else { 01469 pe = bez->list[bez->size - 1]; 01470 for (i = 0; i < 4; i++) 01471 c[i] = bez->list[bez->size - 4 + i]; 01472 pf = Bezier(c, 3, 0.9, NULL, NULL); 01473 } 01474 } 01475 angle = atan2(pf.y - pe.y, pf.x - pe.x) + 01476 RADIANS(late_double(e, E_labelangle, PORT_LABEL_ANGLE, -180.0)); 01477 dist = PORT_LABEL_DISTANCE * late_double(e, E_labeldistance, 1.0, 0.0); 01478 l->pos.x = pe.x + dist * cos(angle); 01479 l->pos.y = pe.y + dist * sin(angle); 01480 l->set = TRUE; 01481 return 1; 01482 } 01483 01484 splines *getsplinepoints(edge_t * e) 01485 { 01486 edge_t *le; 01487 splines *sp; 01488 01489 for (le = e; !(sp = ED_spl(le)) && ED_edge_type(le) != NORMAL; 01490 le = ED_to_orig(le)); 01491 if (sp == NULL) 01492 agerr (AGERR, "getsplinepoints: no spline points available for edge (%s,%s)\n", 01493 agnameof(agtail(e)), agnameof(aghead(e))); 01494 return sp; 01495 } 01496
1.7.5