}
+
+#ifdef WITH_CGRAPH
+/*****CGRAPH BLOCK*/
+static void makePortLabels(edge_t * e)
+{
+ if (ED_head_label(e) && !ED_head_label(e)->set) {
+ place_portlabel(e, TRUE);
+ updateBB(agraphof(agtail(e)), ED_head_label(e));
+ }
+ if (ED_tail_label(e) && !ED_tail_label(e)->set) {
+ place_portlabel(e, FALSE);
+ updateBB(agraphof(agtail(e)), ED_tail_label(e));
+ }
+}
+
+/* endPoints:
+ * Extract the actual end points of the spline, where
+ * they touch the node.
+ */
+static void endPoints(splines * spl, point * p, point * q)
+{
+ bezier bz;
+
+ bz = spl->list[0];
+ if (bz.sflag)
+ *p = bz.sp;
+ else
+ *p = bz.list[0];
+ bz = spl->list[spl->size - 1];
+ if (bz.eflag)
+ *q = bz.ep;
+ else
+ *q = bz.list[bz.size - 1];
+}
+
+/* polylineMidpoint;
+ * Find midpoint of polyline.
+ * pp and pq are set to the endpoints of the line segment containing it.
+ */
+static point
+polylineMidpoint (splines* spl, point* pp, point* pq)
+{
+ bezier bz;
+ int i, j, k;
+ double d, dist = 0;
+ point m;
+ pointf pf, qf, mf;
+
+ for (i = 0; i < spl->size; i++) {
+ bz = spl->list[i];
+ for (j = 0, k=3; k < bz.size; j+=3,k+=3) {
+ P2PF (bz.list[j],pf);
+ P2PF (bz.list[k],qf);
+ dist += DIST(pf,qf);
+ }
+ }
+ dist /= 2;
+ for (i = 0; i < spl->size; i++) {
+ bz = spl->list[i];
+ for (j = 0, k=3; k < bz.size; j+=3,k+=3) {
+ P2PF (bz.list[j],pf);
+ P2PF (bz.list[k],qf);
+ d = DIST(pf,qf);
+ if (d >= dist) {
+ *pp = bz.list[j];
+ *pq = bz.list[k];
+ mf.x = ((qf.x*dist) + (pf.x*(d-dist)))/d;
+ mf.y = ((qf.y*dist) + (pf.y*(d-dist)))/d;
+ PF2P(mf, m);
+ return m;
+ }
+ else
+ dist -= d;
+ }
+ }
+ assert (FALSE); /* should never get here */
+ return m;
+}
+
+#define LEFTOF(a,b,c) (((a.y - b.y)*(c.x - b.x) - (c.y - b.y)*(a.x - b.x)) > 0)
+#define MAXLABELWD (POINTS_PER_INCH/2.0)
+
+/* addEdgeLabels:
+ * rp and rq are the port points of the tail and head node.
+ * Adds label, headlabel and taillabel.
+ * The use of 2 and 4 in computing ld.x and ld.y are fudge factors, to
+ * introduce a bit of spacing.
+ * Updates bounding box.
+ * We try to use the actual endpoints of the spline, as they may differ
+ * significantly from rp and rq, but if the spline is degenerate (e.g.,
+ * the nodes overlap), we use rp and rq.
+ */
+void addEdgeLabels(edge_t * e, point rp, point rq)
+{
+ int et = EDGE_TYPE (agraphof(aghead(e))->root);
+ point p, q;
+ point d; /* midpoint of segment p-q */
+ point ld;
+ point sp;
+ point del;
+ pointf spf;
+ double f, ht, wd, dist2;
+ int leftOf;
+
+ if (ED_label(e) && !ED_label(e)->set) {
+ endPoints(ED_spl(e), &p, &q);
+ if ((p.x == q.x) && (p.y == q.y)) { /* degenerate spline */
+ p = rp;
+ q = rq;
+ sp = p;
+ }
+ else if (et == ET_SPLINE) {
+ d.x = (q.x + p.x) / 2;
+ d.y = (p.y + q.y) / 2;
+ sp = dotneato_closest(ED_spl(e), d);
+ }
+ else { /* ET_PLINE or ET_LINE */
+ sp = polylineMidpoint (ED_spl(e), &p, &q);
+ }
+ del.x = q.x - p.x;
+ del.y = q.y - p.y;
+ dist2 = del.x*del.x + del.y*del.y;
+ ht = (ED_label(e)->dimen.y + 2)/2.0;
+ spf.x = sp.x;
+ spf.y = sp.y;
+ if (dist2) {
+ wd = (MIN(ED_label(e)->dimen.x + 2, MAXLABELWD))/2.0;
+ leftOf = LEFTOF(p, q, sp);
+ if ((leftOf && (del.y >= 0)) || (!leftOf && (del.y < 0))) {
+ if (del.x*del.y >= 0)
+ ht *= -1;
+ }
+ else {
+ wd *= -1;
+ if (del.x*del.y < 0)
+ ht *= -1;
+ }
+ f = (del.y*wd - del.x*ht)/dist2;
+ ld.x = -f*del.y;
+ ld.y = f*del.x;
+ }
+ else { /* end points the same */
+ ld.x = 0;
+ ld.y = -ht;
+ }
+
+ ED_label(e)->p.x = spf.x + ld.x;
+ ED_label(e)->p.y = spf.y + ld.y;
+ ED_label(e)->set = TRUE;
+ updateBB(agraphof(agtail(e)), ED_label(e));
+ }
+ makePortLabels(e);
+}
+
+typedef struct {
+ node_t *n1;
+ point p1;
+ node_t *n2;
+ point p2;
+} edgeinfo;
+typedef struct {
+ Dtlink_t link;
+ edgeinfo id;
+ edge_t *e;
+} edgeitem;
+
+static void *newitem(Dt_t * d, edgeitem * obj, Dtdisc_t * disc)
+{
+ edgeitem *newp;
+
+ NOTUSED(disc);
+ newp = NEW(edgeitem);
+ newp->id = obj->id;
+ newp->e = obj->e;
+ ED_count(newp->e) = 1;
+
+ return newp;
+}
+
+static void freeitem(Dt_t * d, edgeitem * obj, Dtdisc_t * disc)
+{
+ free(obj);
+}
+
+static int
+cmpitems(Dt_t * d, edgeinfo * key1, edgeinfo * key2, Dtdisc_t * disc)
+{
+ int x;
+
+ if (key1->n1 > key2->n1)
+ return 1;
+ if (key1->n1 < key2->n1)
+ return -1;
+ if (key1->n2 > key2->n2)
+ return 1;
+ if (key1->n2 < key2->n2)
+ return -1;
+
+ if ((x = key1->p1.x - key2->p1.x))
+ return x;
+ if ((x = key1->p1.y - key2->p1.y))
+ return x;
+ if ((x = key1->p2.x - key2->p2.x))
+ return x;
+ return (key1->p2.y - key2->p2.y);
+}
+
+Dtdisc_t edgeItemDisc = {
+ offsetof(edgeitem, id),
+ sizeof(edgeinfo),
+ offsetof(edgeitem, link),
+ (Dtmake_f) newitem,
+ (Dtfree_f) freeitem,
+ (Dtcompar_f) cmpitems,
+ 0,
+ 0,
+ 0
+};
+
+/* equivEdge:
+ * See if we have already encountered an edge between the same
+ * node:port pairs. If so, return the earlier edge. If not,
+ * this edge is added to map and returned.
+ * We first have to canonicalize the key fields using a lexicographic
+ * ordering.
+ */
+static edge_t *equivEdge(Dt_t * map, edge_t * e)
+{
+ edgeinfo test;
+ edgeitem dummy;
+ edgeitem *ip;
+
+ if (agtail(e) < aghead(e)) {
+ test.n1 = agtail(e);
+ test.p1 = ED_tail_port(e).p;
+ test.n2 = aghead(e);
+ test.p2 = ED_head_port(e).p;
+ } else if (agtail(e) > aghead(e)) {
+ test.n2 = agtail(e);
+ test.p2 = ED_tail_port(e).p;
+ test.n1 = aghead(e);
+ test.p1 = ED_head_port(e).p;
+ } else {
+ point hp = ED_head_port(e).p;
+ point tp = ED_tail_port(e).p;
+ if (tp.x < hp.x) {
+ test.p1 = tp;
+ test.p2 = hp;
+ } else if (tp.x > hp.x) {
+ test.p1 = hp;
+ test.p2 = tp;
+ } else if (tp.y < hp.y) {
+ test.p1 = tp;
+ test.p2 = hp;
+ } else if (tp.y > hp.y) {
+ test.p1 = hp;
+ test.p2 = tp;
+ } else {
+ test.p1 = test.p2 = tp;
+ }
+ test.n2 = test.n1 = agtail(e);
+ }
+ dummy.id = test;
+ dummy.e = e;
+ ip = dtinsert(map, &dummy);
+ return ip->e;
+}
+
+
+/* makeSelfArcs:
+ * Generate loops. We use the library routine makeSelfEdge
+ * which also places the labels.
+ * We have to handle port labels here.
+ * as well as update the bbox from edge labels.
+ */
+void makeSelfArcs(path * P, edge_t * e, int stepx)
+{
+ int cnt = ED_count(e);
+
+ if (cnt == 1) {
+ edge_t *edges1[1];
+ edges1[0] = e;
+ makeSelfEdge(P, edges1, 0, 1, stepx, stepx, &sinfo);
+ if (ED_label(e))
+ updateBB(agraphof(agtail(e)), ED_label(e));
+ makePortLabels(e);
+ } else {
+ int i;
+ edge_t **edges = N_GNEW(cnt, edge_t *);
+ for (i = 0; i < cnt; i++) {
+ edges[i] = e;
+ e = ED_to_virt(e);
+ }
+ makeSelfEdge(P, edges, 0, cnt, stepx, stepx, &sinfo);
+ for (i = 0; i < cnt; i++) {
+ e = edges[i];
+ if (ED_label(e))
+ updateBB(agraphof(agtail(e)), ED_label(e));
+ makePortLabels(e);
+ }
+ free(edges);
+ }
+}
+
+/* makeStraightEdge:
+ *
+ * FIX: handle ports on boundary?
+ */
+void
+makeStraightEdge(graph_t * g, edge_t * e, int doPolyline)
+{
+ point dumb[4];
+ node_t *n = agtail(e);
+ node_t *head = aghead(e);
+ int e_cnt = ED_count(e);
+ pointf perp;
+ point del;
+ edge_t *e0;
+ int i, j, xstep, dx;
+ double l_perp;
+ point dumber[4];
+ point p, q;
+
+ p = dumb[1] = dumb[0] = add_points(ND_coord_i(n), ED_tail_port(e).p);
+ q = dumb[2] = dumb[3] =
+ add_points(ND_coord_i(head), ED_head_port(e).p);
+
+ if (e_cnt == 1) {
+ clip_and_install(e, aghead(e), dumb, 4, &sinfo);
+ addEdgeLabels(e, p, q);
+ return;
+ }
+
+ e0 = e;
+ perp.x = dumb[0].y - dumb[3].y;
+ perp.y = dumb[3].x - dumb[0].x;
+ if ((perp.x == 0) && (perp.y == 0)) {
+ /* degenerate case */
+ dumb[1] = dumb[0];
+ dumb[2] = dumb[3];
+ del.x = 0;
+ del.y = 0;
+ }
+ else {
+ l_perp = sqrt(perp.x * perp.x + perp.y * perp.y);
+ xstep = GD_nodesep(g);
+ 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 (doPolyline) {
+ Ppoint_t pts[4];
+ Ppolyline_t spl, line;
+ point* ispline;
+
+ line.pn = 4;
+ line.ps = pts;
+ for (i=0; i < 4; i++)
+ P2PF (dumber[i], pts[i]);
+ make_polyline (line, &spl);
+ ispline = N_GNEW(spl.pn, point);
+ for (i=0; i < spl.pn; i++)
+ PF2P (spl.ps[i], ispline[i]);
+ clip_and_install(e0, aghead(e0), ispline, spl.pn, &sinfo);
+ free(ispline);
+ }
+ else
+ clip_and_install(e0, aghead(e0), dumber, 4, &sinfo);
+
+ addEdgeLabels(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;
+ }
+}
+
+#define LEN(x,y) sqrt((x)*(x)+(y)*(y))
+
+/* makeObstacle:
+ * Given a node, return an obstacle reflecting the
+ * node's geometry. pmargin specifies how much space to allow
+ * around the polygon.
+ * Returns the constructed polygon on success, NULL on failure.
+ * Failure means the node shape is not supported.
+ *
+ * The polygon has its vertices in CW order.
+ *
+ */
+Ppoly_t *makeObstacle(node_t * n, expand_t* pmargin)
+{
+ Ppoly_t *obs;
+ polygon_t *poly;
+ double adj = 0.0;
+ int j, sides;
+ pointf polyp;
+ box b;
+ point pt;
+ field_t *fld;
+ epsf_t *desc;
+
+ switch (shapeOf(n)) {
+ case SH_POLY:
+ case SH_POINT:
+ obs = NEW(Ppoly_t);
+ poly = (polygon_t *) ND_shape_info(n);
+ if (poly->sides >= 3) {
+ sides = poly->sides;
+ } else { /* ellipse */
+ sides = 8;
+ adj = drand48() * .01;
+ }
+ obs->pn = sides;
+ obs->ps = N_NEW(sides, Ppoint_t);
+ /* assuming polys are in CCW order, and pathplan needs CW */
+ for (j = 0; j < sides; j++) {
+ double xmargin = 0.0, ymargin = 0.0;
+ if (poly->sides >= 3) {
+ if (pmargin->doAdd) {
+ if (poly->sides == 4) {
+ switch (j) {
+ case 0 :
+ xmargin = pmargin->x;
+ ymargin = pmargin->y;
+ break;
+ case 1 :
+ xmargin = -pmargin->x;
+ ymargin = pmargin->y;
+ break;
+ case 2 :
+ xmargin = -pmargin->x;
+ ymargin = -pmargin->y;
+ break;
+ case 3 :
+ xmargin = pmargin->x;
+ ymargin = -pmargin->y;
+ break;
+ }
+ polyp.x = poly->vertices[j].x + xmargin;
+ polyp.y = poly->vertices[j].y + ymargin;
+ }
+ else {
+ double h = LEN(poly->vertices[j].x,poly->vertices[j].y);
+ polyp.x = poly->vertices[j].x * (1.0 + pmargin->x/h);
+ polyp.y = poly->vertices[j].y * (1.0 + pmargin->y/h);
+ }
+ }
+ else {
+ polyp.x = poly->vertices[j].x * pmargin->x;
+ polyp.y = poly->vertices[j].y * pmargin->y;
+ }
+ } else {
+ double c, s;
+ c = cos(2.0 * M_PI * j / sides + adj);
+ s = sin(2.0 * M_PI * j / sides + adj);
+ if (pmargin->doAdd) {
+ polyp.x = c*(ND_lw_i(n)+ND_rw_i(n)+pmargin->x) / 2.0;
+ polyp.y = s*(ND_ht_i(n)+pmargin->y) / 2.0;
+ }
+ else {
+ polyp.x = pmargin->x * c * (ND_lw_i(n) + ND_rw_i(n)) / 2.0;
+ polyp.y = pmargin->y * s * ND_ht_i(n) / 2.0;
+ }
+ }
+ obs->ps[sides - j - 1].x = polyp.x + ND_coord_i(n).x;
+ obs->ps[sides - j - 1].y = polyp.y + ND_coord_i(n).y;
+ }
+ break;
+ case SH_RECORD:
+ fld = (field_t *) ND_shape_info(n);
+ b = fld->b;
+ obs = NEW(Ppoly_t);
+ obs->pn = 4;
+ obs->ps = N_NEW(4, Ppoint_t);
+ /* CW order */
+ pt = ND_coord_i(n);
+ if (pmargin->doAdd) {
+ obs->ps[0] = genPt(b.LL.x-pmargin->x, b.LL.y-pmargin->y, pt);
+ obs->ps[1] = genPt(b.LL.x-pmargin->x, b.UR.y+pmargin->y, pt);
+ obs->ps[2] = genPt(b.UR.x+pmargin->x, b.UR.y+pmargin->y, pt);
+ obs->ps[3] = genPt(b.UR.x+pmargin->x, b.LL.y-pmargin->y, pt);
+ }
+ else {
+ obs->ps[0] = recPt(b.LL.x, b.LL.y, pt, pmargin);
+ obs->ps[1] = recPt(b.LL.x, b.UR.y, pt, pmargin);
+ obs->ps[2] = recPt(b.UR.x, b.UR.y, pt, pmargin);
+ obs->ps[3] = recPt(b.UR.x, b.LL.y, pt, pmargin);
+ }
+ break;
+ case SH_EPSF:
+ desc = (epsf_t *) (ND_shape_info(n));
+ obs = NEW(Ppoly_t);
+ obs->pn = 4;
+ obs->ps = N_NEW(4, Ppoint_t);
+ /* CW order */
+ pt = ND_coord_i(n);
+ if (pmargin->doAdd) {
+ obs->ps[0] = genPt(-ND_lw_i(n)-pmargin->x, -ND_ht_i(n)-pmargin->y,pt);
+ obs->ps[1] = genPt(-ND_lw_i(n)-pmargin->x, ND_ht_i(n)+pmargin->y,pt);
+ obs->ps[2] = genPt(ND_rw_i(n)+pmargin->x, ND_ht_i(n)+pmargin->y,pt);
+ obs->ps[3] = genPt(ND_rw_i(n)+pmargin->x, -ND_ht_i(n)-pmargin->y,pt);
+ }
+ else {
+ obs->ps[0] = recPt(-ND_lw_i(n), -ND_ht_i(n), pt, pmargin);
+ obs->ps[1] = recPt(-ND_lw_i(n), ND_ht_i(n), pt, pmargin);
+ obs->ps[2] = recPt(ND_rw_i(n), ND_ht_i(n), pt, pmargin);
+ obs->ps[3] = recPt(ND_rw_i(n), -ND_ht_i(n), pt, pmargin);
+ }
+ break;
+ default:
+ obs = NULL;
+ break;
+ }
+ return obs;
+}
+
+/* getPath
+ * Construct the shortest path from one endpoint of e to the other.
+ * The obstacles and their number are given by obs and npoly.
+ * vconfig is a precomputed data structure to help in the computation.
+ * If chkPts is true, the function finds the polygons, if any, containing
+ * the endpoints and tells the shortest path computation to ignore them.
+ * Assumes this info is set in ND_lim, usually from _spline_edges.
+ * Returns the shortest path.
+ */
+Ppolyline_t
+getPath(edge_t * e, vconfig_t * vconfig, int chkPts, Ppoly_t ** obs,
+ int npoly)
+{
+ Ppolyline_t line;
+ int pp, qp;
+ Ppoint_t p, q;
+ point p1, q1;
+
+ p1 = add_points(ND_coord_i(agtail(e)), ED_tail_port(e).p);
+ q1 = add_points(ND_coord_i(aghead(e)), ED_head_port(e).p);
+ P2PF(p1, p);
+ P2PF(q1, q);
+
+ /* determine the polygons (if any) that contain the endpoints */
+ pp = qp = POLYID_NONE;
+ if (chkPts) {
+ pp = ND_lim(agtail(e));
+ qp = ND_lim(aghead(e));
+/*
+ for (i = 0; i < npoly; i++) {
+ if ((pp == POLYID_NONE) && in_poly(*obs[i], p))
+ pp = i;
+ if ((qp == POLYID_NONE) && in_poly(*obs[i], q))
+ qp = i;
+ }
+*/
+ }
+ Pobspath(vconfig, p, pp, q, qp, &line);
+ return line;
+}
+
+/* makePolyline:
+ */
+static void
+makePolyline(edge_t * e)
+{
+ int i;
+ Ppolyline_t spl, line = ED_path(e);
+ point* ispline;
+ point p1, q1;
+ Ppoint_t p0, q0;
+
+ p0 = line.ps[0];
+ q0 = line.ps[line.pn - 1];
+ make_polyline (line, &spl);
+ ispline = N_GNEW(spl.pn, point);
+ for (i=0; i < spl.pn; i++) {
+ PF2P (spl.ps[i], ispline[i]);
+ }
+
+ if (Verbose > 1)
+ fprintf(stderr, "polyline %s %s\n", agnameof(agtail(e)), agnameof(aghead(e)));
+ clip_and_install(e, aghead(e), ispline, spl.pn, &sinfo);
+ free(ispline);
+ PF2P(p0, p1);
+ PF2P(q0, q1);
+ addEdgeLabels(e, p1, q1);
+}
+
+/* makeSpline:
+ * Construct a spline connecting the endpoints of e, avoiding the npoly
+ * obstacles obs.
+ * The resultant spline is attached to the edge, the positions of any
+ * edge labels are computed, and the graph's bounding box is recomputed.
+ *
+ * If chkPts is true, the function checks if one or both of the endpoints
+ * is on or inside one of the obstacles and, if so, tells the shortest path
+ * computation to ignore them.
+ */
+void makeSpline(edge_t * e, Ppoly_t ** obs, int npoly, boolean chkPts)
+{
+ Ppolyline_t line, spline;
+ Pvector_t slopes[2];
+ int i, n_barriers;
+ int pp, qp;
+ Ppoint_t p, q;
+ point *ispline;
+ Pedge_t *barriers;
+ point p1, q1;
+
+ line = ED_path(e);
+ p = line.ps[0];
+ q = line.ps[line.pn - 1];
+ /* determine the polygons (if any) that contain the endpoints */
+ pp = qp = POLYID_NONE;
+ if (chkPts)
+ for (i = 0; i < npoly; i++) {
+ if ((pp == POLYID_NONE) && in_poly(*obs[i], p))
+ pp = i;
+ if ((qp == POLYID_NONE) && in_poly(*obs[i], q))
+ qp = i;
+ }
+
+ make_barriers(obs, npoly, pp, qp, &barriers, &n_barriers);
+ slopes[0].x = slopes[0].y = 0.0;
+ slopes[1].x = slopes[1].y = 0.0;
+ Proutespline(barriers, n_barriers, line, slopes, &spline);
+
+ /* north why did you ever use int coords */
+ ispline = N_GNEW(spline.pn, point);
+ for (i = 0; i < spline.pn; i++) {
+ ispline[i].x = ROUND(spline.ps[i].x);
+ ispline[i].y = ROUND(spline.ps[i].y);
+ }
+ if (Verbose > 1)
+ fprintf(stderr, "spline %s %s\n", agnameof(agtail(e)),agnameof(aghead(e)));
+ clip_and_install(e, aghead(e), ispline, spline.pn, &sinfo);
+ free(ispline);
+ free(barriers);
+ PF2P(p, p1);
+ PF2P(q, q1);
+ addEdgeLabels(e, p1, q1);
+}
+
+ /* True if either head or tail has a port on its boundary */
+#define BOUNDARY_PORT(e) ((ED_tail_port(e).side)||(ED_head_port(e).side))
+
+/* _spline_edges:
+ * Basic default routine for creating edges.
+ * If splines are requested, we construct the obstacles.
+ * If not, or nodes overlap, the function reverts to line segments.
+ * NOTE: intra-cluster edges are not constrained to
+ * remain in the cluster's bounding box and, conversely, a cluster's box
+ * is not altered to reflect intra-cluster edges.
+ * If Nop > 1 and the spline exists, it is just copied.
+ */
+static int _spline_edges(graph_t * g, expand_t* pmargin, int edgetype)
+{
+ node_t *n;
+ edge_t *e;
+ edge_t *e0;
+ Ppoly_t **obs = 0;
+ Ppoly_t *obp;
+ int cnt, i = 0, npoly;
+ vconfig_t *vconfig = 0;
+ path *P = NULL;
+ int useEdges = (Nop > 1);
+#ifdef ORTHO
+ extern void orthoEdges (Agraph_t* g, int useLbls, splineInfo* sinfo);
+#endif
+ router_t* rtr = 0;
+
+ /* build configuration */
+ if (edgetype != ET_LINE) {
+ obs = N_NEW(agnnodes(g), Ppoly_t *);
+ for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
+ obp = makeObstacle(n, pmargin);
+ if (obp) {
+ ND_lim(n) = i;
+ obs[i++] = obp;
+ }
+ else
+ ND_lim(n) = POLYID_NONE;
+ }
+ } else {
+ obs = 0;
+ }
+ npoly = i;
+ if (obs) {
+ if (Plegal_arrangement(obs, npoly)) {
+ if (edgetype != ET_ORTHO) vconfig = Pobsopen(obs, npoly);
+ }
+ else if (Verbose)
+ fprintf(stderr,
+ "nodes touch - falling back to straight line edges\n");
+ }
+
+ /* route edges */
+ if (Verbose)
+ fprintf(stderr, "Creating edges using %s\n",
+ (edgetype == ET_ORTHO) ? "orthogonal lines" :
+ (vconfig ? (edgetype == ET_SPLINE ? "splines" : "polylines") :
+ "line segments"));
+ if (vconfig) {
+ /* path-finding pass */
+ for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
+ for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
+ ED_path(e) = getPath(e, vconfig, TRUE, obs, npoly);
+ }
+ }
+ }
+#ifdef ORTHO
+ else if (edgetype == ET_ORTHO) {
+ orthoEdges (g, 0, &sinfo);
+ useEdges = 1;
+ }
+#endif
+
+ /* spline-drawing pass */
+ for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
+ for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
+/* fprintf (stderr, "%s -- %s %d\n", e->tail->name, e->head->name, ED_count(e)); */
+ node_t *head = aghead(e);
+ if (useEdges && ED_spl(e)) {
+ addEdgeLabels(e,
+ add_points(ND_coord_i(n), ED_tail_port(e).p),
+ add_points(ND_coord_i(head),
+ ED_head_port(e).p));
+ }
+ else if (ED_count(e) == 0) continue; /* only do representative */
+ else if (n == head) { /* self arc */
+ if (!P) {
+ P = NEW(path);
+ P->boxes = N_NEW(agnnodes(g) + 20 * 2 * 9, box);
+ }
+ makeSelfArcs(P, e, GD_nodesep(g));
+ } else if (vconfig) { /* ET_SPLINE or ET_POLYLINE */
+#ifdef HAVE_GTS
+ if ((ED_count(e) > 1) || BOUNDARY_PORT(e)) {
+ int fail = 0;
+ if ((ED_path(e).pn == 2) && !BOUNDARY_PORT(e))
+ /* if a straight line can connect the ends */
+ makeStraightEdge(g, e, edgetype == ET_PLINE);
+ else {
+ if (!rtr) rtr = mkRouter (obs, npoly);
+ fail = makeMultiSpline(e, rtr, edgetype == ET_PLINE);
+ }
+ if (!fail) continue;
+ }
+ /* We can probably remove this branch and just use
+ * makeMultiSpline. It can also catch the makeStraightEdge
+ * case. We could then eliminate all of the vconfig stuff.
+ */
+#endif
+ cnt = ED_count(e);
+ e0 = e;
+ for (i = 0; i < cnt; i++) {
+ if (edgetype == ET_SPLINE)
+ makeSpline(e0, obs, npoly, TRUE);
+ else
+ makePolyline(e0);
+ e0 = ED_to_virt(e0);
+ }
+ } else {
+ makeStraightEdge(g, e, 0);
+ }
+ }
+ }
+
+#ifdef HAVE_GTS
+ if (rtr)
+ freeRouter (rtr);
+#endif
+
+ if (vconfig)
+ Pobsclose (vconfig);
+ if (P) {
+ free(P->boxes);
+ free(P);
+ }
+ if (obs) {
+ for (i=0; i < npoly; i++)
+ free (obs[i]);
+ free (obs);
+ }
+ return 0;
+}
+
+/* splineEdges:
+ * Main wrapper code for generating edges.
+ * Sets desired separation.
+ * Coalesces equivalent edges (edges * with the same endpoints).
+ * It then calls the edge generating function, and marks the
+ * spline phase complete.
+ * Returns 0 on success.
+ *
+ * The edge function is given the graph, the separation to be added
+ * around obstacles, and the type of edge. It must guarantee
+ * that all bounding boxes are current; in particular, the bounding box of
+ * g must reflect the addition of the edges.
+ */
+int
+splineEdges(graph_t * g, int (*edgefn) (graph_t *, expand_t*, int),
+ int edgetype)
+{
+ node_t *n;
+ edge_t *e;
+ expand_t margin;
+ Dt_t *map;
+
+ margin = esepFactor (g);
+
+ /* find equivalent edges */
+ map = dtopen(&edgeItemDisc, Dtoset);
+ for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
+ for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
+ edge_t *leader = equivEdge(map, e);
+ if (leader != e) {
+ ED_count(leader)++;
+ ED_to_virt(e) = ED_to_virt(leader);
+ ED_to_virt(leader) = e;
+ }
+ }
+ }
+ dtclose(map);
+
+ if (edgefn(g, &margin, edgetype))
+ return 1;
+
+ State = GVSPLINES;
+ return 0;
+}
+
+/* spline_edges1:
+ * Construct edges using default algorithm and given splines value.
+ * Return 0 on success.
+ */
+int spline_edges1(graph_t * g, int edgetype)
+{
+ return splineEdges(g, _spline_edges, edgetype);
+}
+
+/* spline_edges0:
+ * Sets the graph's aspect ratio.
+ * Check splines attribute and construct edges using default algorithm.
+ * If the splines attribute is defined but equal to "", skip edge routing.
+ *
+ * Assumes u.bb for has been computed for g and all clusters
+ * (not just top-level clusters), and that GD_bb(g).LL is at the origin.
+ *
+ * This last criterion is, I believe, mainly to simplify the code
+ * in neato_set_aspect. It would be good to remove this constraint,
+ * as this would allow nodes pinned on input to have the same coordinates
+ * when output in dot or plain format.
+ *
+ */
+void spline_edges0(graph_t * g)
+{
+ int et = EDGE_TYPE (agroot(g));
+ neato_set_aspect(g);
+ if (et == ET_NONE) return;
+#ifndef ORTHO
+ if (et == ET_ORTHO) {
+ agerr (AGWARN, "Orthogonal edges not yet supported\n");
+ et = ET_PLINE;
+ GD_flags(agroot(g)) &= ~ET_ORTHO;
+ GD_flags(agroot(g)) |= ET_PLINE;
+ }
+#endif
+ spline_edges1(g, et);
+}
+
+/* spline_edges:
+ * Compute bounding box, translate graph to origin,
+ * then construct all edges. We assume the graph
+ * has no clusters, and only nodes have been
+ * positioned.
+ */
+void spline_edges(graph_t * g)
+{
+ node_t *n;
+ pointf offset;
+
+ compute_bb(g);
+ offset = cvt2ptf(GD_bb(g).LL);
+ for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
+ ND_pos(n)[0] -= offset.x;
+ ND_pos(n)[1] -= offset.y;
+ }
+ GD_bb(g).UR.x -= GD_bb(g).LL.x;
+ GD_bb(g).UR.y -= GD_bb(g).LL.y;
+ GD_bb(g).LL.x = 0;
+ GD_bb(g).LL.y = 0;
+ spline_edges0(g);
+}
+
+/* scaleEdge:
+ * Scale edge by given factor.
+ * Assume ED_spl != NULL.
+ */
+static void scaleEdge(edge_t * e, double xf, double yf)
+{
+ int i, j;
+ point *pt;
+ bezier *bez;
+ point delh, delt;
+
+ delh.x = POINTS(ND_pos(aghead(e))[0] * (xf - 1.0));
+ delh.y = POINTS(ND_pos(aghead(e))[1] * (yf - 1.0));
+ delt.x = POINTS(ND_pos(agtail(e))[0] * (xf - 1.0));
+ delt.y = POINTS(ND_pos(agtail(e))[1] * (yf - 1.0));
+
+ bez = ED_spl(e)->list;
+ for (i = 0; i < ED_spl(e)->size; i++) {
+ pt = bez->list;
+ for (j = 0; j < bez->size; j++) {
+ if ((i == 0) && (j == 0)) {
+ pt->x += delt.x;
+ pt->y += delt.y;
+ }
+ else if ((i == ED_spl(e)->size-1) && (j == bez->size-1)) {
+ pt->x += delh.x;
+ pt->y += delh.y;
+ }
+ else {
+ pt->x *= xf;
+ pt->y *= yf;
+ }
+ pt++;
+ }
+ if (bez->sflag) {
+ bez->sp.x += delt.x;
+ bez->sp.y += delt.y;
+ }
+ if (bez->eflag) {
+ bez->ep.x += delh.x;
+ bez->ep.y += delh.y;
+ }
+ bez++;
+ }
+
+ if (ED_label(e) && ED_label(e)->set) {
+ ED_label(e)->p.x *= xf;
+ ED_label(e)->p.y *= yf;
+ }
+ if (ED_head_label(e) && ED_head_label(e)->set) {
+ ED_head_label(e)->p.x += delh.x;
+ ED_head_label(e)->p.y += delh.y;
+ }
+ if (ED_tail_label(e) && ED_tail_label(e)->set) {
+ ED_tail_label(e)->p.x += delt.x;
+ ED_tail_label(e)->p.y += delt.y;
+ }
+}
+
+/* scaleBB:
+ * Scale bounding box of clusters of g by given factors.
+ */
+static void scaleBB(graph_t * g, double xf, double yf)
+{
+ int i;
+
+ GD_bb(g).UR.x *= xf;
+ GD_bb(g).UR.y *= yf;
+ GD_bb(g).LL.x *= xf;
+ GD_bb(g).LL.y *= yf;
+
+ if (GD_label(g) && GD_label(g)->set) {
+ GD_label(g)->p.x *= xf;
+ GD_label(g)->p.y *= yf;
+ }
+
+ for (i = 1; i <= GD_n_cluster(g); i++)
+ scaleBB(GD_clust(g)[i], xf, yf);
+}
+
+/* _neato_set_aspect;
+ * Assume all bounding boxes are correct and
+ * that GD_bb(g).LL is at origin.
+ */
+static void _neato_set_aspect(graph_t * g)
+{
+ /* int i; */
+ double xf, yf, actual, desired;
+ node_t *n;
+
+ /* compute_bb(g); */
+ if (GD_drawing(g)->ratio_kind) {
+ /* normalize */
+ assert(GD_bb(g).LL.x == 0);
+ assert(GD_bb(g).LL.y == 0);
+ if (GD_flip(g)) {
+ int t = GD_bb(g).UR.x;
+ GD_bb(g).UR.x = GD_bb(g).UR.y;
+ GD_bb(g).UR.y = t;
+ }
+ if (GD_drawing(g)->ratio_kind == R_FILL) {
+ /* fill is weird because both X and Y can stretch */
+ if (GD_drawing(g)->size.x <= 0)
+ return;
+ xf = (double) GD_drawing(g)->size.x / (double) GD_bb(g).UR.x;
+ yf = (double) GD_drawing(g)->size.y / (double) GD_bb(g).UR.y;
+ /* handle case where one or more dimensions is too big */
+ if ((xf < 1.0) || (yf < 1.0)) {
+ if (xf < yf) {
+ yf = yf / xf;
+ xf = 1.0;
+ } else {
+ xf = xf / yf;
+ yf = 1.0;
+ }
+ }
+ } else if (GD_drawing(g)->ratio_kind == R_EXPAND) {
+ if (GD_drawing(g)->size.x <= 0)
+ return;
+ xf = (double) GD_drawing(g)->size.x / (double) GD_bb(g).UR.x;
+ yf = (double) GD_drawing(g)->size.y / (double) GD_bb(g).UR.y;
+ if ((xf > 1.0) && (yf > 1.0)) {
+ double scale = MIN(xf, yf);
+ xf = yf = scale;
+ } else
+ return;
+ } else if (GD_drawing(g)->ratio_kind == R_VALUE) {
+ desired = GD_drawing(g)->ratio;
+ actual = ((double) GD_bb(g).UR.y) / ((double) GD_bb(g).UR.x);
+ if (actual < desired) {
+ yf = desired / actual;
+ xf = 1.0;
+ } else {
+ xf = actual / desired;
+ yf = 1.0;
+ }
+ } else
+ return;
+ if (GD_flip(g)) {
+ double t = xf;
+ xf = yf;
+ yf = t;
+ }
+
+ if (Nop > 1) {
+ edge_t *e;
+ for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
+ for (e = agfstout(g, n); e; e = agnxtout(g, e))
+ if (ED_spl(e))
+ scaleEdge(e, xf, yf);
+ }
+ }
+ /* Not relying on neato_nlist here allows us not to have to
+ * allocate it in the root graph and the connected components.
+ */
+ for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
+ ND_pos(n)[0] = ND_pos(n)[0] * xf;
+ ND_pos(n)[1] = ND_pos(n)[1] * yf;
+ }
+ scaleBB(g, xf, yf);
+ }
+}
+
+/* neato_set_aspect:
+ * Sets aspect ratio if necessary; real work done in _neato_set_aspect;
+ * This also copies the internal layout coordinates (ND_pos) to the
+ * external ones (ND_coord_i).
+ */
+void neato_set_aspect(graph_t * g)
+{
+ node_t *n;
+
+ _neato_set_aspect(g);
+ for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
+ ND_coord_i(n).x = POINTS(ND_pos(n)[0]);
+ ND_coord_i(n).y = POINTS(ND_pos(n)[1]);
+ }
+}
+#else
+/*****CGRAPH BLOCK*/
static void makePortLabels(edge_t * e)
{
if (ED_head_label(e) && !ED_head_label(e)->set) {
}
}
+#endif //end of graph libpicking