/*
 * Electric(tm) VLSI Design System
 *
 * File: routauto.c
 * Auto-stitching code
 * 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
 * info@staticfreesoft.com
 */

#include "config.h"
#if ROUTAID

#include "global.h"
#include "efunction.h"
#include "rout.h"
#include "usr.h"
#include <math.h>

ARCPROTO *ro_preferedarc;	/* the prefered arc */

INTBIG ro_checkstitching(NODEINST*);
INTBIG ro_testpoly(NODEINST*, PORTPROTO*, ARCPROTO*, POLYGON*, NODEINST*);
INTBIG ro_comparepoly(NODEINST*, PORTPROTO*, POLYGON*, NODEINST*, PORTPROTO*, POLYGON*, ARCPROTO*);
ARCPROTO *ro_getarcproto(PORTPROTO*, PORTPROTO*, TECHNOLOGY*);
INTSML ro_canconnect(ARCPROTO*, PORTPROTO*);
void ro_findsmallestlayer(ARCPROTO*);
void ro_substituteforpseudo(NODEINST *ni, POLYGON *poly);
void ro_freercheck(RCHECK*);

void ro_autostitch(void)
{
	REGISTER RCHECK *r, *nextr;
	REGISTER INTSML i, order, total;
	REGISTER INTBIG *bbarray, count;
	INTBIG x1, x2, y1, y2;
	XARRAY trans, localtran, temp;
	REGISTER NODEINST *ni, *rni;
	REGISTER PORTPROTO *pp, *rpp;
	REGISTER NODEPROTO *np;
	REGISTER ARCPROTO *ap;
	REGISTER LIBRARY *lib;
	REGISTER TECHNOLOGY *tech;
	extern AIDENTRY *net_aid;
	REGISTER VARIABLE *var;

	/* make sure network tool is on */
	if ((net_aid->aidstate&AIDON) == 0)
	{
		ttyputerr("Network tool must be running...turning it on");
		aidturnon(net_aid, 0);
		return;
	}

	/* if there is nothing queued to stitch, quit now */
	if (ro_firstrcheck == NORCHECK) return;

	/* set temp1 flag on all facets that will be checked */
	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
			np->temp1 = 0;

	/* next pre-compute bounds on all nodes in facets to be changed */
	count = 0;
	for(r = ro_firstrcheck; r != NORCHECK; r = r->nextcheck)
		if (r->entity->parent->temp1 == 0)
	{
		r->entity->parent->temp1++;
		for(ni = r->entity->parent->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
		{
			ni->temp1 = 0;

			/* count the ports on this node */
			for(total=0, pp = ni->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
				total++;

			/* get memory for bounding box of each port */
			bbarray = emalloc((total * 4 * SIZEOFINTBIG), el_tempcluster);
			if (bbarray == 0)
			{
				ttyputerr("No memory for stitching");
				ni->temp2 = -1;
			} else
			{
				ni->temp2 = (INTBIG)bbarray;
				i = 0;
				for(pp = ni->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
				{
					makerot(ni, trans);
					rni = ni;   rpp = pp;
					while (rni->proto->primindex == 0)
					{
						maketrans(rni, localtran);
						transmult(localtran, trans, temp);
						rni = rpp->subnodeinst;
						rpp = rpp->subportproto;
						makerot(rni, localtran);
						transmult(localtran, temp, trans);
					}
					xform(rni->lowx, rni->lowy, &x1, &y1, trans);
					xform(rni->highx, rni->highy, &x2, &y2, trans);
					bbarray[i++] = mini(x1, x2);  bbarray[i++] = maxi(x1, x2);
					bbarray[i++] = mini(y1, y2);  bbarray[i++] = maxi(y1, y2);
				}
			}
		}
	}

	/* next set ordinals on nodes to be checked */
	order = 1;
	for(r = ro_firstrcheck; r != NORCHECK; r = r->nextcheck)
		r->entity->temp1 = order++;

	/* find out the prefered routing arc */
	ro_preferedarc = NOARCPROTO;
	var = getvalkey((INTBIG)ro_aid, VAID, VARCPROTO, ro_preferedkey);
	if (var != NOVARIABLE) ro_preferedarc = (ARCPROTO *)var->addr; else
	{
		/* see if there is a default user arc */
		if (us_curarcproto != NOARCPROTO) ro_preferedarc = us_curarcproto;
	}

	/* finally, initialize the information about which layer is smallest on each arc */
	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
		for(ap = tech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
			ap->temp1 = -1;

	/* now run through the nodeinsts to be checked for stitching */
	for(r = ro_firstrcheck; r != NORCHECK; r = nextr)
	{
		nextr = r->nextcheck;
		if (stopping("Routing") == 0)
		{
			ni = r->entity;
			if ((ni->parent->userbits&NLOCKED) != 0) continue;
			count += ro_checkstitching(ni);
		}
		ro_freercheck(r);
	}
	ro_firstrcheck = NORCHECK;

	/* free any memory associated with this operation */
	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
			if (np->temp1 != 0)
	{
		for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
			if (ni->temp2 != -1 && ni->temp2 != 0)
				efree((char *)ni->temp2);
	}

	/* if selection was done, restore the highlighting */
	if ((ro_state&SELDONE) != 0)
		(void)askaid(us_aid, "up-stack");

	/* report results */
	if (count != 0)
		ttyputmsg("AUTO ROUTING: added %ld %s", count, makeplural("wire", count));
}

/*
 * routine to check nodeinst "ni" for possible stitching to neighboring
 * nodeinsts
 */
INTBIG ro_checkstitching(NODEINST *ni)
{
	REGISTER INTBIG search, lx, hx, ly, hy, bestdist, dist, ox, oy, stitched, count;
	REGISTER INTSML tot, i, j, bbp;
	INTBIG x, y;
	XARRAY trans, localtran, temp;
	REGISTER GEOM *geom;
	REGISTER NODEINST *oni, *rni;
	REGISTER PORTPROTO *pp, *rpp, *bestpp;
	REGISTER PORTARCINST *pi;
	REGISTER ARCPROTO *ap, *couldbe;
	static POLYGON *poly = NOPOLYGON;

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

	/* look for any other nodes that touch or overlap this one */
	lx = ni->geom->lowx;   hx = ni->geom->highx;
	ly = ni->geom->lowy;   hy = ni->geom->highy;
	search = initsearch(lx-1, hx+1, ly-1, hy+1, ni->parent);
	count = 0;
	for(;;)
	{
		if (stopping("Routing") != 0) { termsearch(search);   return(count); }

		/* find another node in this area */
		if ((geom = nextobject(search)) == NOGEOM) break;
		if (geom->entrytype != OBJNODEINST) continue;
		oni = geom->entryaddr.ni;

		/* don't check newly created nodes */
		if (oni->temp2 == 0) continue;

		/* if both nodes are being checked, examine them only once */
		if (oni->temp1 != 0 && oni->temp1 <= ni->temp1) continue;

		/* now look at every layer in this node */
		if (ni->proto->primindex == 0)
		{
			/* complex node instance: look at all ports */
			if (ni->temp2 == 0 || ni->temp2 == -1) bbp = -1; else
				bbp = 0;
			for(pp = ni->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
			{
				/* first do a bounding box check */
				if (bbp >= 0)
				{
					lx = ((INTBIG *)ni->temp2)[bbp++];
					hx = ((INTBIG *)ni->temp2)[bbp++];
					ly = ((INTBIG *)ni->temp2)[bbp++];
					hy = ((INTBIG *)ni->temp2)[bbp++];
					if (lx > oni->geom->highx || hx < oni->geom->lowx ||
						ly > oni->geom->highy || hy < oni->geom->lowy) continue;
				}

				/* stop now if already an arc on this port to other node */
				for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
					if (pi->proto == pp && (pi->conarcinst->end[0].nodeinst == oni ||
							pi->conarcinst->end[1].nodeinst == oni)) break;
				if (pi != NOPORTARCINST) continue;

				/* find the primitive node at the bottom of this port */
				makerot(ni, trans);
				rni = ni;   rpp = pp;
				while (rni->proto->primindex == 0)
				{
					maketrans(rni, localtran);
					transmult(localtran, trans, temp);
					rni = rpp->subnodeinst;
					rpp = rpp->subportproto;
					makerot(rni, localtran);
					transmult(localtran, temp, trans);
				}

				/* determine the smallest layer for all possible arcs */
				for(i=0; pp->connects[i] != NOARCPROTO; i++)
				{
					ap = pp->connects[i];
					ro_findsmallestlayer(ap);
				}

				/* look at all polygons on this nodeinst */
				tot = nodeEpolys(rni);
				for(j=0; j<tot; j++)
				{
					shapeEnodepoly(rni, j, poly);

					/* only want electrically connected polygons */
					if (poly->portproto == NOPORTPROTO) continue;

					/* only want polygons on correct part of this nodeinst */
					if (poly->portproto->network != rpp->network) continue;

					/* transformed polygon */
					xformpoly(poly, trans);

					/* if the polygon layer is pseudo, substitute real layer */
					ro_substituteforpseudo(rni, poly);

					/* check all possible arcs */
					for(i=0; pp->connects[i] != NOARCPROTO; i++)
						if (pp->connects[i] == ro_preferedarc) break;
					couldbe = pp->connects[i];
					for(i=0; pp->connects[i] != NOARCPROTO; i++)
					{
						ap = pp->connects[i];
						if (couldbe != NOARCPROTO && couldbe != ap) continue;

						/* arc must be in the same technology */
						if (ap->tech != rni->proto->tech) continue;

						/* this polygon must be the smallest arc layer */
						if (ap->temp1 != poly->layer) continue;

						/* pass it on to the next test */
						stitched = ro_testpoly(ni, pp, ap, poly, oni);
						count += stitched;
						if (stitched != 0) break;
					}
					if (pp->connects[i] != NOARCPROTO) break;
				}
			}
		} else
		{
			/* primitive node: check its layers */
			makerot(ni, trans);

			/* save information about the other node */
			ox = (oni->lowx + oni->highx) / 2;
			oy = (oni->lowy + oni->highy) / 2;

			/* look at all polygons on this nodeinst */
			tot = nodeEpolys(ni);
			for(j=0; j<tot; j++)
			{
				shapeEnodepoly(ni, j, poly);

				/* only want electrically connected polygons */
				if (poly->portproto == NOPORTPROTO) continue;

				/* if the polygon layer is pseudo, substitute real layer */
				ro_substituteforpseudo(ni, poly);

				/* get the correct port connected to this polygon */
				rpp = NOPORTPROTO;

				/* search all ports for the closest connected to this layer */
				bestpp = NOPORTPROTO;
				for(rpp = ni->proto->firstportproto; rpp != NOPORTPROTO; rpp = rpp->nextportproto)
					if (rpp->network == poly->portproto->network)
				{
					/* compute best distance to the other node */
					portposition(ni, rpp, &x, &y);
					dist = abs(x-ox) + abs(y-oy);
					if (bestpp == NOPORTPROTO) bestdist = dist;
					if (dist > bestdist) continue;
					bestpp = rpp;   bestdist = dist;
				}
				if (bestpp == NOPORTPROTO) continue;
				rpp = bestpp;

				/* stop now if already an arc on this port to other node */
				for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
					if (pi->proto->network == rpp->network &&
							(pi->conarcinst->end[0].nodeinst == oni ||
								pi->conarcinst->end[1].nodeinst == oni)) break;
				if (pi != NOPORTARCINST) continue;

				/* transformed the polygon */
				xformpoly(poly, trans);

				/* check all possible arcs */
				for(i=0; rpp->connects[i] != NOARCPROTO; i++)
					if (rpp->connects[i] == ro_preferedarc) break;
				couldbe = rpp->connects[i];
				for(i=0; rpp->connects[i] != NOARCPROTO; i++)
				{
					ap = rpp->connects[i];
					if (couldbe != NOARCPROTO && couldbe != ap) continue;

					/* arc must be in the same technology */
					if (ap->tech != ni->proto->tech) continue;

					/* this polygon must be the smallest arc layer */
					ro_findsmallestlayer(ap);
					if (ap->temp1 != poly->layer) continue;

					/* pass it on to the next test */
					stitched = ro_testpoly(ni, rpp, ap, poly, oni);
					count += stitched;
					if (stitched != 0) break;
				}
				if (rpp->connects[i] != NOARCPROTO) break;
			}
		}
	}
	return(count);
}

/*
 * routine to find exported polygons in node "oni" that abut with the polygon
 * in "poly" on the same layer.  When they do, these should be connected to
 * nodeinst "ni", port "pp" with an arc of type "ap".  Returns the number of
 * connections made (0 if none).
 */
INTBIG ro_testpoly(NODEINST *ni, PORTPROTO *pp, ARCPROTO *ap, POLYGON *poly, NODEINST *oni)
{
	REGISTER NODEINST *rni;
	REGISTER PORTPROTO *rpp, *mpp, *bestpp;
	XARRAY localtran, temp, trans;
	INTBIG x, y, plx, phx, ply, phy;
	static POLYGON *opoly = NOPOLYGON;
	REGISTER INTSML tot, j, stitched, bbp;
	REGISTER NETWORK *net, *onet;
	REGISTER INTBIG dist, ox, oy, bestdist, lx, hx, ly, hy;

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

	/* get network associated with the node/port */
	net = getnetonport(ni, pp);

	/* now look at every layer in this node */
	stitched = 0;
	if (oni->proto->primindex == 0)
	{
		/* complex facet: look at all exported ports */
		if (oni->temp2 == 0 || oni->temp2 == -1) bbp = -1; else
		{
			getbbox(poly, &plx, &phx, &ply, &phy);
			bbp = 0;
		}
		for(mpp = oni->proto->firstportproto; mpp != NOPORTPROTO; mpp = mpp->nextportproto)
		{
			/* first do a bounding box check */
			if (bbp >= 0)
			{
				lx = ((INTBIG *)oni->temp2)[bbp++];
				hx = ((INTBIG *)oni->temp2)[bbp++];
				ly = ((INTBIG *)oni->temp2)[bbp++];
				hy = ((INTBIG *)oni->temp2)[bbp++];
				if (lx > phx || hx < plx || ly > phy || hy < ply) continue;
			}

			/* port must be able to connect to the arc */
			if (ro_canconnect(ap, mpp) == 0) continue;

			/* do not stitch where there is already an electrical connection */
			onet = getnetonport(oni, mpp);
			if (net != NONETWORK && onet == net) continue;

			/* find the primitive node at the bottom of this port */
			makerot(oni, trans);
			rni = oni;   rpp = mpp;
			while (rni->proto->primindex == 0)
			{
				maketrans(rni, localtran);
				transmult(localtran, trans, temp);
				rni = rpp->subnodeinst;
				rpp = rpp->subportproto;
				makerot(rni, localtran);
				transmult(localtran, temp, trans);
			}

			/* look at all polygons on this nodeinst */
			tot = nodeEpolys(rni);
			for(j=0; j<tot; j++)
			{
				shapeEnodepoly(rni, j, opoly);

				/* only want electrically connected polygons */
				if (opoly->portproto == NOPORTPROTO) continue;

				/* only want polygons connected to correct part of nodeinst */
				if (opoly->portproto->network != rpp->network) continue;

				/* if the polygon layer is pseudo, substitute real layer */
				ro_substituteforpseudo(rni, opoly);

				if (ap->temp1 != opoly->layer) continue;

				/* transformed the polygon and pass it on to the next test */
				xformpoly(opoly, trans);
				if (ro_comparepoly(oni, mpp, opoly, ni, pp, poly, ap) != 0)
				{
					stitched++;
					break;
				}
			}
		}
	} else
	{
		/* primitive node: check its layers */
		makerot(oni, trans);

		/* save information about the other node */
		ox = (ni->lowx + ni->highx) / 2;
		oy = (ni->lowy + ni->highy) / 2;

		/* look at all polygons on this nodeinst */
		tot = nodeEpolys(oni);
		for(j=0; j<tot; j++)
		{
			shapeEnodepoly(oni, j, opoly);

			/* only want electrically connected polygons */
			if (opoly->portproto == NOPORTPROTO) continue;

			/* if the polygon layer is pseudo, substitute real layer */
			ro_substituteforpseudo(oni, opoly);

			/* this must be the smallest layer on the arc */
			if (ap->temp1 != opoly->layer) continue;

			/* do not stitch where there is already an electrical connection */
			onet = getnetonport(oni, opoly->portproto);
			if (net != NONETWORK && onet == net) continue;

			/* search all ports for the closest connected to this layer */
			bestpp = NOPORTPROTO;
			for(rpp = oni->proto->firstportproto; rpp != NOPORTPROTO; rpp = rpp->nextportproto)
				if (rpp->network == opoly->portproto->network)
			{
				/* compute best distance to the other node */
				portposition(oni, rpp, &x, &y);
				dist = abs(x-ox) + abs(y-oy);
				if (bestpp == NOPORTPROTO) bestdist = dist;
				if (dist > bestdist) continue;
				bestpp = rpp;   bestdist = dist;
			}
			if (bestpp == NOPORTPROTO) continue;
			rpp = bestpp;

			/* port must be able to connect to the arc */
			if (ro_canconnect(ap, rpp) == 0) continue;

			/* transformed the polygon and pass it on to the next test */
			xformpoly(opoly, trans);
			if (ro_comparepoly(oni, rpp, opoly, ni, pp, poly, ap) != 0)
			{
				stitched++;
				break;
			}
		}
	}
	return(stitched);
}

/*
 * routine to compare polygon "opoly" from nodeinst "oni", port "opp" and
 * polygon "poly" from nodeinst "ni", port "pp".  If these polygons touch
 * or overlap then the two nodes should be connected with an arc of type
 * "ap".  If a connection is made, the routine returns nonzero, otherwise
 * it returns zero.
 */
INTBIG ro_comparepoly(NODEINST *oni, PORTPROTO *opp, POLYGON *opoly, NODEINST *ni,
	PORTPROTO *pp, POLYGON *poly, ARCPROTO *ap)
{
	INTBIG x, y, ox, oy, lx, hx, ly, hy, olx, oly, ohx, ohy, ret;
	ARCINST *alt1, *alt2;
	REGISTER ARCINST *newai;

	/* find the bounding boxes of the polygons */
	getbbox(poly, &lx, &hx, &ly, &hy);
	getbbox(opoly, &olx, &ohx, &oly, &ohy);

	/* quit now if bounding boxes don't intersect */
	if (lx > ohx || olx > hx || ly > ohy || oly > hy) return(0);

	/* make sure there is more than a point of overlap */
	if ((lx >= ohx || hx <= olx) && (ly >= ohy || hy <= oly)) return(0);

	/* find some dummy position to help run the arc */
	portposition(ni, pp, &x, &y);
	portposition(oni, opp, &ox, &oy);
	x = (ox+x) / 2;   y = (oy+y) / 2;

	/* run the wire */
	newai = aconnect(ni->geom, pp, oni->geom, opp, ap, x, y, &alt1, &alt2, 900, 1);
	ret = ro_didaccept(newai, alt1, alt2);
	return(ret);
}

/*
 * routine to examine up to three arcs that were created and see if they
 * are acceptable to the user.  Returns zero if not, nonzero if so.
 */
INTBIG ro_didaccept(ARCINST *ai, ARCINST *alt1, ARCINST *alt2)
{
	char ch;
	REGISTER VARIABLE *var;

	/* if main arc wasn't created, no update */
	if (ai == NOARCINST) return(0);

	/* see if user selection is to be done */
	if ((ro_state&(SELECT|SELSKIP)) == SELECT)
	{
		/* save highlighting on the first selection */
		if ((ro_state&SELDONE) == 0)
		{
			(void)askaid(us_aid, "down-stack");
			(void)setvalkey((INTBIG)ro_aid, VAID, ro_statekey, ro_state | SELDONE,
				VINTEGER|VDONTSAVE);
		}

		/* erase all highlighting */
		(void)askaid(us_aid, "clear");

		/* force the stitch to be drawn */
		endobjectchange((INTBIG)ai, VARCINST);
		if (alt1 != NOARCINST) endobjectchange((INTBIG)alt1, VARCINST);
		if (alt2 != NOARCINST) endobjectchange((INTBIG)alt2, VARCINST);

		/* highlight the stitch */
		(void)askaid(us_aid, "show-object", (INTBIG)ai->geom);
		if (alt1 != NOARCINST) (void)askaid(us_aid, "show-object", (INTBIG)alt1->geom);
		if (alt2 != NOARCINST) (void)askaid(us_aid, "show-object", (INTBIG)alt2->geom);

		/* loop on user response */
		for(;;)
		{
			ttyputmsg("Ok? ");
			ch = (char)ttygetchar();
			(void)askaid(us_aid, "clear");
			if (ch == 's')
			{
				var = getvalkey((INTBIG)ro_aid, VAID, VINTEGER, ro_statekey);
				if (var != NOVARIABLE)
					(void)setvalkey((INTBIG)ro_aid, VAID, ro_statekey,
						var->addr & ~SELSKIP, VINTEGER|VDONTSAVE);
				break;
			}
			if (ch == ' ' || ch == '\r' || ch == 'y') break;
			if (ch == 'n')
			{
				startobjectchange((INTBIG)ai, VARCINST);
				if (killarcinst(ai) != 0) ttyputerr("Problem retracting arc");
				if (alt1 != NOARCINST)
				{
					startobjectchange((INTBIG)alt1, VARCINST);
					if (killarcinst(alt1) != 0)
						ttyputerr("Problem retracting arc");
				}
				if (alt2 != NOARCINST)
				{
					startobjectchange((INTBIG)alt2, VARCINST);
					if (killarcinst(alt2) != 0)
						ttyputerr("Problem retracting arc");
				}
				ttyputmsg("Stitch not made");
				return(0);
			}
			ttyputmsg("Type: y, SPACE, or CARRIAGE-RETURN to accept");
			ttyputmsg("      n to reject");
			ttyputmsg("      s to continue silently");
		}
	}
	return(1);
}

/*
 * routine to determine whether arcproto "ap" can connect to portproto
 * "pp".  Returns nonzero if it can connect.
 */
INTSML ro_canconnect(ARCPROTO *ap, PORTPROTO *pp)
{
	REGISTER INTSML i;

	for(i=0; pp->connects[i] != NOARCPROTO; i++)
		if (pp->connects[i] == ap) return(1);
	return(0);
}

/*
 * routine to find the smallest layer on arc proto "ap" and cache that information
 * in the "temp1" field of the arc proto.
 */
void ro_findsmallestlayer(ARCPROTO *ap)
{
	REGISTER ARCINST *ai;
	REGISTER float area, bestarea;
	REGISTER INTSML i, j, bestfound;
	static POLYGON *poly = NOPOLYGON;

	/* quit if the value has already been computed */
	if (ap->temp1 >= 0) return;

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

	/* get a dummy arc to analyze */
	ai = dummyarc();
	ai->proto = ap;
	ai->width = defaultarcwidth(ap);
	ai->end[0].xpos = -5000;   ai->end[0].ypos = 0;
	ai->end[1].xpos = 5000;    ai->end[1].ypos = 0;
	ai->length = 10000;

	/* find the smallest layer */
	bestfound = 0;
	j = arcpolys(ai);
	for(i=0; i<j; i++)
	{
		shapearcpoly(ai, i, poly);
		area = (float)fabs(areapoly(poly));
		if (bestfound != 0 && area >= bestarea) continue;
		bestarea = area;
		bestfound++;
		ap->temp1 = poly->layer;
	}
}

void ro_substituteforpseudo(NODEINST *ni, POLYGON *poly)
{
	REGISTER INTBIG fun, ofun, mask;
	REGISTER INTSML i;
	REGISTER TECHNOLOGY *tech;

	tech = ni->proto->tech;
	fun = layerfunction(tech, poly->layer);
	if ((fun&LFPSEUDO) == 0) return;

	/* these bits must match between pseudo and nonpseudo */
	mask = LFTYPE | LFPTYPE | LFNTYPE | LFDEPLETION | LFENHANCEMENT |
		LFLIGHT | LFHEAVY | LFNONELEC;

#if 0
	/* if this is a pin, try substituting the bounds of an attached arc */
	nfun = (ni->userbits&NFUNCTION) >> NFUNCTIONSH;
	if (nfun == NPPIN)
	{
		/* get polygon */
		if (apoly == NOPOLYGON) apoly = allocstaticpolygon(4, ro_aid->cluster);
		for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
		{
			ai = pi->conarcinst;
			j = arcpolys(ai);
			for(i=0; i<j; i++)
			{
				shapearcpoly(ai, i, apoly);
				ofun = layerfunction(tech, apoly->layer);
				if ((ofun&LFPSEUDO) != 0) continue;
				if ((fun&mask) == (ofun&mask))
				{
					/* this layer on the arc is the true geometry of the pseudo-layer */
					(void)extendpolygon(poly, apoly->count);
					for(k=0; k<apoly->count; k++)
					{
						poly->xv[k] = apoly->xv[k];
						poly->yv[k] = apoly->yv[k];
					}
					poly->count = apoly->count;
					poly->layer = apoly->layer;
					return;
				}
			}
		}
	}
#endif

	for(i=0; i<tech->layercount; i++)
	{
		ofun = layerfunction(tech, i);
		if ((ofun&LFPSEUDO) != 0) continue;
		if ((fun&mask) != (ofun&mask)) continue;
		poly->layer = i;
		break;
	}
}

#endif  /* ROUTAID - at top */
