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