/* $Id: routespl.c,v 1.32 2011/02/07 16:00:40 erg Exp $ $Revision: 1.32 $ */
/* vim:set shiftwidth=4 ts=8: */

/*************************************************************************
 * Copyright (c) 2011 AT&T Intellectual Property 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors: See CVS logs. Details at http://www.graphviz.org/
 *************************************************************************/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "render.h"
#include "pathplan.h"

#ifdef UNUSED
static box *bs = NULL;
static int bn;
static int maxbn = 0;
#define BINC 300
#endif

#define PINC 300

#ifdef NOTNOW
static edge_t *origedge;
#endif

static int nedges, nboxes; /* total no. of edges and boxes used in routing */

static int routeinit;
/* static data used across multiple edges */
static pointf *ps;             /* final spline points */
static int maxpn;             /* size of ps[] */
static Ppoint_t *polypoints;  /* vertices of polygon defined by boxes */
static int polypointn;        /* size of polypoints[] */
static Pedge_t *edges;        /* polygon edges passed to Proutespline */
static int edgen;             /* size of edges[] */

static void checkpath(int, boxf*, path*);
static void mkspacep(int size);
static void printpath(path * pp);
#ifdef DEBUG
static void printboxes(int boxn, boxf* boxes)
{
    pointf ll, ur;
    int bi;
    char buf[BUFSIZ];
    int newcnt = Show_cnt + boxn;

    Show_boxes = ALLOC(newcnt+2,Show_boxes,char*);
    for (bi = 0; bi < boxn; bi++) {
	ll = boxes[bi].LL, ur = boxes[bi].UR;
	sprintf(buf, "%d %d %d %d pathbox", (int)ll.x, (int)ll.y, (int)ur.x, (int)ur.y);
	Show_boxes[bi+1+Show_cnt] = strdup (buf);
    }
    Show_cnt = newcnt;
    Show_boxes[Show_cnt+1] = NULL;
}

static void psprintpolypts(Ppoint_t * p, int sz)
{
    int i;

    fprintf(stderr, "%%!\n");
    fprintf(stderr, "%% constraint poly\n");
    fprintf(stderr, "newpath\n");
    for (i = 0; i < sz; i++)
	fprintf(stderr, "%f %f %s\n", p[i].x, p[i].y,
		(i == 0 ? "moveto" : "lineto"));
    fprintf(stderr, "closepath stroke\n");
}
static void psprintpoint(point p)
{
    fprintf(stderr, "gsave\n");
    fprintf(stderr,
	    "newpath %d %d moveto %d %d 2 0 360 arc closepath fill stroke\n",
	    p.x, p.y, p.x, p.y);
    fprintf(stderr, "/Times-Roman findfont 4 scalefont setfont\n");
    fprintf(stderr, "%d %d moveto (\\(%d,%d\\)) show\n", p.x + 5, p.y + 5,
	    p.x, p.y);
    fprintf(stderr, "grestore\n");
}
static void psprintpointf(pointf p)
{
    fprintf(stderr, "gsave\n");
    fprintf(stderr,
	    "newpath %.5g %.5g moveto %.5g %.5g 2 0 360 arc closepath fill stroke\n",
	    p.x, p.y, p.x, p.y);
    fprintf(stderr, "/Times-Roman findfont 4 scalefont setfont\n");
    fprintf(stderr, "%.5g %.5g moveto (\\(%.5g,%.5g\\)) show\n", p.x + 5, p.y + 5,
	    p.x, p.y);
    fprintf(stderr, "grestore\n");
}

static void psprintspline(Ppolyline_t spl)
{
    char buf[BUFSIZ];
    int newcnt = Show_cnt + spl.pn + 4;
    int li, i;

    Show_boxes = ALLOC(newcnt+2,Show_boxes,char*);
    li = Show_cnt+1;
    Show_boxes[li++] = strdup ("%%!");
    Show_boxes[li++] = strdup ("%% spline");
    Show_boxes[li++] = strdup ("gsave 1 0 0 setrgbcolor newpath");
    for (i = 0; i < spl.pn; i++) {
	sprintf(buf, "%f %f %s", spl.ps[i].x, spl.ps[i].y,
	  (i == 0) ?  "moveto" : ((i % 3 == 0) ? "curveto" : ""));
	Show_boxes[li++] = strdup (buf);
    }
    Show_boxes[li++] = strdup ("stroke grestore");
    Show_cnt = newcnt;
    Show_boxes[Show_cnt+1] = NULL;
}

static void psprintline(Ppolyline_t pl)
{
    char buf[BUFSIZ];
    int newcnt = Show_cnt + pl.pn + 4;
    int i, li;

    Show_boxes = ALLOC(newcnt+2,Show_boxes,char*);
    li = Show_cnt+1;
    Show_boxes[li++] = strdup ("%%!");
    Show_boxes[li++] = strdup ("%% line");
    Show_boxes[li++] = strdup ("gsave 0 0 1 setrgbcolor newpath");
    for (i = 0; i < pl.pn; i++) {
	sprintf(buf, "%f %f %s", pl.ps[i].x, pl.ps[i].y,
		(i == 0 ? "moveto" : "lineto"));
	Show_boxes[li++] = strdup (buf);
    }
    Show_boxes[li++] = strdup ("stroke grestore");
    Show_cnt = newcnt;
    Show_boxes[Show_cnt+1] = NULL;
}

static void psprintpoly(Ppoly_t p)
{
    char buf[BUFSIZ];
    int newcnt = Show_cnt + p.pn + 3;
    point tl, hd;
    int bi, li;
    char*  pfx;

    Show_boxes = ALLOC(newcnt+2,Show_boxes,char*);
    li = Show_cnt+1;
    Show_boxes[li++] = strdup ("%% poly list");
    Show_boxes[li++] = strdup ("gsave 0 1 0 setrgbcolor");
    for (bi = 0; bi < p.pn; bi++) {
	tl.x = (int)p.ps[bi].x;
	tl.y = (int)p.ps[bi].y;
	hd.x = (int)p.ps[(bi+1) % p.pn].x;
	hd.y = (int)p.ps[(bi+1) % p.pn].y;
	if ((tl.x == hd.x) && (tl.y == hd.y)) pfx = "%%";
	else pfx ="";
	sprintf(buf, "%s%d %d %d %d makevec", pfx, tl.x, tl.y, hd.x, hd.y);
	Show_boxes[li++] = strdup (buf);
    }
    Show_boxes[li++] = strdup ("grestore");

    Show_cnt = newcnt;
    Show_boxes[Show_cnt+1] = NULL;
}

static void psprintboxes(int boxn, boxf* boxes)
{
    char buf[BUFSIZ];
    int newcnt = Show_cnt + 5*boxn + 3;
    pointf ll, ur;
    int bi, li;

    Show_boxes = ALLOC(newcnt+2,Show_boxes,char*);
    li = Show_cnt+1;
    Show_boxes[li++] = strdup ("%% box list");
    Show_boxes[li++] = strdup ("gsave 0 1 0 setrgbcolor");
    for (bi = 0; bi < boxn; bi++) {
	ll = boxes[bi].LL, ur = boxes[bi].UR;
	sprintf(buf, "newpath\n%d %d moveto", (int)ll.x, (int)ll.y);
	Show_boxes[li++] = strdup (buf);
	sprintf(buf, "%d %d lineto", (int)ll.x, (int)ur.y);
	Show_boxes[li++] = strdup (buf);
	sprintf(buf, "%d %d lineto", (int)ur.x, (int)ur.y);
	Show_boxes[li++] = strdup (buf);
	sprintf(buf, "%d %d lineto", (int)ur.x, (int)ll.y);
	Show_boxes[li++] = strdup (buf);
	Show_boxes[li++] = strdup ("closepath stroke");
    }
    Show_boxes[li++] = strdup ("grestore");

    Show_cnt = newcnt;
    Show_boxes[Show_cnt+1] = NULL;
}

static void psprintinit (int begin)
{
    int newcnt = Show_cnt + 1;

    Show_boxes = ALLOC(newcnt+2,Show_boxes,char*);
    if (begin)
	Show_boxes[1+Show_cnt] = strdup ("dbgstart");
    else
	Show_boxes[1+Show_cnt] = strdup ("grestore");
    Show_cnt = newcnt;
    Show_boxes[Show_cnt+1] = NULL;
}

static int debugleveln(edge_t* realedge, int i)
{
    return (GD_showboxes(realedge->head->graph) == i ||
	    GD_showboxes(realedge->tail->graph) == i ||
	    ED_showboxes(realedge) == i ||
	    ND_showboxes(realedge->head) == i ||
	    ND_showboxes(realedge->tail) == i);
}
#endif  /* DEBUG */



/* 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) == -1)
        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) == -1)
            return NULL;
    }

    mkspacep(spl.pn);
    for (i = 0; i < spl.pn; i++) {
        ps[i] = spl.ps[i];
    }
    *n_spl_pts = spl.pn;
    return ps;
}

/* routesplinesinit:
 * Data initialized once until matching call to routeplineterm
 * Allows recursive calls to dot
 */
void
routesplinesinit()
{
    if (++routeinit > 1) return;
    if (!(ps = N_GNEW(PINC, pointf))) {
	agerr(AGERR, "cannot allocate ps\n");
	abort();
    }
    maxpn = PINC;
#ifdef DEBUG
    if (Show_boxes) {
	int i;
        for (i = 0; Show_boxes[i]; i++)
	    free (Show_boxes[i]);
	free (Show_boxes);
	Show_boxes = NULL;
	Show_cnt = 0;
    }
#endif
    nedges = 0;
    nboxes = 0;
    if (Verbose)
	start_timer();
}

void routesplinesterm()
{
    if (--routeinit > 0) return;
    free(ps);
#ifdef UNUSED
    free(bs), bs = NULL /*, maxbn = bn = 0 */ ;
#endif
    if (Verbose)
	fprintf(stderr,
		"routesplines: %d edges, %d boxes %.2f sec\n",
		nedges, nboxes, elapsed_sec());
}

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;
}

pointf *routesplines(path * pp, int *npoints)
{
    return _routesplines (pp, npoints, 0);
}

pointf *routepolylines(path * pp, int *npoints)
{
    return _routesplines (pp, npoints, 1);
}

static int overlap(int i0, int i1, int j0, int j1)
{
    /* i'll bet there's an elegant way to do this */
    if (i1 <= j0)
	return 0;
    if (i0 >= j1)
	return 0;
    if ((j0 <= i0) && (i0 <= j1))
	return (j1 - i0);
    if ((j0 <= i1) && (i1 <= j1))
	return (i1 - j0);
    return MIN(i1 - i0, j1 - j0);
}


/*
 * repairs minor errors in the boxpath, such as boxes not joining
 * or slightly intersecting.  it's sort of the equivalent of the
 * audit process in the 5E control program - if you've given up on
 * fixing all the bugs, at least try to engineer around them!
 * in postmodern CS, we could call this "self-healing code."
 */
static void checkpath(int boxn, boxf* boxes, path* thepath)
{
    boxf *ba, *bb;
    int bi, i, errs, l, r, d, u;
    int xoverlap, yoverlap;

#ifndef DONTFIXPATH
    /* remove degenerate boxes. */
    i = 0;
    for (bi = 0; bi < boxn; bi++) {
	if (ABS(boxes[bi].LL.y - boxes[bi].UR.y) < .01)
	    continue;
	if (ABS(boxes[bi].LL.x - boxes[bi].UR.x) < .01)
	    continue;
	if (i != bi)
	    boxes[i] = boxes[bi];
	i++;
    }
    boxn = i;
#endif				/* DONTFIXPATH */

    ba = &boxes[0];
    if (ba->LL.x > ba->UR.x || ba->LL.y > ba->UR.y) {
	agerr(AGERR, "in checkpath, box 0 has LL coord > UR coord\n");
	printpath(thepath);
	abort();
    }
    for (bi = 0; bi < boxn - 1; bi++) {
	ba = &boxes[bi], bb = &boxes[bi + 1];
	if (bb->LL.x > bb->UR.x || bb->LL.y > bb->UR.y) {
	    agerr(AGERR, "in checkpath, box %d has LL coord > UR coord\n",
		  bi + 1);
	    printpath(thepath);
	    abort();
	}
	l = (ba->UR.x < bb->LL.x) ? 1 : 0;
	r = (ba->LL.x > bb->UR.x) ? 1 : 0;
	d = (ba->UR.y < bb->LL.y) ? 1 : 0;
	u = (ba->LL.y > bb->UR.y) ? 1 : 0;
	errs = l + r + d + u;
	if (errs > 0 && Verbose) {
	    fprintf(stderr, "in checkpath, boxes %d and %d don't touch\n",
		    bi, bi + 1);
	    printpath(thepath);
	}
#ifndef DONTFIXPATH
	if (errs > 0) {
	    int xy;

	    if (l == 1)
		xy = ba->UR.x, ba->UR.x = bb->LL.x, bb->LL.x = xy, l = 0;
	    else if (r == 1)
		xy = ba->LL.x, ba->LL.x = bb->UR.x, bb->UR.x = xy, r = 0;
	    else if (d == 1)
		xy = ba->UR.y, ba->UR.y = bb->LL.y, bb->LL.y = xy, d = 0;
	    else if (u == 1)
		xy = ba->LL.y, ba->LL.y = bb->UR.y, bb->UR.y = xy, u = 0;
	    for (i = 0; i < errs - 1; i++) {
		if (l == 1)
		    xy = (ba->UR.x + bb->LL.x) / 2.0 + 0.5, ba->UR.x =
			bb->LL.x = xy, l = 0;
		else if (r == 1)
		    xy = (ba->LL.x + bb->UR.x) / 2.0 + 0.5, ba->LL.x =
			bb->UR.x = xy, r = 0;
		else if (d == 1)
		    xy = (ba->UR.y + bb->LL.y) / 2.0 + 0.5, ba->UR.y =
			bb->LL.y = xy, d = 0;
		else if (u == 1)
		    xy = (ba->LL.y + bb->UR.y) / 2.0 + 0.5, ba->LL.y =
			bb->UR.y = xy, u = 0;
	    }
	}
#else
	abort();
#endif
#ifndef DONTFIXPATH
	/* check for overlapping boxes */
	xoverlap = overlap(ba->LL.x, ba->UR.x, bb->LL.x, bb->UR.x);
	yoverlap = overlap(ba->LL.y, ba->UR.y, bb->LL.y, bb->UR.y);
	if (xoverlap && yoverlap) {
	    if (xoverlap < yoverlap) {
		if (ba->UR.x - ba->LL.x > bb->UR.x - bb->LL.x) {
		    /* take space from ba */
		    if (ba->UR.x < bb->UR.x)
			ba->UR.x = bb->LL.x;
		    else
			ba->LL.x = bb->UR.x;
		} else {
		    /* take space from bb */
		    if (ba->UR.x < bb->UR.x)
			bb->LL.x = ba->UR.x;
		    else
			bb->UR.x = ba->LL.x;
		}
	    } else {		/* symmetric for y coords */
		if (ba->UR.y - ba->LL.y > bb->UR.y - bb->LL.y) {
		    /* take space from ba */
		    if (ba->UR.y < bb->UR.y)
			ba->UR.y = bb->LL.y;
		    else
			ba->LL.y = bb->UR.y;
		} else {
		    /* take space from bb */
		    if (ba->UR.y < bb->UR.y)
			bb->LL.y = ba->UR.y;
		    else
			bb->UR.y = ba->LL.y;
		}
	    }
	}
    }
#endif				/* DONTFIXPATH */

    if (thepath->start.p.x < boxes[0].LL.x
	|| thepath->start.p.x > boxes[0].UR.x
	|| thepath->start.p.y < boxes[0].LL.y
	|| thepath->start.p.y > boxes[0].UR.y) {
	if (Verbose) {
	    fprintf(stderr, "in checkpath, start port not in first box\n");
	    printpath(thepath);
	}
#ifndef DONTFIXPATH
	if (thepath->start.p.x < boxes[0].LL.x)
	    thepath->start.p.x = boxes[0].LL.x;
	if (thepath->start.p.x > boxes[0].UR.x)
	    thepath->start.p.x = boxes[0].UR.x;
	if (thepath->start.p.y < boxes[0].LL.y)
	    thepath->start.p.y = boxes[0].LL.y;
	if (thepath->start.p.y > boxes[0].UR.y)
	    thepath->start.p.y = boxes[0].UR.y;
#else
	abort();
#endif
    }
    if (thepath->end.p.x < boxes[boxn - 1].LL.x
	|| thepath->end.p.x > boxes[boxn - 1].UR.x
	|| thepath->end.p.y < boxes[boxn - 1].LL.y
	|| thepath->end.p.y > boxes[boxn - 1].UR.y) {
	if (Verbose) {
	    fprintf(stderr, "in checkpath, end port not in last box\n");
	    printpath(thepath);
	}
#ifndef DONTFIXPATH
	if (thepath->end.p.x < boxes[boxn - 1].LL.x)
	    thepath->end.p.x = boxes[boxn - 1].LL.x;
	if (thepath->end.p.x > boxes[boxn - 1].UR.x)
	    thepath->end.p.x = boxes[boxn - 1].UR.x;
	if (thepath->end.p.y < boxes[boxn - 1].LL.y)
	    thepath->end.p.y = boxes[boxn - 1].LL.y;
	if (thepath->end.p.y > boxes[boxn - 1].UR.y)
	    thepath->end.p.y = boxes[boxn - 1].UR.y;
#else
	abort();
#endif
    }
}

static void mkspacep(int size)
{
    if (size > maxpn) {
	int newmax = maxpn + (size / PINC + 1) * PINC;
	ps = RALLOC(newmax, ps, pointf);
	if (!ps) {
	    agerr(AGERR, "cannot re-allocate ps\n");
	    abort();
	}
	maxpn = newmax;
    }
}

static void printpath(path * pp)
{
    int bi;

#ifdef NOTNOW
    fprintf(stderr, "edge %d from %s to %s\n", nedges,
	    realedge->tail->name, realedge->head->name);
    if (ED_count(origedge) > 1)
	fprintf(stderr, "    (it's part of a concentrator edge)\n");
#endif
    fprintf(stderr, "%d boxes:\n", pp->nbox);
    for (bi = 0; bi < pp->nbox; bi++)
	fprintf(stderr, "%d (%.5g, %.5g), (%.5g, %.5g)\n", bi,
		pp->boxes[bi].LL.x, pp->boxes[bi].LL.y,
	       	pp->boxes[bi].UR.x, pp->boxes[bi].UR.y);
    fprintf(stderr, "start port: (%.5g, %.5g), tangent angle: %.5g, %s\n",
	    pp->start.p.x, pp->start.p.y, pp->start.theta,
	    pp->start.constrained ? "constrained" : "not constrained");
    fprintf(stderr, "end port: (%.5g, %.5g), tangent angle: %.5g, %s\n",
	    pp->end.p.x, pp->end.p.y, pp->end.theta,
	    pp->end.constrained ? "constrained" : "not constrained");
}