/*
 * Electric(tm) VLSI Design System
 *
 * File: usrarc.c
 * User interface aid: simple arc routing
 * Written by: Steven M. Rubin, Static Free Software
 *
 * Copyright (c) 2000 Static Free Software.
 *
 * Electric(tm) is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Electric(tm) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Electric(tm); see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, Mass 02111-1307, USA.
 *
 * Static Free Software
 * 4119 Alpine Road
 * Portola Valley, California 94028
 * support@staticfreesoft.com
 */

#include "global.h"
#include "egraphics.h"
#include "usr.h"
#include "efunction.h"

/* prototypes for local routines */
TECHNOLOGY *us_gettech(NODEINST*, PORTPROTO*);
ARCINST *us_makeconnection(NODEINST*, PORTPROTO*, NODEINST*, PORTPROTO*, ARCPROTO*,
	INTBIG, INTBIG, ARCINST**, ARCINST**, INTSML, INTSML);
ARCINST *us_directarcinst(NODEINST*, PORTPROTO*, NODEINST*, PORTPROTO*, ARCPROTO*, INTBIG,
	INTBIG, INTBIG, INTSML);
ARCINST *us_onebend(NODEINST*, PORTPROTO*, NODEINST*, PORTPROTO*, ARCPROTO*, INTBIG, INTBIG,
	INTBIG, ARCINST**);
ARCINST *us_twobend(NODEINST*, PORTPROTO*, NODEINST*, PORTPROTO*, ARCPROTO*, INTBIG, INTBIG,
	INTBIG, ARCINST**, ARCINST**);
INTBIG us_widestarcinst(ARCPROTO*, NODEINST*, PORTPROTO*);
INTBIG us_portdistance(NODEINST*, PORTPROTO*, INTBIG, INTBIG, INTBIG*, INTBIG*, INTBIG, INTSML);
INTSML us_fitportangle(NODEINST*, PORTPROTO*, INTSML, INTSML);
INTBIG us_figurearcwidth(ARCPROTO *typ, INTBIG wid, NODEINST *ni1, PORTPROTO *pp1,
	INTBIG ox1, INTBIG oy1, NODEINST *ni2, PORTPROTO *pp2, INTBIG ox2, INTBIG oy2);

/*
 * routine to run an arcinst from "fromgeom" to "togeom".  The prefered
 * ports to use on these objects is "frompp" and "topp" (presuming that
 * "fromgeom" and "togeom" are nodeinsts and "frompp" and "topp" are
 * not NOPORTPROTO).  The prefered layer for the arc is "typ" but
 * this may be overridden if other layers are possible and the prefered
 * layer isn't.  The prefered location for the arcinst is closest to
 * (prefx, prefy) if there is a choice.  If "nozigzag" is nonzero, do not
 * make 3-arc connections (only 1 or 2).  If the arcinst is run, the
 * routine returns its address (if two or three arcs are created, their
 * addresses are returned in "alt1" and "alt2").  Otherwise, it issues an
 * error and returns NOARCINST.  The required angle increment for the
 * arcs is "ang" tenth-degrees (900 for manhattan geometry).
 */
ARCINST *aconnect(GEOM *fromgeom, PORTPROTO *frompp, GEOM *togeom, PORTPROTO *topp,
	ARCPROTO *typ, INTBIG prefx, INTBIG prefy, ARCINST **alt1, ARCINST **alt2, INTSML ang,
	INTSML nozigzag)
{
	REGISTER NODEINST *fromnodeinst, *tonodeinst, *ni;
	REGISTER ARCINST *ai, *newai;
	REGISTER PORTPROTO *fpt, *tpt;
	REGISTER ARCPROTO *ap;
	REGISTER TECHNOLOGY *tech, *tech1, *tech2;
	REGISTER INTSML j, t, ans;
	REGISTER INTBIG i, bestdist, bestx, besty;
	INTBIG x, y;
	char *result[2];
	extern COMCOMP us_noyesp;

	/* error check */
	if (fromgeom == togeom && fromgeom->entrytype != OBJNODEINST)
	{
		us_abortcommand(_("Cannot run from arc to itself"));
		return(NOARCINST);
	}

	/* handle special case of an arcinst connecting to a facet */
	if (fromgeom->entrytype == OBJARCINST || togeom->entrytype == OBJARCINST)
	{
		if (fromgeom->entrytype == OBJARCINST) ai = fromgeom->entryaddr.ai; else
			ai = togeom->entryaddr.ai;
		ni = NONODEINST;
		if (fromgeom->entrytype == OBJNODEINST) ni = fromgeom->entryaddr.ni;
		if (togeom->entrytype == OBJNODEINST) ni = togeom->entryaddr.ni;

		if (ni != NONODEINST && ni->proto->primindex == 0)
		{
			/* found facet connected to arcinst: search for closest portinst */
			bestdist = HUGEINT;
			for(fpt = ni->proto->firstportproto; fpt != NOPORTPROTO; fpt = fpt->nextportproto)
			{
				/* see if the arcinst can connect at this portinst */
				for(i=0; fpt->connects[i] != NOARCPROTO; i++)
					if (fpt->connects[i] == ai->proto) break;
				if (fpt->connects[i] == NOARCPROTO) continue;

				/* compute position for closest applicable portinst */
				i = us_portdistance(ni, fpt, prefx, prefy, &x, &y,
					defaultarcwidth(typ) - arcprotowidthoffset(typ), 0);
				if (i > bestdist) continue;
				bestdist = i;   bestx = x;   besty = y;
			}

			/* adjust prefered position to closest port (manhattan only!!!) */
			if (bestdist < HUGEINT)
			{
				if (ai->end[0].xpos == ai->end[1].xpos) prefy = besty;
				if (ai->end[0].ypos == ai->end[1].ypos) prefx = bestx;
			}
		}
	}

	/* get actual nodes for each end of connection */
	fromnodeinst = us_getnodeonarcinst(&fromgeom, &frompp, togeom, topp, prefx, prefy);
	if (fromnodeinst == NONODEINST)
	{
		us_abortcommand(_("Cannot create splitting pin"));
		return(NOARCINST);
	}

	tonodeinst = us_getnodeonarcinst(&togeom, &topp, fromgeom, frompp, prefx, prefy);
	if (tonodeinst == NONODEINST)
	{
		us_abortcommand(_("Cannot create splitting pin"));
		return(NOARCINST);
	}

	/* default to single port on one-port nodeinsts */
	if (frompp == NOPORTPROTO)
		if ((fpt = fromnodeinst->proto->firstportproto) != NOPORTPROTO)
			if (fpt->nextportproto == NOPORTPROTO) frompp = fpt;
	if (topp == NOPORTPROTO)
		if ((tpt = tonodeinst->proto->firstportproto) != NOPORTPROTO)
			if (tpt->nextportproto == NOPORTPROTO) topp = tpt;

	/* sillyness check */
	if (fromnodeinst == tonodeinst && frompp == topp)
	{
		(void)initinfstr();
		(void)formatinfstr(_("Are you sure you want to run an arc from node %s to itself?"),
			describenodeinst(fromnodeinst));
		ans = ttygetparam(returninfstr(), &us_noyesp, 1, result);
		if (ans <= 0 || (result[0][0] != 'y' && result[0][0] != 'Y')) return(NOARCINST);
	}

	/* reset all arcproto connection bits */
	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
		for(ap = tech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
			ap->userbits &= ~CANCONNECT;

	/* set bits in arc prototypes that can make the connection */
	t = 0;
	for(fpt = fromnodeinst->proto->firstportproto; fpt !=NOPORTPROTO; fpt = fpt->nextportproto)
	{
		if (frompp != NOPORTPROTO && frompp != fpt) continue;
		for (tpt = tonodeinst->proto->firstportproto; tpt != NOPORTPROTO; tpt = tpt->nextportproto)
		{
			if (topp != NOPORTPROTO && topp != tpt) continue;

			/* set those bits common to both "fpt" and "tpt" */
			for(i=0; fpt->connects[i] != NOARCPROTO; i++)
			{
				for(j=0; tpt->connects[j] != NOARCPROTO; j++)
				{
					if (tpt->connects[j] != fpt->connects[i]) continue;
					fpt->connects[i]->userbits |= CANCONNECT;
					t++;
					break;
				}
			}
		}
	}

	/* if no common ports, don't run the arcinst */
	if (t == 0)
	{
		us_abortcommand(_("Cannot find arc that connects %s to %s"), geomname(fromgeom),
			geomname(togeom));
		return(NOARCINST);
	}

	/* see if the default arc prototype can be used */
	if ((typ->userbits&CANCONNECT) != 0)
	{
		newai = us_makeconnection(fromnodeinst, frompp, tonodeinst, topp, typ, prefx, prefy,
			alt1, alt2, ang, nozigzag);
		if (newai != NOARCINST) return(newai);
	}

	/* default arc prototype cannot be used: try others in this technology */
	for(ap = el_curtech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
	{
		if ((ap->userbits&CANCONNECT) == 0) continue;
		if (ap == typ) continue;
		newai = us_makeconnection(fromnodeinst, frompp, tonodeinst, topp, ap, prefx, prefy,
			alt1, alt2, ang, nozigzag);
		if (newai != NOARCINST) return(newai);
	}

	/* current technology bad: see if this is a cross-technology connect */
	tech1 = us_gettech(fromnodeinst, frompp);
	tech2 = us_gettech(tonodeinst, topp);
	if (tech1 == tech2)
	{
		/* if current technology not that of the two nodes, check it */
		if (tech1 != el_curtech)
			for(ap = tech1->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
		{
			if ((ap->userbits&CANCONNECT) == 0) continue;
			if (ap == typ) continue;
			newai = us_makeconnection(fromnodeinst, frompp, tonodeinst, topp, ap,
				prefx, prefy, alt1, alt2, ang, nozigzag);
			if (newai != NOARCINST) return(newai);
		}
	} else
	{
		/* current technology bad: try other technologies */
		for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
			if (tech != el_curtech)
				for(ap = tech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
		{
			if ((ap->userbits&CANCONNECT) == 0) continue;
			newai = us_makeconnection(fromnodeinst, frompp, tonodeinst, topp, ap,
				prefx, prefy, alt1, alt2, ang, nozigzag);
			if (newai != NOARCINST) return(newai);
		}
	}
	us_abortcommand(_("There is no way to connect these objects"));
	return(NOARCINST);
}

/*
 * routine to return the technology associated with node "ni", port "pp".
 */
TECHNOLOGY *us_gettech(NODEINST *ni, PORTPROTO *pp)
{
	for(;;)
	{
		if (ni->proto->primindex != 0) break;
		if (pp == NOPORTPROTO) break;
		ni = pp->subnodeinst;
		pp = pp->subportproto;
	}
	return(ni->proto->tech);
}

/*
 * routine to try to run an arcinst from nodeinst "fromnodeinst" to nodeinst
 * "tonodeinst" in arcproto "typ".  The default port prototypes to use are
 * in "fromportproto" and "toportproto".  If it can be done, the arcinst is
 * created and the routine returns its address, otherwise it returns
 * NOARCINST.  If two or three arcs are created, their addresses are returned
 * in "alt1" and "alt2".  The preferred arcinst runs closest to (prefx, prefy).
 * The most direct route will be traveled, but an additional nodeinst or two
 * may have to be created.  The angle increment for the arcs is "ang".
 * If "nozigzag" is nonzero, disallow 3-arc connections (only 1 or 2).
 */
ARCINST *us_makeconnection(NODEINST *fromnodeinst, PORTPROTO *fromportproto,
	NODEINST *tonodeinst, PORTPROTO *toportproto, ARCPROTO *typ, INTBIG prefx,
	INTBIG prefy, ARCINST **alt1, ARCINST **alt2, INTSML ang, INTSML nozigzag)
{
	REGISTER PORTPROTO *fpt, *tpt;
	REGISTER INTBIG bestdist, dist, wid, w;
	REGISTER INTSML i;
	REGISTER ARCINST *newai;
	INTBIG x, y, hx, hy, lx, ly;

	/* see if the cursor is near a port on a facet */
	bestdist = HUGEINT;
	if (fromportproto == NOPORTPROTO && fromnodeinst->proto->primindex == 0)
		for(fpt = fromnodeinst->proto->firstportproto; fpt != NOPORTPROTO; fpt = fpt->nextportproto)
	{
		for(i=0; fpt->connects[i] != NOARCPROTO; i++)
			if (typ == fpt->connects[i]) break;
		if (fpt->connects[i] == NOARCPROTO) continue;

		/* portproto may be valid: compute distance to it */
		portposition(fromnodeinst, fpt, &x, &y);
		dist = abs(x-prefx) + abs(y-prefy);
		if (dist > bestdist) continue;
		bestdist = dist;   fromportproto = fpt;
	}
	if (toportproto == NOPORTPROTO && tonodeinst->proto->primindex == 0)
		for(tpt = tonodeinst->proto->firstportproto; tpt != NOPORTPROTO; tpt = tpt->nextportproto)
	{
		for(i=0; tpt->connects[i] != NOARCPROTO; i++)
			if (typ == tpt->connects[i]) break;
		if (tpt->connects[i] == NOARCPROTO) continue;

		/* portproto may be valid: compute distance to it */
		portposition(tonodeinst, tpt, &x, &y);
		dist = abs(x-prefx) + abs(y-prefy);
		if (dist > bestdist) continue;
		bestdist = dist;   toportproto = tpt;
	}
	if (fromportproto == NOPORTPROTO || toportproto == NOPORTPROTO) return(NOARCINST);

	/* determine the width of this arc from others on the nodes */
	wid = defaultarcwidth(typ);
	w = us_widestarcinst(typ, fromnodeinst, fromportproto);
	if (w > wid) wid = w;
	w = us_widestarcinst(typ, tonodeinst, toportproto);
	if (w > wid) wid = w;

	/* now first check for direct connection */
	newai = us_directarcinst(fromnodeinst,fromportproto, tonodeinst, toportproto, typ,
		prefx, prefy, wid, ang);
	if (newai != NOARCINST)
	{
		*alt1 = *alt2 = NOARCINST;
		return(newai);
	}

#if 0
	/* now try a zig-zag if in Manhattan and port angles are right */
	if (ang == 900 && nozigzag == 0 && (fromportproto == NOPORTPROTO ||
		((fromportproto->userbits&PORTARANGE) >> PORTARANGESH) == 180) &&
			(toportproto == NOPORTPROTO ||
				((toportproto->userbits&PORTARANGE) >> PORTARANGESH) == 180))
#else
	/* now try a zig-zag if in Manhattan allowed */
	if (ang == 900 && nozigzag == 0)
#endif
	{
		/*
		 * check location of cursor with respect to fromnode and tonode.
		 * There are nine possibilities.  Each implies a specific routing
		 * request (cases where cursor is lined up horizontally or vertically
		 * with either fromnode or tonode are not included as they default to
		 * L-connections)
		 *       1   2   3
		 *         *             [fromnode]
		 *       4   5   6
		 *             *         [tonode]
		 *       7   8   9
		 */
		portposition(fromnodeinst, fromportproto, &lx, &ly);
		portposition(tonodeinst, toportproto, &hx, &hy);
		if (hx < lx)
		{
			x = lx;   lx = hx;   hx = x;
		}
		if (hy < ly)
		{
			y = ly;   ly = hy;   hy = y;
		}

		/* if cursor location is in 2, 4, 5, 6, or 8, try two-bend connection */
		if ((prefx >= lx && prefx <= hx) || (prefy >= ly && prefy <= hy))
		{
			newai = us_twobend(fromnodeinst, fromportproto, tonodeinst,
				toportproto, typ, prefx, prefy, wid, alt1, alt2);
			if (newai != NOARCINST) return(newai);
		}
	}

	/* next check for a one-bend connection */
	newai = us_onebend(fromnodeinst,fromportproto, tonodeinst,toportproto,
		typ, prefx, prefy, wid, alt1);
	if (newai != NOARCINST)
	{
		*alt2 = NOARCINST;
		return(newai);
	}

	/* finally check for any zig-zag connection */
	newai = us_twobend(fromnodeinst,fromportproto, tonodeinst,toportproto,
		typ, prefx, prefy, wid, alt1, alt2);
	if (newai != NOARCINST) return(newai);

	/* give up */
	return(NOARCINST);
}

/*
 * routine to check for direct connection from nodeinst "fromnodeinst" to
 * nodeinst "tonodeinst" through arcproto "typ" and create an arc that is
 * "wid" wide if possible.  It is prefered that the arcinst be close to
 * (prefx, prefy) and is on portproto "fromppt" (if "fromppt" is not
 * NOPORTPROTO) on the FROM nodeinst and portproto "toppt" (if "toppt" is not
 * NOPORTPROTO) on the TO nodeinst.  If the arcinst is created, the routine
 * returns its address, otherwise it returns NOARCINST.  The angle increment
 * for the arc is "ang" tenth-degrees (900 for manhattan geometry).
 */
ARCINST *us_directarcinst(NODEINST *fromnodeinst, PORTPROTO *fromppt,
	NODEINST *tonodeinst, PORTPROTO *toppt, ARCPROTO *typ,
	INTBIG prefx, INTBIG prefy, INTBIG wid, INTSML ang)
{
	REGISTER PORTPROTO *fpt, *tpt, *fstart, *tstart;
	REGISTER ARCINST *ai;
	PORTPROTO *fromportproto, *toportproto;
	REGISTER INTSML i, j, trange, frange, bad, gotpath;
	INTBIG bestdist, dist, bestpdist, pdist, bestfx,bestfy, besttx,bestty, otheralign, fpx, fpy,
		tpx, tpy, flx, fly, fhx, fhy, tlx, tly, thx, thy, rwid, x, y;
	static POLYGON *fpoly = NOPOLYGON, *tpoly = NOPOLYGON;

	/* get polygons */
	if (fpoly == NOPOLYGON) fpoly = allocstaticpolygon(4, us_aid->cluster);
	if (tpoly == NOPOLYGON) tpoly = allocstaticpolygon(4, us_aid->cluster);

	/* determine true width */
	portposition(tonodeinst, toppt, &tpx, &tpy);
	portposition(fromnodeinst, fromppt, &fpx, &fpy);
	wid = us_figurearcwidth(typ, wid, fromnodeinst, fromppt, tpx, tpy, tonodeinst, toppt, fpx, fpy);
	rwid = wid - arcprotowidthoffset(typ);

	/* assume default prefered positions for port locations */
	tpx = (tonodeinst->highx + tonodeinst->lowx) / 2;
	tpy = (tonodeinst->highy + tonodeinst->lowy) / 2;
	fpx = (fromnodeinst->highx + fromnodeinst->lowx) / 2;
	fpy = (fromnodeinst->highy + fromnodeinst->lowy) / 2;

	/* precompute better positions if ports were specified */
	if (toppt != NOPORTPROTO)
	{
		tpoly->xv[0] = prefx;   tpoly->yv[0] = prefy;   tpoly->count = 1;
		shapeportpoly(tonodeinst, toppt, tpoly, 1);
		reduceportpoly(tpoly, tonodeinst, toppt, rwid);
		tpx = prefx;   tpy = prefy;
		closestpoint(tpoly, &tpx, &tpy);
		tstart = toppt;
	} else tstart = tonodeinst->proto->firstportproto;
	if (fromppt != NOPORTPROTO)
	{
		fpoly->xv[0] = prefx;   fpoly->yv[0] = prefy;   fpoly->count = 1;
		shapeportpoly(fromnodeinst, fromppt, fpoly, 1);
		reduceportpoly(fpoly, fromnodeinst, fromppt, rwid);
		fpx = prefx;   fpy = prefy;
		closestpoint(fpoly, &fpx, &fpy);
		fstart = fromppt;
	} else fstart = fromnodeinst->proto->firstportproto;

	/* check again to make sure the ports align */
	if (toppt != NOPORTPROTO)
	{
		tpx = fpx;   tpy = fpy;
		closestpoint(tpoly, &tpx, &tpy);
	}
	if (fromppt != NOPORTPROTO)
	{
		fpx = tpx;   fpy = tpy;
		closestpoint(fpoly, &fpx, &fpy);
	}

	/* search every possible port on the "from" node */
	bestdist = bestpdist = HUGEINT;
	for(fpt = fstart; fpt != NOPORTPROTO; fpt = fpt->nextportproto)
	{
		if (fromppt != NOPORTPROTO && fromppt != fpt) break;

		/* see if the port has an opening for this type of arcinst */
		for(i=0; fpt->connects[i] != NOARCPROTO; i++)
			if (typ == fpt->connects[i]) break;
		if (fpt->connects[i] == NOARCPROTO) continue;

		/* potential port: get information about its position if not already known */
		if (fromppt == NOPORTPROTO)
		{
			fpoly->xv[0] = prefx;   fpoly->yv[0] = prefy;   fpoly->count = 1;
			shapeportpoly(fromnodeinst, fpt, fpoly, 1);

			/* handle arc width offset from node edge */
			reduceportpoly(fpoly, fromnodeinst, fpt, rwid);

			/* find single closest point */
			fpx = tpx;   fpy = tpy;
			closestpoint(fpoly, &fpx, &fpy);
		}

		/* correlate with every possible port on the "to" node */
		for(tpt = tstart; tpt != NOPORTPROTO; tpt = tpt->nextportproto)
		{
			if (toppt != NOPORTPROTO && toppt != tpt) break;

			/* silly to run from port to itself when ports are unspecified */
			if (fromnodeinst == tonodeinst && fpt == tpt &&
				(fromppt == NOPORTPROTO || toppt == NOPORTPROTO)) continue;

			/* see if the port has an opening for this type of arcinst */
			for(i=0; tpt->connects[i] != NOARCPROTO; i++)
				if (typ == tpt->connects[i]) break;
			if (tpt->connects[i] == NOARCPROTO) continue;

			/* get the shape of the "to" port if not already done */
			if (toppt == NOPORTPROTO)
			{
				tpoly->xv[0] = prefx;   tpoly->yv[0] = prefy;   tpoly->count = 1;
				shapeportpoly(tonodeinst, tpt, tpoly, 1);

				/* handle arc width offset from node edge */
				reduceportpoly(tpoly, tonodeinst, tpt, rwid);

				/* find single closest point */
				tpx = fpx;   tpy = fpy;
				closestpoint(tpoly, &tpx, &tpy);
			}

			/* check directionality of ports */
			trange = (INTSML)(((tpt->userbits&PORTARANGE) >> PORTARANGESH) * 10);
			frange = (INTSML)(((fpt->userbits&PORTARANGE) >> PORTARANGESH) * 10);

			/* ignore range calculations for serpentine transistors */
			if ((tonodeinst->proto->userbits&HOLDSTRACE) != 0 &&
				getvalkey((INTBIG)tonodeinst, VNODEINST, VINTEGER|VISARRAY, el_trace) != NOVARIABLE)
					trange = 1800;
			if ((fromnodeinst->proto->userbits&HOLDSTRACE) != 0 &&
				getvalkey((INTBIG)fromnodeinst, VNODEINST, VINTEGER|VISARRAY, el_trace) != NOVARIABLE)
					frange = 1800;

			/* make sure ranges are acceptable */
			if (trange != 1800 || frange != 1800)
			{
				/* determine angle between port centers */
				bad = 0;
				if (fpx != tpx || fpy != tpy)
				{
					/* see if the angle is permitted */
					i = figureangle(fpx, fpy, tpx, tpy);
					if (us_fitportangle(fromnodeinst, fpt, i, frange) != 0) bad = 1; else
						if (us_fitportangle(tonodeinst, tpt, (INTSML)(i+1800), trange) != 0)
							bad = 1;
				}

				/* special case for ports that overlap */
				if (bad != 0)
				{
					j = figureangle((fromnodeinst->lowx+fromnodeinst->highx)/2,
						(fromnodeinst->lowy+fromnodeinst->highy)/2,
							(tonodeinst->lowx+tonodeinst->highx)/2,
								(tonodeinst->lowy+tonodeinst->highy)/2);
					if ((j+1800)%3600 == i &&
						us_fitportangle(fromnodeinst, fpt, j, frange) == 0 &&
							us_fitportangle(tonodeinst, tpt, (INTSML)(j+1800), trange) == 0)
								bad = 0;
				}
				if (bad != 0) continue;
			}

			/* see if an arc can connect at this angle */
			if (ang == 0)
			{
				/* no angle restrictions: simply use the chosen locations */
				gotpath = 1;
			} else
			{
				/* if manhattan is possible, test it first */
				gotpath = 0;
				if (900 / ang * ang == 900)
				{
					/* manhattan angle restriction: check directly */
					if (tpx == fpx || tpy == fpy) gotpath = 1;
				}
				if (gotpath == 0 && ang != 900)
				{
					flx = fhx = fpx;   fly = fhy = fpy;
					tlx = thx = tpx;   tly = thy = tpy;

					/* arbitrary angle restrictions: try all angle possibilities */
					for(i=0; i<3600; i += ang)
						if (arcconnects(i, flx,fhx,fly,fhy, tlx,thx,tly,thy, &fpx,&fpy, &tpx,&tpy) != 0)
					{
						gotpath = 1;
						break;
					}
				}
			}
			if (gotpath == 0) continue;

			/* for manhattan arcs, adjust if edge alignment requested */
			if (fpx == tpx)
			{
				/* arcinst runs vertically */
				getbbox(fpoly, &flx, &fhx, &fly, &fhy);
				getbbox(tpoly, &tlx, &thx, &tly, &thy);
				if (us_edgealignment != 0 && flx != fhx && tlx != thx)
				{
					/* make the arc edge align */
					x = us_alignvalue(fpx - rwid/2, us_edgealignment, &otheralign) + rwid/2;
					otheralign += rwid/2;
					if (x <= mini(fhx,thx) && x >= maxi(flx,tlx)) tpx = fpx = x; else
						if (otheralign <= mini(fhx,thx) && otheralign >= maxi(flx,tlx))
							fpx = tpx = otheralign;

					/* try to align the ends */
					y = us_alignvalue(tpy+rwid/2, us_edgealignment, &otheralign) - rwid/2;
					otheralign -= rwid/2;
					if (isinside(tpx, y, tpoly) != 0) tpy = y; else
						if (isinside(tpx, otheralign, tpoly) != 0) tpy = otheralign;
					y = us_alignvalue(fpy+rwid/2, us_edgealignment, &otheralign) - rwid/2;
					otheralign -= rwid/2;
					if (isinside(fpx, y, fpoly) != 0) fpy = y; else
						if (isinside(fpx, otheralign, fpoly) != 0) fpy = otheralign;
				}
			} else if (fpy == tpy)
			{
				/* arcinst runs horizontally */
				getbbox(fpoly, &flx, &fhx, &fly, &fhy);
				getbbox(tpoly, &tlx, &thx, &tly, &thy);
				if (us_edgealignment != 0 && fly != fhy && tly != thy)
				{
					/* make the arc edge align */
					y = us_alignvalue(tpy - rwid/2, us_edgealignment, &otheralign) + rwid/2;
					otheralign += rwid/2;
					if (y <= mini(fhy,thy) && y >= maxi(fly,tly)) tpy = fpy = y; else
						if (otheralign <= mini(fhy,thy) && otheralign >= maxi(fly,tly))
							tpy = fpy = otheralign;

					/* try to align the ends */
					x = us_alignvalue(tpx+rwid/2, us_edgealignment, &otheralign) - rwid/2;
					otheralign -= rwid/2;
					if (isinside(x, tpy, tpoly) != 0) tpx = x; else
						if (isinside(otheralign, tpy, tpoly) != 0) tpx = otheralign;
					x = us_alignvalue(fpx+rwid/2, us_edgealignment, &otheralign) - rwid/2;
					otheralign -= rwid/2;
					if (isinside(fpx, x, fpoly) != 0) fpx = x; else
						if (isinside(otheralign, fpy, fpoly) != 0) fpx = otheralign;
				}
			}

			/* if this path is longer than another, forget it */
			dist = abs(fpx-tpx) + abs(fpy-tpy);
			if (dist > bestdist) continue;

			/* if this path is same as another check prefered position */
			pdist = (fpx==tpx) ? abs(prefx-fpx) : abs(prefy-fpy);
			if (dist == bestdist && pdist > bestpdist) continue;

			/* this is best: remember it */
			bestdist = dist;       bestpdist = pdist;
			fromportproto = fpt;   toportproto = tpt;
			bestfx = fpx;          bestfy = fpy;
			besttx = tpx;          bestty = tpy;
		}
	}

	if (bestdist < HUGEINT)
	{
		ai = us_runarcinst(fromnodeinst, fromportproto, bestfx, bestfy, 
			tonodeinst, toportproto, besttx, bestty, typ, wid);
		if (ai != NOARCINST) ttyputmsg(_("Created 1 %s arc"), typ->protoname);
		return(ai);
	}

	/* give up */
	return(NOARCINST);
}

/*
 * routine to check for a one-bend connection running from nodeinst
 * "fromnodeinst" to nodeinst "tonodeinst" on arcproto "typ" and create two
 * arcs that are "wid" wide if possible.  It is prefered that the bend pass
 * close to (prefx, prefy) and is on portproto "fromppt" (if "fromppt" is not
 * NOPORTPROTO) on the FROM nodeinst and on portproto "toppt" (if "toppt" is
 * not NOPORTPROTO) on the TO nodeinst.  If the arcinst is created, the
 * routine returns its address and the second arc's address is returned in
 * "alt".  If no arc can be created, the routine returns NOARCINST.
 */
ARCINST *us_onebend(NODEINST *fromnodeinst, PORTPROTO *fromppt, NODEINST *tonodeinst,
	PORTPROTO *toppt, ARCPROTO *typ, INTBIG prefx, INTBIG prefy, INTBIG wid, ARCINST **alt)
{
	REGISTER NODEINST *ni;
	REGISTER NODEPROTO *ft;
	REGISTER PORTPROTO *fpt, *tpt;
	PORTPROTO *fromportproto, *toportproto;
	REGISTER INTSML i, bad, frange, trange;
	REGISTER INTBIG lx, hx, ly, hy, rwid;
	REGISTER ARCINST *ai1, *ai2;
	static POLYGON *poly = NOPOLYGON;
	INTBIG fx, fy, tx, ty, xi, yi, bestxi, bestyi, bestdist, dist,
		bestfx, bestfy, besttx, bestty, altxi, altyi, otheralign, pxs, pys;
	INTSML found;

	/* get polygon */
	if (poly == NOPOLYGON) poly = allocstaticpolygon(4, us_aid->cluster);

	/* determine true width */
	wid = us_figurearcwidth(typ, wid, fromnodeinst, fromppt, prefx, prefy, tonodeinst, toppt, prefx, prefy);
	rwid = wid - arcprotowidthoffset(typ);

	found = 0;    bestdist = HUGEINT;
	for(fpt = fromnodeinst->proto->firstportproto; fpt != NOPORTPROTO; fpt = fpt->nextportproto)
	{
		if (fromppt != NOPORTPROTO && fromppt != fpt) continue;

		/* see if the port has an opening for this type of arcinst */
		for(i=0; fpt->connects[i] != NOARCPROTO; i++)
			if (typ == fpt->connects[i]) break;
		if (fpt->connects[i] == NOARCPROTO) continue;

		/* potential port: get information about its position */
		(void)us_portdistance(fromnodeinst, fpt, prefx, prefy, &fx, &fy, rwid, 1);

		for(tpt = tonodeinst->proto->firstportproto; tpt !=NOPORTPROTO; tpt = tpt->nextportproto)
		{
			if (toppt != NOPORTPROTO && toppt != tpt) continue;

			/* should not run arcinst from port to itself */
			if (fromnodeinst == tonodeinst && fpt == tpt) continue;

			/* see if the port has an opening for this type of arcinst */
			for(i=0; tpt->connects[i] != NOARCPROTO; i++)
				if (typ == tpt->connects[i]) break;
			if (tpt->connects[i] == NOARCPROTO) continue;

			/* potential portinst: get information about its position */
			(void)us_portdistance(tonodeinst, tpt, prefx, prefy, &tx, &ty, rwid, 1);

			if (abs(tx-prefx) + abs(fy-prefy) < abs(fx-prefx) + abs(ty-prefy))
			{
				xi = tx;  yi = fy;   altxi = fx;   altyi = ty;
			} else
			{
				xi = fx;  yi = ty;   altxi = tx;   altyi = fy;
			}

			/* see if port angles are correct */
			bad = 0;
			trange = (INTSML)(((tpt->userbits&PORTARANGE) >> PORTARANGESH) * 10);
			if (trange != 1800)
			{
				if (tx == xi && ty == yi) bad++; else
				{
					i = figureangle(tx, ty, xi, yi);
					if (us_fitportangle(tonodeinst, tpt, i, trange) != 0) bad++;
				}
			}
			frange = (INTSML)(((fpt->userbits&PORTARANGE) >> PORTARANGESH) * 10);
			if (frange != 1800)
			{
				if (fx == xi && fy == yi) bad++; else
				{
					i = figureangle(fx, fy, xi, yi);
					if (us_fitportangle(fromnodeinst, fpt, i, frange) != 0) bad++;
				}
			}

			/* if port angles are wrong, try the other inflexion point */
			if (bad != 0)
			{
				if (tx == altxi && ty == altyi) continue;
				i = figureangle(tx, ty, altxi, altyi);
				if (us_fitportangle(tonodeinst, tpt, i, trange) != 0) continue;
				if (fx == altxi && fy == altyi) continue;
				i = figureangle(fx, fy, altxi, altyi);
				if (us_fitportangle(fromnodeinst, fpt, i, frange) != 0) continue;
				xi = altxi;   yi = altyi;
			}

			/* see if this path is better than any previous ones */
			dist = abs(fx-tx) + abs(fy-ty);
			if (dist > bestdist) continue;

			/* select this path */
			found++;               bestdist = dist;
			fromportproto = fpt;   toportproto = tpt;
			bestxi = xi;           bestyi = yi;
			bestfx = fx;           bestfy = fy;
			besttx = tx;           bestty = ty;
		}
	}

	/* make one-bend arcinst */
	if (found != 0)
	{
		/* figure out what primitive nodeproto connects these arcs */
		ft = getpinproto(typ);
		if (ft == NONODEPROTO)
		{
			us_abortcommand(_("No pin for %s arcs"), describearcproto(typ));
			return(NOARCINST);
		}

		/* handle edge alignment */
		if (us_edgealignment != 0)
		{
			if (bestfx == bestxi)
			{
				/* see if "bestxi" and "bestfx" can be aligned */
				i = us_alignvalue(bestfx - wid/2, us_edgealignment, &otheralign) + wid/2;
				otheralign += wid/2;
				shapeportpoly(fromnodeinst, fromportproto, poly, 0);
				if (isinside(i, bestfy, poly) != 0) bestfx = bestxi = i; else
					if (isinside(otheralign, bestfy, poly) != 0) bestfx = bestxi = otheralign;

				/* see if "bestyi" and "bestty" can be aligned */
				i = us_alignvalue(bestty - wid/2, us_edgealignment, &otheralign) + wid/2;
				otheralign += wid/2;
				shapeportpoly(tonodeinst, toportproto, poly, 0);
				if (isinside(besttx, i, poly) != 0) bestty = bestyi = i; else
					if (isinside(besttx, otheralign, poly) != 0) bestty = bestyi = otheralign;
			} else if (bestfy == bestyi)
			{
				/* see if "bestyi" and "bestfy" can be aligned */
				i = us_alignvalue(bestfy - wid/2, us_edgealignment, &otheralign) + wid/2;
				otheralign += wid/2;
				shapeportpoly(fromnodeinst, fromportproto, poly, 0);
				if (isinside(bestfx, i, poly) != 0) bestfy = bestyi = i; else
					if (isinside(bestfx, otheralign, poly) != 0) bestfy = bestyi = otheralign;

				/* see if "bestxi" and "besttx" can be aligned */
				i = us_alignvalue(besttx - wid/2, us_edgealignment, &otheralign) + wid/2;
				otheralign += wid/2;
				shapeportpoly(tonodeinst, toportproto, poly, 0);
				if (isinside(i, bestty, poly) != 0) besttx = bestxi = i; else
					if (isinside(otheralign, bestty, poly) != 0) besttx = bestxi = otheralign;
			}
		}

		/* create the intermediate nodeinst */
		defaultnodesize(ft, &pxs, &pys);
		lx = bestxi - pxs/2;   hx = lx + pxs;
		ly = bestyi - pys/2;   hy = ly + pys;
		ni = newnodeinst(ft, lx,hx, ly,hy, 0, 0, fromnodeinst->parent);
		if (ni == NONODEINST)
		{
			us_abortcommand(_("Cannot create connecting pin"));
			return(NOARCINST);
		}
		endobjectchange((INTBIG)ni, VNODEINST);

		/* run the connecting arcs */
		fpt = ft->firstportproto;
		ai1 = us_runarcinst(fromnodeinst,fromportproto, bestfx, bestfy, ni, fpt,
			bestxi, bestyi, typ, wid);
		if (ai1 == NOARCINST) return(NOARCINST);
		ai2 = us_runarcinst(ni, fpt, bestxi, bestyi, tonodeinst, toportproto,
			besttx, bestty, typ, wid);
		if (ai2 == NOARCINST) return(NOARCINST);
		ttyputmsg(_("Created 2 %s arcs"), typ->protoname);
		if (abs(bestfx-bestxi)+abs(bestfy-bestyi) > abs(bestxi-besttx)+abs(bestyi-bestty))
		{
			*alt = ai2;
			return(ai1);
		} else
		{
			*alt = ai1;
			return(ai2);
		}
	}

	/* give up */
	return(NOARCINST);
}

/*
 * routine to check for a two-bend connection running from nodeinst
 * "fromnodeinst" to nodeinst "tonodeinst" on arcproto "typ" and create two
 * arcs that are "wid" wide if possible.  It is prefered that the jog pass
 * close to (prefx, prefy) and is on portproto "fromppt" (if "fromppt" is not
 * NOPORTPROTO) on the FROM nodeinst and on portproto "toppt" (if "toppt" is
 * not NOPORTPROTO) on the TO nodeinst.  If the arcinst is created, the
 * routine returns its address and the other two arcs are returned in "alt1"
 * and "alt2".  If no arc can be created, the routine returns NOARCINST.
 */
ARCINST *us_twobend(NODEINST *fromnodeinst, PORTPROTO *fromppt, NODEINST *tonodeinst,
	PORTPROTO *toppt, ARCPROTO *typ, INTBIG prefx,
	INTBIG prefy, INTBIG wid, ARCINST **alt1, ARCINST **alt2)
{
	REGISTER NODEINST *ni1, *ni2;
	REGISTER NODEPROTO *ft;
	REGISTER PORTPROTO *fpt, *tpt;
	PORTPROTO *fromportproto, *toportproto;
	REGISTER INTSML i;
	REGISTER INTBIG lx, hx, ly, hy, rwid;
	REGISTER ARCINST *ai;
	INTBIG fx, fy, tx, ty, xi1, yi1, xi2, yi2, bestdist, dist,
		bestfx, bestfy, besttx, bestty, pxs, pys;

	/* determine true width */
	wid = us_figurearcwidth(typ, wid, fromnodeinst, fromppt, prefx, prefy, tonodeinst, toppt, prefx, prefy);
	rwid = wid - arcprotowidthoffset(typ);

	/* find the "from" port */
	bestdist = HUGEINT;   fromportproto = NOPORTPROTO;
	for(fpt = fromnodeinst->proto->firstportproto; fpt != NOPORTPROTO; fpt = fpt->nextportproto)
	{
		if (fromppt != NOPORTPROTO && fromppt != fpt) continue;

		/* see if the port has an opening for this type of arcinst */
		for(i=0; fpt->connects[i] != NOARCPROTO; i++)
			if (typ == fpt->connects[i]) break;
		if (fpt->connects[i] == NOARCPROTO) continue;

		/* potential portinst: get information about its position */
		(void)us_portdistance(fromnodeinst, fpt, prefx, prefy, &fx, &fy, rwid, 1);

		dist = abs(fx-prefx) + abs(fy-prefy);
		if (dist > bestdist) continue;
		fromportproto = fpt;   bestdist = dist;
		bestfx = fx;           bestfy = fy;
	}
	if (fromportproto == NOPORTPROTO) return(NOARCINST);

	/* find the "to" port */
	bestdist = HUGEINT;   toportproto = NOPORTPROTO;
	for(tpt = tonodeinst->proto->firstportproto; tpt != NOPORTPROTO; tpt = tpt->nextportproto)
	{
		if (toppt != NOPORTPROTO && toppt != tpt) continue;

		/* see if the port has an opening for this type of arcinst */
		for(i=0; tpt->connects[i] != NOARCPROTO; i++)
			if (typ == tpt->connects[i]) break;
		if (tpt->connects[i] == NOARCPROTO) continue;

		/* potential portinst: get information about its position */
		(void)us_portdistance(tonodeinst, tpt, prefx, prefy, &tx, &ty, rwid, 1);

		dist = abs(tx-prefx) + abs(ty-prefy);
		if (dist > bestdist) continue;
		toportproto = tpt;   bestdist = dist;
		besttx = tx;         bestty = ty;
	}
	if (toportproto == NOPORTPROTO) return(NOARCINST);

	/*
	 * figure out whether the jog will run horizontally or vertically.
	 * Use directionality constraints if they exist
	 */
	if (((fromportproto->userbits&PORTARANGE) >> PORTARANGESH) != 180)
	{
		i = us_bottomrecurse(fromnodeinst, fromportproto);
	} else if (((toportproto->userbits&PORTARANGE) >> PORTARANGESH) != 180)
	{
		i = us_bottomrecurse(tonodeinst, toportproto);
	} else if ((prefy > bestfy && prefy > bestty) || (prefy < bestfy && prefy < bestty))
	{
		/* jog is horizontal if prefy is above or below both ports */
		i = 900;
	} else if ((prefx > bestfx && prefx > besttx) || (prefx < bestfx && prefx < besttx))
	{
		/* jog is vertical if prefx is to right or left of both ports */
		i = 0;
	} else
	{
		/* if area between nodes is wider than tall, jog is vertical */
		if (abs(bestfx-besttx) > abs(bestfy-bestty)) i = 0; else i = 900;
	}
	i = (i+450) % 1800;   if (i < 0) i += 1800;
	if (i < 900)
	{
		xi1 = xi2 = prefx;
		yi1 = bestfy;   yi2 = bestty;
	} else
	{
		xi1 = bestfx;   xi2 = besttx;
		yi1 = yi2 = prefy;
	}

	/* figure out what primitive nodeproto connects these arcs */
	ft = getpinproto(typ);
	if (ft == NONODEPROTO)
	{
		us_abortcommand(_("No pin for %s arcs"), describearcproto(typ));
		return(NOARCINST);
	}
	defaultnodesize(ft, &pxs, &pys);

	/* create the intermediate nodeinsts */
	lx = xi1 - pxs/2;   hx = lx + pxs;
	ly = yi1 - pys/2;   hy = ly + pys;
	ni1 = newnodeinst(ft, lx,hx, ly,hy, 0, 0, fromnodeinst->parent);
	if (ni1 == NONODEINST)
	{
		us_abortcommand(_("Cannot create first connecting pin"));
		return(NOARCINST);
	}
	endobjectchange((INTBIG)ni1, VNODEINST);
	lx = xi2 - pxs/2;   hx = lx + pxs;
	ly = yi2 - pys/2;   hy = ly + pys;
	ni2 = newnodeinst(ft, lx,hx, ly,hy, 0, 0, fromnodeinst->parent);
	if (ni2 == NONODEINST)
	{
		us_abortcommand(_("Cannot create second connecting pin"));
		return(NOARCINST);
	}
	endobjectchange((INTBIG)ni2, VNODEINST);

	/* run the arcs */
	fpt = ft->firstportproto;
	*alt1 = us_runarcinst(fromnodeinst, fromportproto, bestfx, bestfy, ni1, fpt,
		xi1,yi1, typ, wid);
	if (*alt1 == NOARCINST) return(NOARCINST);
	ai = us_runarcinst(ni1,fpt, xi1,yi1, ni2,fpt, xi2,yi2, typ, wid);
	if (ai == NOARCINST) return(NOARCINST);
	*alt2 = us_runarcinst(ni2,fpt, xi2,yi2, tonodeinst, toportproto, besttx, bestty, typ, wid);
	if (*alt2 == NOARCINST) return(NOARCINST);
	ttyputmsg(_("Created 3 %s arcs"), typ->protoname);
	return(ai);
}

/*
 * run an arcinst from portproto "fromportproto" of nodeinst "fromnodeinst" at
 * (fromx,fromy) to portproto "toportproto" of nodeinst "tonodeinst" at
 * (tox,toy).  The type of the arcinst is "ap" and the width is "wid".  The
 * routine returns the address of the newly created arcinst (NOARCINST on
 * error).
 */
ARCINST *us_runarcinst(NODEINST *fromnodeinst, PORTPROTO *fromportproto, INTBIG fromx, INTBIG fromy,
	NODEINST *tonodeinst, PORTPROTO *toportproto, INTBIG tox, INTBIG toy, ARCPROTO *ap, INTBIG wid)
{
	REGISTER ARCINST *ai;
	REGISTER INTBIG bits;

	/* see if nodes need to be undrawn to account for "Steiner Point" changes */
	if ((fromnodeinst->proto->userbits&WIPEON1OR2) != 0)
		startobjectchange((INTBIG)fromnodeinst, VNODEINST);
	if ((tonodeinst->proto->userbits&WIPEON1OR2) != 0)
		startobjectchange((INTBIG)tonodeinst, VNODEINST);

	/* create the arcinst */
	bits = us_makearcuserbits(ap);
	ai = newarcinst(ap, wid, bits, fromnodeinst, fromportproto, fromx,fromy,
		tonodeinst, toportproto, tox, toy, fromnodeinst->parent);
	if (ai == NOARCINST)
	{
		us_abortcommand(_("Problem creating the arc"));
		return(NOARCINST);
	}
	ai->changed = 0;
	endobjectchange((INTBIG)ai, VARCINST);
	us_setarcproto(ap, 0);

	/* see if nodes need to be redrawn to account for "Steiner Point" changes */
	if ((fromnodeinst->proto->userbits&WIPEON1OR2) != 0)
		endobjectchange((INTBIG)fromnodeinst, VNODEINST);
	if ((tonodeinst->proto->userbits&WIPEON1OR2) != 0)
		endobjectchange((INTBIG)tonodeinst, VNODEINST);
	return(ai);
}

/*
 * routine to find the width of the widest arcinst of type "ap" connected
 * to any port of nodeinst "ni" (if "ni" is primitive) or to port "por" of
 * nodeinst "ni" (if "ni" is complex).
 */
INTBIG us_widestarcinst(ARCPROTO *ap, NODEINST *ni, PORTPROTO *por)
{
	REGISTER INTBIG wid;
	REGISTER INTSML pindex;
	REGISTER PORTARCINST *pi;

	/* look at all arcs on the nodeinst */
	wid = 0;
	pindex = ni->proto->primindex;
	if (por == NOPORTPROTO) pindex = 1;
	for(;;)
	{
		for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
		{
			if (pindex == 0 && pi->proto != por) continue;
			if (pi->conarcinst->proto != ap) continue;
			if (pi->conarcinst->width > wid) wid = pi->conarcinst->width;
		}

		/* descend to the next level in the hierarchy */
		if (pindex != 0) break;
		ni = por->subnodeinst;
		pindex = ni->proto->primindex;
		por = por->subportproto;
	}
	return(wid);
}

/*
 * Routine to determine the proper width of arc of type "typ" with width "wid" and running from node
 * "ni1", port "pp1" to node "ni2", port "pp2".  Oversize nodes are considered when sizing the arc.
 */
INTBIG us_stretchtonodes(ARCPROTO *typ, INTBIG wid, NODEINST *ni1, PORTPROTO *pp1, INTBIG otherx, INTBIG othery)
{
	/* this turns out to be a bad idea.  So it is turned off. */
#if 0
	REGISTER INTBIG xstretch, ystretch, i, rot, cx, cy;
	INTBIG ni1x, ni1y;

	/* see if node 1 is stretched */
	xstretch = (ni1->highx - ni1->lowx) - (ni1->proto->highx - ni1->proto->lowx);
	ystretch = (ni1->highy - ni1->lowy) - (ni1->proto->highy - ni1->proto->lowy);
	if (xstretch > 0 || ystretch > 0)
	{
		rot = (ni1->rotation + 900 * ni1->transpose) % 3600;
		switch (rot)
		{
			case 0:
			case 1800:
				break;
			case 900:
			case 2700:
				i = xstretch;   xstretch = ystretch;   ystretch = i;
				break;
			default:
				return(wid);
		}
		portposition(ni1, pp1, &ni1x, &ni1y);
		cx = (ni1->lowx + ni1->highx) / 2;
		cy = (ni1->lowy + ni1->highy) / 2;
		if (ni1x == cx && ni1y == cy)
		{
			if (abs(ni1x-otherx) > abs(ni1y-othery))
			{
				/* horizontal wire: see if Y stretch allows growth */
				if (ystretch > wid - typ->nominalwidth) wid = ystretch + typ->nominalwidth;
			} else
			{
				/* vertical wire: see if X stretch allows growth */
				if (xstretch > wid - typ->nominalwidth) wid = xstretch + typ->nominalwidth;
			}
		} else
		{
			if (abs(ni1x-cx) > abs(ni1y-cy))
			{
				/* horizontal wire: see if Y stretch allows growth */
				if (ystretch > wid - typ->nominalwidth) wid = ystretch + typ->nominalwidth;
			} else
			{
				/* vertical wire: see if X stretch allows growth */
				if (xstretch > wid - typ->nominalwidth) wid = xstretch + typ->nominalwidth;
			}
		}
	}
#endif
	return(wid);
}

/*
 * Routine to determine the width to use for arc "typ", given a default width of "wid", that it
 * runs from "ni1/pp1" towards (ox1,oy1) and to "ni2/pp2" towards (ox2,oy2).  If either end is
 * a pin, the width calculation is not performed for it.
 */
INTBIG us_figurearcwidth(ARCPROTO *typ, INTBIG wid, NODEINST *ni1, PORTPROTO *pp1, INTBIG ox1, INTBIG oy1,
	NODEINST *ni2, PORTPROTO *pp2, INTBIG ox2, INTBIG oy2)
{
	INTBIG wid1, wid2, stretchwid, stretch1, stretch2, fun;
	char *extra;
	REGISTER NODEINST *rni;
	REGISTER PORTPROTO *rpp;

	stretch1 = stretch2 = 1;

	/* see if node 1 is a pin */
	rni = ni1;   rpp = pp1;
	while (rni->proto->primindex == 0)
	{
		rni = rpp->subnodeinst;
		rpp = rpp->subportproto;
	}
	fun = nodefunction(rni, &extra);
	if (fun == NPPIN) stretch1 = 0;

	/* see if node 2 is a pin */
	rni = ni2;   rpp = pp2;
	while (rni->proto->primindex == 0)
	{
		rni = rpp->subnodeinst;
		rpp = rpp->subportproto;
	}
	fun = nodefunction(rni, &extra);
	if (fun == NPPIN) stretch2 = 0;

	if (stretch1 == 0 && stretch2 == 0) return(wid);

	wid1 = us_stretchtonodes(typ, wid, ni1, pp1, ox1, oy1);
	wid2 = us_stretchtonodes(typ, wid, ni2, pp2, ox2, oy2);
	if (stretch1 == 0) wid1 = wid2;
	if (stretch2 == 0) wid2 = wid1;
	stretchwid = mini(wid1, wid2);
	if (stretchwid > wid) wid = stretchwid;
	return(wid);
}

/*
 * routine to determine the nodeinst to be used when connecting the geometry
 * module pointed to by "ipos" and the geometry module in "othergeom".
 * The port prototype on the other object ("othergeom") is in "otherport" and
 * the port prototype on the object in "ipos" is in "ipp".  The prefered site
 * of connection is in (prefx, prefy).  If the first module (ipos) is an
 * arcinst, it may be split into two arcs and a nodeinst in which case that
 * nodeinst will be returned and the address of the geometry module will be
 * changed to point to that nodeinst.
 */
NODEINST *us_getnodeonarcinst(GEOM **ipos, PORTPROTO **ipp, GEOM *othergeom,
	PORTPROTO *otherport, INTBIG prefx, INTBIG prefy)
{
	REGISTER ARCINST *ai, *oar, *ar1,*ar2;
	REGISTER ARCPROTO *ap;
	REGISTER NODEINST *ni, *fno, *tno;
	REGISTER PORTPROTO *fpt, *tpt, *pt1;
	REGISTER NODEPROTO *np, *pnt;
	REGISTER GEOM *geom;
	REGISTER INTBIG wid, bits1, bits2, lx, hx, ly, hy;
	INTBIG fendx, fendy, tendx, tendy, pxs, pys;
	static POLYGON *poly = NOPOLYGON;

	/* get the actual geometry modules */
	geom = *ipos;

	/* if the module is a nodeinst, return it */
	if (geom->entrytype == OBJNODEINST) return(geom->entryaddr.ni);

	/* if the other module is a primitive node, use center as break point */
	if (othergeom->entrytype == OBJNODEINST)
	{
		ni = othergeom->entryaddr.ni;
		if (otherport != NOPORTPROTO)
		{
			/* make sure there is a polygon */
			if (poly == NOPOLYGON) poly = allocstaticpolygon(1, us_aid->cluster);

			/* get the polygon describing the port */
			poly->xv[0] = prefx;   poly->yv[0] = prefy;   poly->count = 1;
			shapeportpoly(ni, otherport, poly, 1);

			/* determine the center of the polygon */
			getcenter(poly, &prefx, &prefy);
		} else if (ni->proto->primindex != 0)
		{
			prefx = (ni->lowx + ni->highx) / 2;
			prefy = (ni->lowy + ni->highy) / 2;
		}
	}

	/* find point on this arcinst closest to break point */
	ai = geom->entryaddr.ai;
	if (ai->end[0].xpos == ai->end[1].xpos)
	{
		/* vertical arcinst */
		if (othergeom->entrytype == OBJARCINST)
		{
			/* if two arcs are perpendicular, find point of intersection */
			oar = othergeom->entryaddr.ai;
			if (oar->end[0].ypos == oar->end[1].ypos) prefy=oar->end[0].ypos;
		}
	} else if (ai->end[0].ypos == ai->end[1].ypos)
	{
		/* horizontal arcinst */
		if (othergeom->entrytype == OBJARCINST)
		{
			/* if two arcs are perpendicular, find point of intersection */
			oar = othergeom->entryaddr.ai;
			if (oar->end[0].xpos == oar->end[1].xpos) prefx=oar->end[0].xpos;
		}
	}

	/* adjust to closest point */
	(void)closestpointtosegment(ai->end[0].xpos,ai->end[0].ypos,
		ai->end[1].xpos,ai->end[1].ypos, &prefx, &prefy);
	if (prefx == ai->end[0].xpos && prefy == ai->end[0].ypos)
	{
		*ipp = ai->end[0].portarcinst->proto;
		return(ai->end[0].nodeinst);
	}
	if (prefx == ai->end[1].xpos && prefy == ai->end[1].ypos)
	{
		*ipp = ai->end[1].portarcinst->proto;
		return(ai->end[1].nodeinst);
	}

	/* break is at (prefx, prefy): save information about the arcinst */
	fno = ai->end[0].nodeinst;    fpt = ai->end[0].portarcinst->proto;
	tno = ai->end[1].nodeinst;    tpt = ai->end[1].portarcinst->proto;
	fendx = ai->end[0].xpos;      fendy = ai->end[0].ypos;
	tendx = ai->end[1].xpos;      tendy = ai->end[1].ypos;
	ap = ai->proto;   wid = ai->width;  pnt = ai->parent;
	bits1 = bits2 = ai->userbits;
	if ((bits1&ISNEGATED) != 0)
	{
		if ((bits1&REVERSEEND) == 0) bits2 &= ~ISNEGATED; else
			bits1 &= ~ISNEGATED;
	}
	if (figureangle(fendx,fendy, prefx,prefy) != figureangle(prefx,prefy, tendx,tendy))
	{
		bits1 &= ~FIXANG;
		bits2 &= ~FIXANG;
	}

	/* create the splitting pin */
	np = getpinproto(ap);
	if (np == NONODEPROTO) return(NONODEINST);
	defaultnodesize(np, &pxs, &pys);
	pt1 = np->firstportproto;
	lx = prefx - pxs/2;   hx = lx + pxs;
	ly = prefy - pys/2;   hy = ly + pys;
	ni = newnodeinst(np, lx,hx, ly,hy, 0, 0, pnt);
	if (ni == NONODEINST)
	{
		ttyputerr(_("Cannot create splitting pin"));
		return(NONODEINST);
	}
	endobjectchange((INTBIG)ni, VNODEINST);

	/* delete the old arcinst */
	startobjectchange((INTBIG)ai, VARCINST);
	if (killarcinst(ai)) ttyputerr(_("Error deleting original arc"));

	/* create the two new arcinsts */
	ar1 = newarcinst(ap, wid, bits1, fno, fpt, fendx, fendy, ni, pt1, prefx, prefy, pnt);
	ar2 = newarcinst(ap, wid, bits2, ni, pt1, prefx, prefy, tno, tpt, tendx, tendy, pnt);
	if (ar1 == NOARCINST || ar2 == NOARCINST)
	{
		ttyputerr(_("Error creating the split arc parts"));
		return(ni);
	}
	(void)copyvars((INTBIG)ai, VARCINST, (INTBIG)ar1, VARCINST);
	endobjectchange((INTBIG)ar1, VARCINST);
	endobjectchange((INTBIG)ar2, VARCINST);

	/* return pointers to the splitting pin */
	*ipos = ni->geom;
	*ipp = pt1;
	return(ni);
}

/*
 * routine to report the distance of point (prefx,prefy) to port "pt" of node
 * instance "ni".  The closest point on the polygon is returned in (x,y),
 * given that the port will connect to an arc with width "wid".  If "purpose"
 * is nonzero, a new sub-port location within the port is desired from
 * the "shapeportpoly" routine.  Euclidean distance is not always used, but
 * at least the metric is consistent with itself.
 */
INTBIG us_portdistance(NODEINST *ni, PORTPROTO *pt, INTBIG prefx, INTBIG prefy, INTBIG *x,
	INTBIG *y, INTBIG wid, INTSML purpose)
{
	static POLYGON *poly = NOPOLYGON;
	REGISTER INTSML i, j;
	REGISTER INTBIG bestdist, px, py, nx, ny;

	/* get polygon */
	if (poly == NOPOLYGON) poly = allocstaticpolygon(4, us_aid->cluster);

	/* specify prefered location of new port */
	poly->xv[0] = prefx;   poly->yv[0] = prefy;   poly->count = 1;
	shapeportpoly(ni, pt, poly, purpose);
	switch (poly->style)
	{
		case FILLED:
			/* reduce the port area to the proper amount for this arc */
			reduceportpoly(poly, ni, pt, wid);

			/* for filled polygon, see if point is inside */
			if (isinside(prefx, prefy, poly) != 0)
			{
				*x = prefx;   *y = prefy;
				return(0);
			}

			*x = prefx;   *y = prefy;
			closestpoint(poly, x, y);
			return(0);

		case OPENED:
		case CLOSED:
			/* for OPENED/CLOSED polygons look for proximity to a vertex */
			bestdist = abs(poly->xv[0] - prefx) + abs(poly->yv[0] - prefy);
			*x = poly->xv[0];   *y = poly->yv[0];
			for(j=1; j<poly->count; j++)
			{
				i = abs(poly->xv[j] - prefx) + abs(poly->yv[j] - prefy);
				if (i < bestdist)
				{
					bestdist = i;
					*x = poly->xv[j];   *y = poly->yv[j];
				}
			}

			/* additionally, look for proximity to an edge */
			for(j=0; j<poly->count; j++)
			{
				if (j == 0)
				{
					if (poly->style == OPENED) continue;
					px = poly->xv[poly->count-1];
					py = poly->yv[poly->count-1];
				} else
				{
					px = poly->xv[j-1];
					py = poly->yv[j-1];
				}
				nx = poly->xv[j];
				ny = poly->yv[j];

				/* handle vertical line that is perpendicular to point */
				if (px == nx && maxi(py, ny) >= prefy && mini(py, ny) <= prefy &&
					abs(px - prefx) < bestdist)
				{
					bestdist = i;
					*x = px;
					*y = prefy;
				}

				/* handle horizontal line that is perpendicular to point */
				if (py == ny && maxi(px, nx) >= prefx && mini(px, nx) <= prefx &&
					abs(py - prefy) < bestdist)
				{
					bestdist = i;
					*x = prefx;
					*y = py;
				}
			}
			return(bestdist);
	}

	/* bogus answer for unusual shapes!!! */
	return(0);
}

/*
 * routine to determine whether port "pp" of node "ni" can connect to an
 * arc at angle "angle" within range "range".  Returns nonzero if the
 * connection cannot be made.
 */
INTSML us_fitportangle(NODEINST *ni, PORTPROTO *pp, INTSML angle, INTSML range)
{
	REGISTER INTSML j;

	j = us_bottomrecurse(ni, pp);
	j = (j - angle) % 3600;   if (j < 0) j += 3600;
	if (j > 1800) j = 3600 - j;
	if (j > range) return(1);
	return(0);
}

/*
 * routine to recurse to the bottom (most primitive node) of a port and
 * compute the port orientation from the bottom up.
 */
INTSML us_bottomrecurse(NODEINST *ni, PORTPROTO *pp)
{
	REGISTER INTSML k;

	if (ni->proto->primindex == 0)
		k = us_bottomrecurse(pp->subnodeinst, pp->subportproto); else
			k = (INTSML)(((pp->userbits&PORTANGLE) >> PORTANGLESH) * 10);
	k += ni->rotation;
	if (ni->transpose != 0) k = 2700 - k;
	return(k);
}
