00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "dot.h"
00022
00023
00024
00025
00026
00027 static point midPt(point p, point q)
00028 {
00029 point v;
00030
00031 v.x = (p.x + q.x) / 2;
00032 v.y = (p.y + q.y) / 2;
00033 return v;
00034 }
00035
00036
00037
00038
00039 static char *p2s(point p, char *buf)
00040 {
00041 sprintf(buf, "(%d,%d)", p.x, p.y);
00042 return buf;
00043 }
00044
00045
00046
00047
00048
00049 static point boxIntersect(point pp, point cp, box * bp)
00050 {
00051 point ipp;
00052 double ppx = pp.x;
00053 double ppy = pp.y;
00054 double cpx = cp.x;
00055 double cpy = cp.y;
00056 point ll = bp->LL;
00057 point ur = bp->UR;
00058
00059 if (cp.x < ll.x) {
00060 ipp.x = ll.x;
00061 ipp.y = pp.y + (int) ((ipp.x - ppx) * (ppy - cpy) / (ppx - cpx));
00062 if (ipp.y >= ll.y && ipp.y <= ur.y)
00063 return ipp;
00064 }
00065 if (cp.x > ur.x) {
00066 ipp.x = ur.x;
00067 ipp.y = pp.y + (int) ((ipp.x - ppx) * (ppy - cpy) / (ppx - cpx));
00068 if (ipp.y >= ll.y && ipp.y <= ur.y)
00069 return ipp;
00070 }
00071 if (cp.y < ll.y) {
00072 ipp.y = ll.y;
00073 ipp.x = pp.x + (int) ((ipp.y - ppy) * (ppx - cpx) / (ppy - cpy));
00074 if (ipp.x >= ll.x && ipp.x <= ur.x)
00075 return ipp;
00076 }
00077 if (cp.y > ur.y) {
00078 ipp.y = ur.y;
00079 ipp.x = pp.x + (int) ((ipp.y - ppy) * (ppx - cpx) / (ppy - cpy));
00080 if (ipp.x >= ll.x && ipp.x <= ur.x)
00081 return ipp;
00082 }
00083
00084
00085 {
00086 char ppbuf[100], cpbuf[100], llbuf[100], urbuf[100];
00087
00088 agerr(AGERR,
00089 "segment [%s,%s] does not intersect box ll=%s,ur=%s\n",
00090 p2s(pp, ppbuf), p2s(cp, cpbuf), p2s(ll, llbuf), p2s(ur,
00091 urbuf));
00092 assert(0);
00093 }
00094 return ipp;
00095 }
00096
00097
00098
00099
00100 static int inBox(point p, box * bb)
00101 {
00102 return ((p.x >= bb->LL.x) && (p.x <= bb->UR.x) &&
00103 (p.y >= bb->LL.y) && (p.y <= bb->UR.y));
00104 }
00105
00106
00107
00108
00109
00110
00111 static graph_t *getCluster(graph_t * g, char *cluster_name)
00112 {
00113 graph_t *sg;
00114
00115 if (!cluster_name || (*cluster_name == '\0'))
00116 return NULL;
00117 sg = agfindsubg(g, cluster_name);
00118 if (sg == NULL)
00119 agerr(AGWARN, "cluster named %s not found\n", cluster_name);
00120 return sg;
00121 }
00122
00123
00124
00125
00126
00127
00128
00129 #define SGN(a,b) (((a)<(b)) ? -1 : 1)
00130 #define ZSGN(a,b) (((a)<(b)) ? -1 : (a)>(b) ? 1 : 0)
00131
00132
00133
00134
00135
00136 static int countVertCross(pointf * pts, int xcoord)
00137 {
00138 int i;
00139 int sign, old_sign;
00140 int num_crossings = 0;
00141
00142 sign = old_sign = ZSGN(pts[0].x, xcoord);
00143 if (sign == 0)
00144 num_crossings++;
00145 for (i = 1; i <= 3; i++) {
00146 sign = ZSGN(pts[i].x, xcoord);
00147 if ((sign != old_sign) && (old_sign != 0))
00148 num_crossings++;
00149 old_sign = sign;
00150 }
00151
00152 return num_crossings;
00153
00154 }
00155
00156
00157
00158
00159
00160 static int countHorzCross(pointf * pts, int ycoord)
00161 {
00162 int i;
00163 int sign, old_sign;
00164 int num_crossings = 0;
00165
00166 sign = old_sign = ZSGN(pts[0].y, ycoord);
00167 if (sign == 0)
00168 num_crossings++;
00169 for (i = 1; i <= 3; i++) {
00170 sign = ZSGN(pts[i].y, ycoord);
00171 if ((sign != old_sign) && (old_sign != 0))
00172 num_crossings++;
00173 old_sign = sign;
00174 }
00175
00176 return num_crossings;
00177
00178 }
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188 static double
00189 findVertical(pointf * pts, double tmin, double tmax,
00190 int xcoord, int ymin, int ymax)
00191 {
00192 pointf Left[4];
00193 pointf Right[4];
00194 double t;
00195 int no_cross = countVertCross(pts, xcoord);
00196
00197 if (no_cross == 0)
00198 return -1.0;
00199
00200
00201 if ((no_cross == 1) && (ROUND(pts[3].x) == xcoord)) {
00202 if ((ymin <= pts[3].y) && (pts[3].y <= ymax)) {
00203 return tmax;
00204 } else
00205 return -1.0;
00206 }
00207
00208
00209 Bezier(pts, 3, 0.5, Left, Right);
00210 t = findVertical(Left, tmin, (tmin + tmax) / 2.0, xcoord, ymin, ymax);
00211 if (t >= 0.0)
00212 return t;
00213 return findVertical(Right, (tmin + tmax) / 2.0, tmax, xcoord, ymin,
00214 ymax);
00215
00216 }
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226 static double
00227 findHorizontal(pointf * pts, double tmin, double tmax,
00228 int ycoord, int xmin, int xmax)
00229 {
00230 pointf Left[4];
00231 pointf Right[4];
00232 double t;
00233 int no_cross = countHorzCross(pts, ycoord);
00234
00235 if (no_cross == 0)
00236 return -1.0;
00237
00238
00239 if ((no_cross == 1) && (ROUND(pts[3].y) == ycoord)) {
00240 if ((xmin <= pts[3].x) && (pts[3].x <= xmax)) {
00241 return tmax;
00242 } else
00243 return -1.0;
00244 }
00245
00246
00247 Bezier(pts, 3, 0.5, Left, Right);
00248 t = findHorizontal(Left, tmin, (tmin + tmax) / 2.0, ycoord, xmin,
00249 xmax);
00250 if (t >= 0.0)
00251 return t;
00252 return findHorizontal(Right, (tmin + tmax) / 2.0, tmax, ycoord, xmin,
00253 xmax);
00254
00255 }
00256
00257 #define P2PF(p, pf) (pf.x = p.x, pf.y = p.y)
00258 #define PF2P(pf, p) (p.x = ROUND (pf.x), p.y = ROUND (pf.y))
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268 static int splineIntersect(point * ipts, box * bb)
00269 {
00270 double tmin = 2.0;
00271 double t;
00272 pointf pts[4];
00273 pointf origpts[4];
00274 int i;
00275
00276 for (i = 0; i < 4; i++) {
00277 P2PF(ipts[i], origpts[i]);
00278 pts[i] = origpts[i];
00279 }
00280
00281 t = findVertical(pts, 0.0, 1.0, bb->LL.x, bb->LL.y, bb->UR.y);
00282 if ((t >= 0) && (t < tmin)) {
00283 Bezier(origpts, 3, t, pts, NULL);
00284 tmin = t;
00285 }
00286 t = findVertical(pts, 0.0, MIN(1.0, tmin), bb->UR.x, bb->LL.y,
00287 bb->UR.y);
00288 if ((t >= 0) && (t < tmin)) {
00289 Bezier(origpts, 3, t, pts, NULL);
00290 tmin = t;
00291 }
00292 t = findHorizontal(pts, 0.0, MIN(1.0, tmin), bb->LL.y, bb->LL.x,
00293 bb->UR.x);
00294 if ((t >= 0) && (t < tmin)) {
00295 Bezier(origpts, 3, t, pts, NULL);
00296 tmin = t;
00297 }
00298 t = findHorizontal(pts, 0.0, MIN(1.0, tmin), bb->UR.y, bb->LL.x,
00299 bb->UR.x);
00300 if ((t >= 0) && (t < tmin)) {
00301 Bezier(origpts, 3, t, pts, NULL);
00302 tmin = t;
00303 }
00304
00305 if (tmin < 2.0) {
00306 for (i = 0; i < 4; i++) {
00307 PF2P(pts[i], ipts[i]);
00308 }
00309 return 1;
00310 } else
00311 return 0;
00312
00313 }
00314
00315
00316
00317
00318
00319
00320
00321
00322 static void makeCompoundEdge(graph_t * g, edge_t * e)
00323 {
00324 graph_t *lh;
00325 graph_t *lt;
00326 bezier *bez;
00327 bezier *nbez;
00328 int starti = 0, endi = 0;
00329 node_t *head;
00330 node_t *tail;
00331 box *bb;
00332 int i, j;
00333 int size;
00334 point pts[4];
00335 point p;
00336 int fixed;
00337
00338
00339 lh = getCluster(g, agget(e, "lhead"));
00340 lt = getCluster(g, agget(e, "ltail"));
00341 if (!lt && !lh)
00342 return;
00343 if (!ED_spl(e)) return;
00344
00345
00346 if (ED_spl(e)->size > 1) {
00347 agerr(AGWARN, "%s -> %s: spline size > 1 not supported\n",
00348 e->tail->name, e->head->name);
00349 return;
00350 }
00351 bez = ED_spl(e)->list;
00352 size = bez->size;
00353
00354 head = e->head;
00355 tail = e->tail;
00356
00357
00358 nbez = GNEW(bezier);
00359 nbez->eflag = bez->eflag;
00360 nbez->sflag = bez->sflag;
00361
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371 fixed = 0;
00372 if (lh) {
00373 bb = &(GD_bb(lh));
00374 if (!inBox(ND_coord_i(head), bb)) {
00375 agerr(AGWARN, "%s -> %s: head not inside head cluster %s\n",
00376 e->tail->name, e->head->name, agget(e, "lhead"));
00377 } else {
00378
00379
00380
00381
00382
00383 if (inBox(bez->list[0], bb)) {
00384 if (inBox(ND_coord_i(tail), bb)) {
00385 agerr(AGWARN,
00386 "%s -> %s: tail is inside head cluster %s\n",
00387 e->tail->name, e->head->name, agget(e, "lhead"));
00388 } else {
00389 assert(bez->sflag);
00390 p = boxIntersect(bez->list[0], bez->sp, bb);
00391 bez->list[3] = p;
00392 bez->list[1] = midPt(p, bez->sp);
00393 bez->list[0] = midPt(bez->list[1], bez->sp);
00394 bez->list[2] = midPt(bez->list[1], p);
00395 if (bez->eflag)
00396 endi =
00397 arrowEndClip(e, bez->list,
00398 starti, 0, nbez, bez->eflag);
00399 endi += 3;
00400 fixed = 1;
00401 }
00402 } else {
00403 for (endi = 0; endi < size - 1; endi += 3) {
00404 if (splineIntersect(&(bez->list[endi]), bb))
00405 break;
00406 }
00407 if (endi == size - 1) {
00408 assert(bez->eflag);
00409 nbez->ep = boxIntersect(bez->ep, bez->list[endi], bb);
00410 } else {
00411 if (bez->eflag)
00412 endi =
00413 arrowEndClip(e, bez->list,
00414 starti, endi, nbez, bez->eflag);
00415 endi += 3;
00416 }
00417 fixed = 1;
00418 }
00419 }
00420 }
00421 if (fixed == 0) {
00422 endi = size - 1;
00423 if (bez->eflag)
00424 nbez->ep = bez->ep;
00425 }
00426
00427
00428
00429
00430
00431
00432 fixed = 0;
00433 if (lt) {
00434 bb = &(GD_bb(lt));
00435 if (!inBox(ND_coord_i(tail), bb)) {
00436 agerr(AGWARN, "%s -> %s: tail not inside tail cluster %s\n",
00437 e->tail->name, head->name, agget(e, "ltail"));
00438 } else {
00439
00440
00441
00442
00443
00444 if (inBox(bez->list[endi], bb)) {
00445 if (inBox(ND_coord_i(head), bb)) {
00446 agerr(AGWARN,
00447 "%s -> %s: head is inside tail cluster %s\n",
00448 e->tail->name, e->head->name, agget(e, "ltail"));
00449 } else {
00450 assert(bez->eflag);
00451 p = boxIntersect(bez->list[endi], nbez->ep, bb);
00452 starti = endi - 3;
00453 bez->list[starti] = p;
00454 bez->list[starti + 2] = midPt(p, nbez->ep);
00455 bez->list[starti + 3] =
00456 midPt(bez->list[starti + 2], nbez->ep);
00457 bez->list[starti + 1] =
00458 midPt(bez->list[starti + 2], p);
00459 if (bez->sflag)
00460 starti =
00461 arrowStartClip(e, bez->list,
00462 starti, endi - 3, nbez,
00463 bez->sflag);
00464 fixed = 1;
00465 }
00466 } else {
00467 for (starti = endi; starti > 0; starti -= 3) {
00468 for (i = 0; i < 4; i++)
00469 pts[i] = bez->list[starti - i];
00470 if (splineIntersect(pts, bb)) {
00471 for (i = 0; i < 4; i++)
00472 bez->list[starti - i] = pts[i];
00473 break;
00474 }
00475 }
00476 if (starti == 0) {
00477 assert(bez->sflag);
00478 nbez->sp =
00479 boxIntersect(bez->sp, bez->list[starti], bb);
00480 } else {
00481 starti -= 3;
00482 if (bez->sflag)
00483 starti =
00484 arrowStartClip(e, bez->list,
00485 starti, endi - 3, nbez,
00486 bez->sflag);
00487 }
00488 fixed = 1;
00489 }
00490 }
00491 }
00492 if (fixed == 0) {
00493
00494 if (bez->sflag)
00495 nbez->sp = bez->sp;
00496 }
00497
00498
00499
00500 nbez->size = endi - starti + 1;
00501 nbez->list = N_GNEW(nbez->size, point);
00502 for (i = 0, j = starti; i < nbez->size; i++, j++)
00503 nbez->list[i] = bez->list[j];
00504 free(bez->list);
00505 free(bez);
00506 ED_spl(e)->list = nbez;
00507
00508 }
00509
00510
00511
00512 void dot_compoundEdges(graph_t * g)
00513 {
00514 edge_t *e;
00515 node_t *n;
00516
00517 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00518 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
00519 makeCompoundEdge(g, e);
00520 }
00521 }
00522 }