/* simpleSplineRoute: * Given a simple (ccw) polygon, route an edge from tp to hp. */ pointf* simpleSplineRoute (pointf tp, pointf hp, Ppoly_t poly, int* n_spl_pts, int polyline) { Ppolyline_t pl, spl; Ppoint_t eps[2]; Pvector_t evs[2]; int i; eps[0].x = tp.x; eps[0].y = tp.y; eps[1].x = hp.x; eps[1].y = hp.y; if (Pshortestpath(&poly, eps, &pl) < 0) return NULL; if (polyline) make_polyline (pl, &spl); else { if (poly.pn > edgen) { edges = ALLOC(poly.pn, edges, Pedge_t); edgen = poly.pn; } for (i = 0; i < poly.pn; i++) { edges[i].a = poly.ps[i]; edges[i].b = poly.ps[(i + 1) % poly.pn]; } #if 0 if (pp->start.constrained) { evs[0].x = cos(pp->start.theta); evs[0].y = sin(pp->start.theta); } else #endif evs[0].x = evs[0].y = 0; #if 0 if (pp->end.constrained) { evs[1].x = -cos(pp->end.theta); evs[1].y = -sin(pp->end.theta); } else #endif evs[1].x = evs[1].y = 0; if (Proutespline(edges, poly.pn, pl, evs, &spl) < 0) return NULL; } if (mkspacep(spl.pn)) return NULL; for (i = 0; i < spl.pn; i++) { ps[i] = spl.ps[i]; } *n_spl_pts = spl.pn; return ps; }
/* makePolyline: */ static void makePolyline(graph_t* g, edge_t * e) { Ppolyline_t spl, line = ED_path(e); Ppoint_t p0, q0; p0 = line.ps[0]; q0 = line.ps[line.pn - 1]; make_polyline (line, &spl); if (Verbose > 1) fprintf(stderr, "polyline %s %s\n", agnameof(agtail(e)), agnameof(aghead(e))); clip_and_install(e, aghead(e), spl.ps, spl.pn, &sinfo); addEdgeLabels(g, e, p0, q0); }
/* makeStraightEdge: * * FIX: handle ports on boundary? */ void makeStraightEdge(graph_t * g, edge_t * e, int et, splineInfo* sinfo) { pointf dumb[4]; node_t *n = agtail(e); node_t *head = aghead(e); int e_cnt = ED_count(e); int curved = (et == ET_CURVED); pointf perp; pointf del; edge_t *e0; int i, j, xstep, dx; double l_perp; pointf dumber[4]; pointf p, q; p = dumb[1] = dumb[0] = add_pointf(ND_coord(n), ED_tail_port(e).p); q = dumb[2] = dumb[3] = add_pointf(ND_coord(head), ED_head_port(e).p); if ((e_cnt == 1) || Concentrate) { if (curved) bend(dumb,get_centroid(g)); clip_and_install(e, aghead(e), dumb, 4, sinfo); addEdgeLabels(g, e, p, q); return; } e0 = e; if (APPROXEQPT(dumb[0], dumb[3], MILLIPOINT)) { /* degenerate case */ dumb[1] = dumb[0]; dumb[2] = dumb[3]; del.x = 0; del.y = 0; } else { perp.x = dumb[0].y - dumb[3].y; perp.y = dumb[3].x - dumb[0].x; l_perp = LEN(perp.x, perp.y); xstep = GD_nodesep(g->root); dx = xstep * (e_cnt - 1) / 2; dumb[1].x = dumb[0].x + (dx * perp.x) / l_perp; dumb[1].y = dumb[0].y + (dx * perp.y) / l_perp; dumb[2].x = dumb[3].x + (dx * perp.x) / l_perp; dumb[2].y = dumb[3].y + (dx * perp.y) / l_perp; del.x = -xstep * perp.x / l_perp; del.y = -xstep * perp.y / l_perp; } for (i = 0; i < e_cnt; i++) { if (aghead(e0) == head) { p = dumb[0]; q = dumb[3]; for (j = 0; j < 4; j++) { dumber[j] = dumb[j]; } } else { p = dumb[3]; q = dumb[0]; for (j = 0; j < 4; j++) { dumber[3 - j] = dumb[j]; } } if (et == ET_PLINE) { Ppoint_t pts[4]; Ppolyline_t spl, line; line.pn = 4; line.ps = pts; for (j=0; j < 4; j++) { pts[j] = dumber[j]; } make_polyline (line, &spl); clip_and_install(e0, aghead(e0), spl.ps, spl.pn, sinfo); } else clip_and_install(e0, aghead(e0), dumber, 4, sinfo); addEdgeLabels(g, e0, p, q); e0 = ED_to_virt(e0); dumb[1].x += del.x; dumb[1].y += del.y; dumb[2].x += del.x; dumb[2].y += del.y; } }
/* routesplines: * Route a path using the path info in pp. This includes start and end points * plus a collection of contiguous boxes contain the terminal points. The boxes * are converted into a containing polygon. A shortest path is constructed within * the polygon from between the terminal points. If polyline is true, this path * is converted to a spline representation. Otherwise, we call the path planner to * convert the polyline into a smooth spline staying within the polygon. In both * cases, the function returns an array of the computed control points. The number * of these points is given in npoints. * * Note that the returned points are stored in a single array, so the points must be * used before another call to this function. * * During cleanup, the function determines the x-extent of the spline in the box, so * the box can be shrunk to the minimum width. The extra space can then be used by other * edges. * * If a catastrophic error, return NULL. */ static pointf *_routesplines(path * pp, int *npoints, int polyline) { Ppoly_t poly; Ppolyline_t pl, spl; int splinepi; Ppoint_t eps[2]; Pvector_t evs[2]; int edgei, prev, next; int pi, bi; boxf *boxes; int boxn; edge_t* realedge; int flip; int loopcnt, delta = INIT_DELTA; boolean unbounded; nedges++; nboxes += pp->nbox; for (realedge = (edge_t *) pp->data; #ifdef NOTNOW origedge = realedge; #endif realedge && ED_edge_type(realedge) != NORMAL; realedge = ED_to_orig(realedge)); if (!realedge) { agerr(AGERR, "in routesplines, cannot find NORMAL edge\n"); return NULL; } boxes = pp->boxes; boxn = pp->nbox; if (checkpath(boxn, boxes, pp)) return NULL; #ifdef DEBUG if (debugleveln(realedge, 1)) printboxes(boxn, boxes); if (debugleveln(realedge, 3)) { psprintinit(1); psprintboxes(boxn, boxes); } #endif if (boxn * 8 > polypointn) { polypoints = ALLOC(boxn * 8, polypoints, Ppoint_t); polypointn = boxn * 8; } if ((boxn > 1) && (boxes[0].LL.y > boxes[1].LL.y)) { flip = 1; for (bi = 0; bi < boxn; bi++) { double v = boxes[bi].UR.y; boxes[bi].UR.y = -1*boxes[bi].LL.y; boxes[bi].LL.y = -v; } } else flip = 0; if (agtail(realedge) != aghead(realedge)) { /* I assume that the path goes either down only or up - right - down */ for (bi = 0, pi = 0; bi < boxn; bi++) { next = prev = 0; if (bi > 0) prev = (boxes[bi].LL.y > boxes[bi - 1].LL.y) ? -1 : 1; if (bi < boxn - 1) next = (boxes[bi + 1].LL.y > boxes[bi].LL.y) ? 1 : -1; if (prev != next) { if (next == -1 || prev == 1) { polypoints[pi].x = boxes[bi].LL.x; polypoints[pi++].y = boxes[bi].UR.y; polypoints[pi].x = boxes[bi].LL.x; polypoints[pi++].y = boxes[bi].LL.y; } else { polypoints[pi].x = boxes[bi].UR.x; polypoints[pi++].y = boxes[bi].LL.y; polypoints[pi].x = boxes[bi].UR.x; polypoints[pi++].y = boxes[bi].UR.y; } } else if (prev == 0) { /* single box */ polypoints[pi].x = boxes[bi].LL.x; polypoints[pi++].y = boxes[bi].UR.y; polypoints[pi].x = boxes[bi].LL.x; polypoints[pi++].y = boxes[bi].LL.y; } else { if (!(prev == -1 && next == -1)) { agerr(AGERR, "in routesplines, illegal values of prev %d and next %d, line %d\n", prev, next, __LINE__); return NULL; } } } for (bi = boxn - 1; bi >= 0; bi--) { next = prev = 0; if (bi < boxn - 1) prev = (boxes[bi].LL.y > boxes[bi + 1].LL.y) ? -1 : 1; if (bi > 0) next = (boxes[bi - 1].LL.y > boxes[bi].LL.y) ? 1 : -1; if (prev != next) { if (next == -1 || prev == 1 ) { polypoints[pi].x = boxes[bi].LL.x; polypoints[pi++].y = boxes[bi].UR.y; polypoints[pi].x = boxes[bi].LL.x; polypoints[pi++].y = boxes[bi].LL.y; } else { polypoints[pi].x = boxes[bi].UR.x; polypoints[pi++].y = boxes[bi].LL.y; polypoints[pi].x = boxes[bi].UR.x; polypoints[pi++].y = boxes[bi].UR.y; } } else if (prev == 0) { /* single box */ polypoints[pi].x = boxes[bi].UR.x; polypoints[pi++].y = boxes[bi].LL.y; polypoints[pi].x = boxes[bi].UR.x; polypoints[pi++].y = boxes[bi].UR.y; } else { if (!(prev == -1 && next == -1)) { /* it went badly, e.g. degenerate box in boxlist */ agerr(AGERR, "in routesplines, illegal values of prev %d and next %d, line %d\n", prev, next, __LINE__); return NULL; /* for correctness sake, it's best to just stop */ } polypoints[pi].x = boxes[bi].UR.x; polypoints[pi++].y = boxes[bi].LL.y; polypoints[pi].x = boxes[bi].UR.x; polypoints[pi++].y = boxes[bi].UR.y; polypoints[pi].x = boxes[bi].LL.x; polypoints[pi++].y = boxes[bi].UR.y; polypoints[pi].x = boxes[bi].LL.x; polypoints[pi++].y = boxes[bi].LL.y; } } } else { agerr(AGERR, "in routesplines, edge is a loop at %s\n", agnameof(aghead(realedge))); return NULL; } if (flip) { int i; for (bi = 0; bi < boxn; bi++) { int v = boxes[bi].UR.y; boxes[bi].UR.y = -1*boxes[bi].LL.y; boxes[bi].LL.y = -v; } for (i = 0; i < pi; i++) polypoints[i].y *= -1; } for (bi = 0; bi < boxn; bi++) boxes[bi].LL.x = INT_MAX, boxes[bi].UR.x = INT_MIN; poly.ps = polypoints, poly.pn = pi; eps[0].x = pp->start.p.x, eps[0].y = pp->start.p.y; eps[1].x = pp->end.p.x, eps[1].y = pp->end.p.y; if (Pshortestpath(&poly, eps, &pl) < 0) { agerr(AGERR, "in routesplines, Pshortestpath failed\n"); return NULL; } #ifdef DEBUG if (debugleveln(realedge, 3)) { psprintpoly(poly); psprintline(pl); } #endif if (polyline) { make_polyline (pl, &spl); } else { if (poly.pn > edgen) { edges = ALLOC(poly.pn, edges, Pedge_t); edgen = poly.pn; } for (edgei = 0; edgei < poly.pn; edgei++) { edges[edgei].a = polypoints[edgei]; edges[edgei].b = polypoints[(edgei + 1) % poly.pn]; } if (pp->start.constrained) { evs[0].x = cos(pp->start.theta); evs[0].y = sin(pp->start.theta); } else evs[0].x = evs[0].y = 0; if (pp->end.constrained) { evs[1].x = -cos(pp->end.theta); evs[1].y = -sin(pp->end.theta); } else evs[1].x = evs[1].y = 0; if (Proutespline(edges, poly.pn, pl, evs, &spl) < 0) { agerr(AGERR, "in routesplines, Proutespline failed\n"); return NULL; } #ifdef DEBUG if (debugleveln(realedge, 3)) { psprintspline(spl); psprintinit(0); } #endif } if (mkspacep(spl.pn)) return NULL; /* Bailout if no memory left */ for (bi = 0; bi < boxn; bi++) { boxes[bi].LL.x = INT_MAX; boxes[bi].UR.x = INT_MIN; } unbounded = TRUE; for (splinepi = 0; splinepi < spl.pn; splinepi++) { ps[splinepi] = spl.ps[splinepi]; } for (loopcnt = 0; unbounded && (loopcnt < LOOP_TRIES); loopcnt++) { limitBoxes (boxes, boxn, ps, spl.pn, delta); /* The following check is necessary because if a box is not very * high, it is possible that the sampling above might miss it. * Therefore, we make the sample finer until all boxes have * valid values. cf. bug 456. Would making sp[] pointfs help? */ for (bi = 0; bi < boxn; bi++) { /* these fp equality tests are used only to detect if the * values have been changed since initialization - ok */ if ((boxes[bi].LL.x == INT_MAX) || (boxes[bi].UR.x == INT_MIN)) { delta *= 2; /* try again with a finer interval */ if (delta > INT_MAX/boxn) /* in limitBoxes, boxn*delta must fit in an int, so give up */ loopcnt = LOOP_TRIES; break; } } if (bi == boxn) unbounded = FALSE; } if (unbounded) { /* Either an extremely short, even degenerate, box, or some failure with the path * planner causing the spline to miss some boxes. In any case, use the shortest path * to bound the boxes. This will probably mean a bad edge, but we avoid an infinite * loop and we can see the bad edge, and even use the showboxes scaffolding. */ Ppolyline_t polyspl; agerr(AGWARN, "Unable to reclaim box space in spline routing for edge \"%s\" -> \"%s\". Something is probably seriously wrong.\n", agnameof(agtail(realedge)), agnameof(aghead(realedge))); make_polyline (pl, &polyspl); limitBoxes (boxes, boxn, polyspl.ps, polyspl.pn, INIT_DELTA); free (polyspl.ps); } *npoints = spl.pn; #ifdef DEBUG if (GD_showboxes(agraphof(aghead(realedge))) == 2 || GD_showboxes(agraphof(agtail(realedge))) == 2 || ED_showboxes(realedge) == 2 || ND_showboxes(aghead(realedge)) == 2 || ND_showboxes(agtail(realedge)) == 2) printboxes(boxn, boxes); #endif return ps; }
static pointf *_routesplines(path * pp, int *npoints, int polyline) { Ppoly_t poly; Ppolyline_t pl, spl; int splinepi; Ppoint_t eps[2]; Pvector_t evs[2]; int edgei, prev, next; pointf sp[4]; int pi, bi, si; double t; boxf *boxes; int boxn; edge_t* realedge; int flip; int delta = 10; nedges++; nboxes += pp->nbox; for (realedge = (edge_t *) pp->data; #ifdef NOTNOW origedge = realedge; #endif realedge && ED_edge_type(realedge) != NORMAL; realedge = ED_to_orig(realedge)); if (!realedge) { agerr(AGERR, "in routesplines, cannot find NORMAL edge\n"); abort(); } boxes = pp->boxes; boxn = pp->nbox; checkpath(boxn, boxes, pp); #ifdef DEBUG if (debugleveln(realedge, 1)) printboxes(boxn, boxes); if (debugleveln(realedge, 3)) { psprintinit(1); psprintboxes(boxn, boxes); } #endif if (boxn * 8 > polypointn) { polypoints = ALLOC(boxn * 8, polypoints, Ppoint_t); polypointn = boxn * 8; } if ((boxn > 1) && (boxes[0].LL.y > boxes[1].LL.y)) { flip = 1; for (bi = 0; bi < boxn; bi++) { double v = boxes[bi].UR.y; boxes[bi].UR.y = -1*boxes[bi].LL.y; boxes[bi].LL.y = -v; } } else flip = 0; if (agtail(realedge) != aghead(realedge)) { /* I assume that the path goes either down only or up - right - down */ for (bi = 0, pi = 0; bi < boxn; bi++) { next = prev = 0; if (bi > 0) prev = (boxes[bi].LL.y > boxes[bi - 1].LL.y) ? -1 : 1; if (bi < boxn - 1) next = (boxes[bi + 1].LL.y > boxes[bi].LL.y) ? 1 : -1; if (prev != next) { if (next == -1 || prev == 1) { polypoints[pi].x = boxes[bi].LL.x; polypoints[pi++].y = boxes[bi].UR.y; polypoints[pi].x = boxes[bi].LL.x; polypoints[pi++].y = boxes[bi].LL.y; } else { polypoints[pi].x = boxes[bi].UR.x; polypoints[pi++].y = boxes[bi].LL.y; polypoints[pi].x = boxes[bi].UR.x; polypoints[pi++].y = boxes[bi].UR.y; } } else if (prev == 0) { /* single box */ polypoints[pi].x = boxes[bi].LL.x; polypoints[pi++].y = boxes[bi].UR.y; polypoints[pi].x = boxes[bi].LL.x; polypoints[pi++].y = boxes[bi].LL.y; } else { if (!(prev == -1 && next == -1)) abort(); } } for (bi = boxn - 1; bi >= 0; bi--) { next = prev = 0; if (bi < boxn - 1) prev = (boxes[bi].LL.y > boxes[bi + 1].LL.y) ? -1 : 1; if (bi > 0) next = (boxes[bi - 1].LL.y > boxes[bi].LL.y) ? 1 : -1; if (prev != next) { if (next == -1 || prev == 1 ) { polypoints[pi].x = boxes[bi].LL.x; polypoints[pi++].y = boxes[bi].UR.y; polypoints[pi].x = boxes[bi].LL.x; polypoints[pi++].y = boxes[bi].LL.y; } else { polypoints[pi].x = boxes[bi].UR.x; polypoints[pi++].y = boxes[bi].LL.y; polypoints[pi].x = boxes[bi].UR.x; polypoints[pi++].y = boxes[bi].UR.y; } } else if (prev == 0) { /* single box */ polypoints[pi].x = boxes[bi].UR.x; polypoints[pi++].y = boxes[bi].LL.y; polypoints[pi].x = boxes[bi].UR.x; polypoints[pi++].y = boxes[bi].UR.y; } else { if (!(prev == -1 && next == -1)) { /* it went badly, e.g. degenerate box in boxlist */ *npoints = 0; abort(); /* for correctness sake, it's best to just stop */ return ps; /* could also be reported as a lost edge (no spline) */ } polypoints[pi].x = boxes[bi].UR.x; polypoints[pi++].y = boxes[bi].LL.y; polypoints[pi].x = boxes[bi].UR.x; polypoints[pi++].y = boxes[bi].UR.y; polypoints[pi].x = boxes[bi].LL.x; polypoints[pi++].y = boxes[bi].UR.y; polypoints[pi].x = boxes[bi].LL.x; polypoints[pi++].y = boxes[bi].LL.y; } } } else { abort(); } if (flip) { int i; for (bi = 0; bi < boxn; bi++) { int v = boxes[bi].UR.y; boxes[bi].UR.y = -1*boxes[bi].LL.y; boxes[bi].LL.y = -v; } for (i = 0; i < pi; i++) polypoints[i].y *= -1; } for (bi = 0; bi < boxn; bi++) boxes[bi].LL.x = INT_MAX, boxes[bi].UR.x = INT_MIN; poly.ps = polypoints, poly.pn = pi; eps[0].x = pp->start.p.x, eps[0].y = pp->start.p.y; eps[1].x = pp->end.p.x, eps[1].y = pp->end.p.y; if (Pshortestpath(&poly, eps, &pl) == -1) abort(); #ifdef DEBUG if (debugleveln(realedge, 3)) { psprintpoly(poly); psprintline(pl); } #endif if (polyline) { make_polyline (pl, &spl); } else { if (poly.pn > edgen) { edges = ALLOC(poly.pn, edges, Pedge_t); edgen = poly.pn; } for (edgei = 0; edgei < poly.pn; edgei++) { edges[edgei].a = polypoints[edgei]; edges[edgei].b = polypoints[(edgei + 1) % poly.pn]; } if (pp->start.constrained) { evs[0].x = cos(pp->start.theta); evs[0].y = sin(pp->start.theta); } else evs[0].x = evs[0].y = 0; if (pp->end.constrained) { evs[1].x = -cos(pp->end.theta); evs[1].y = -sin(pp->end.theta); } else evs[1].x = evs[1].y = 0; if (Proutespline(edges, poly.pn, pl, evs, &spl) == -1) abort(); #ifdef DEBUG if (debugleveln(realedge, 3)) { psprintspline(spl); psprintinit(0); } #endif } mkspacep(spl.pn); for (bi = 0; bi < boxn; bi++) { boxes[bi].LL.x = INT_MAX; boxes[bi].UR.x = INT_MIN; } for (splinepi = 0; splinepi < spl.pn; splinepi++) { ps[splinepi] = spl.ps[splinepi]; } REDO: for (splinepi = 0; splinepi + 3 < spl.pn; splinepi += 3) { int num_div = delta * boxn; for (si = 0; si <= num_div; si++) { t = si / (double)num_div; sp[0] = ps[splinepi]; sp[1] = ps[splinepi + 1]; sp[2] = ps[splinepi + 2]; sp[3] = ps[splinepi + 3]; sp[0].x = sp[0].x + t * (sp[1].x - sp[0].x); sp[0].y = sp[0].y + t * (sp[1].y - sp[0].y); sp[1].x = sp[1].x + t * (sp[2].x - sp[1].x); sp[1].y = sp[1].y + t * (sp[2].y - sp[1].y); sp[2].x = sp[2].x + t * (sp[3].x - sp[2].x); sp[2].y = sp[2].y + t * (sp[3].y - sp[2].y); sp[0].x = sp[0].x + t * (sp[1].x - sp[0].x); sp[0].y = sp[0].y + t * (sp[1].y - sp[0].y); sp[1].x = sp[1].x + t * (sp[2].x - sp[1].x); sp[1].y = sp[1].y + t * (sp[2].y - sp[1].y); sp[0].x = sp[0].x + t * (sp[1].x - sp[0].x); sp[0].y = sp[0].y + t * (sp[1].y - sp[0].y); for (bi = 0; bi < boxn; bi++) { /* this tested ok on 64bit machines, but on 32bit we need this FUDGE * or graphs/directed/records.gv fails */ #define FUDGE .0001 if (sp[0].y <= boxes[bi].UR.y+FUDGE && sp[0].y >= boxes[bi].LL.y-FUDGE) { if (boxes[bi].LL.x > sp[0].x) boxes[bi].LL.x = sp[0].x; if (boxes[bi].UR.x < sp[0].x) boxes[bi].UR.x = sp[0].x; } } } } /* The following check is necessary because if a box is not very * high, it is possible that the sampling above might miss it. * Therefore, we make the sample finer until all boxes have * valid values. cf. bug 456. Would making sp[] pointfs help? */ for (bi = 0; bi < boxn; bi++) { /* these fp equality tests are used only to detect if the * values have been changed since initialization - ok */ if ((boxes[bi].LL.x == INT_MAX) || (boxes[bi].UR.x == INT_MIN)) { delta *= 2; goto REDO; } } *npoints = spl.pn; #ifdef DEBUG if (GD_showboxes(realedge->head->graph) == 2 || GD_showboxes(realedge->tail->graph) == 2 || ED_showboxes(realedge) == 2 || ND_showboxes(realedge->head) == 2 || ND_showboxes(realedge->tail) == 2) printboxes(boxn, boxes); #endif return ps; }
/* genroute: * Generate splines for e and cohorts. * Edges go from s to t. * Return 0 on success. */ static int genroute(graph_t* g, tripoly_t * trip, int s, int t, edge_t * e, int doPolyline) { pointf eps[2]; Pvector_t evs[2]; pointf **cpts; /* lists of control points */ Ppoly_t poly; Ppolyline_t pl, spl; int i, j; Ppolyline_t mmpl; Pedge_t *medges = N_GNEW(trip->poly.pn, Pedge_t); int pn; int mult = ED_count(e); node_t* head = aghead(e); eps[0].x = trip->poly.ps[s].x, eps[0].y = trip->poly.ps[s].y; eps[1].x = trip->poly.ps[t].x, eps[1].y = trip->poly.ps[t].y; Pshortestpath(&(trip->poly), eps, &pl); if (pl.pn == 2) { makeStraightEdge(agraphof(head), e, doPolyline); return 0; } evs[0].x = evs[0].y = 0; evs[1].x = evs[1].y = 0; if ((mult == 1) || Concentrate) { poly = trip->poly; for (j = 0; j < poly.pn; j++) { medges[j].a = poly.ps[j]; medges[j].b = poly.ps[(j + 1) % poly.pn]; } tweakPath (poly, s, t, pl); Proutespline(medges, poly.pn, pl, evs, &spl); finishEdge (g, e, spl, aghead(e) != head, eps[0], eps[1]); free(medges); return 0; } pn = 2 * (pl.pn - 1); cpts = N_NEW(pl.pn - 2, pointf *); for (i = 0; i < pl.pn - 2; i++) { cpts[i] = mkCtrlPts(t, mult+1, pl.ps[i], pl.ps[i + 1], pl.ps[i + 2], trip); if (!cpts[i]) { agerr(AGWARN, "Could not create control points for multiple spline for edge (%s,%s)\n", agnameof(agtail(e)), agnameof(aghead(e))); return 1; } } poly.ps = N_GNEW(pn, pointf); poly.pn = pn; for (i = 0; i < mult; i++) { poly.ps[0] = eps[0]; for (j = 1; j < pl.pn - 1; j++) { poly.ps[j] = cpts[j - 1][i]; } poly.ps[pl.pn - 1] = eps[1]; for (j = 1; j < pl.pn - 1; j++) { poly.ps[pn - j] = cpts[j - 1][i + 1]; } Pshortestpath(&poly, eps, &mmpl); if (doPolyline) { make_polyline (mmpl, &spl); } else { for (j = 0; j < poly.pn; j++) { medges[j].a = poly.ps[j]; medges[j].b = poly.ps[(j + 1) % poly.pn]; } tweakPath (poly, 0, pl.pn-1, mmpl); Proutespline(medges, poly.pn, mmpl, evs, &spl); } finishEdge (g, e, spl, aghead(e) != head, eps[0], eps[1]); e = ED_to_virt(e); } for (i = 0; i < pl.pn - 2; i++) free(cpts[i]); free(cpts); free(medges); free(poly.ps); return 0; }